# 目录 - [目录](#目录) - [第三章 输入输出专题](#第三章-输入输出专题) - [内容](#内容) - [格式化输入输出函数](#格式化输入输出函数) - [`printf`](#printf) - [`scanf`](#scanf) - [字符输入输出函数](#字符输入输出函数) # 第三章 输入输出专题 input & output -> I/O(标准IO,文件IO) ## 内容 1. 格式化输入输出函数:`scanf`, `printf` 2. 字符输入输出函数:`getchar`, `putchar` 3. 字符串输入输出函数:`gets(!)`,`puts` ## 格式化输入输出函数 ![标准输出格式字符](https://s2.loli.net/2024/03/18/OMiu6CUDZy5RH3P.png) ![标准输出修饰符](https://s2.loli.net/2024/03/18/Z6gVaw1AOJ87Uil.png) ### `printf` ```c int printf(const char *format, ...); format: "%【修饰符】格式字符",参照图片标准输出修饰符和格式字符 ``` - 有关`l`修饰符的提醒 ```C int main() { FILE *fp; long a = 11; fp = fopen(); // 可行,a定义了long型,与函数要求一致 func(fp, a); // 早期的编译器不认可,这个12没有单位,不同编译器默认类型不同 // 需要加上L修饰符 func(fp, 12); func(fp, 12L); // 对于func(FILE *fp, long long i) func(fp, 12LL); } ``` 传输一个5G的文件 ```c 5LL*1204LL*1204LL*1204LL ``` 定义一个宏,表示一年有多少秒 ```c // 定义一年有多少秒 #define SEC_YEAR (60LL * 60LL * 24LL * 365LL) ``` - 函数 变参与定参重载的区别 - 变参:编译器只报警告,函数自己都不知道自己有多少个参数 - 定参重载:编译报错 - `\n`的作用 强制刷新缓冲区,以死循环调试举例。 ```c // 不加\n,before while()存入了缓冲区,故不输出 // printf("[%s:%d]before while().", __FUNCTION__, __LINE__); printf("[%s:%d]before while().\n", __FUNCTION__, __LINE__); while (1) { } // printf("[%s:%d]after while().", __FUNCTION__, __LINE__); printf("[%s:%d]after while().\n", __FUNCTION__, __LINE__); ``` - 完整代码 ```c #include #include /*** * printf("%[修饰符]格式字符", 输出表项); * */ #define STRSIZE 10 int main() { // printf("hello world\n"); // char ch = 65; // printf("%c\n", ch); // out: A int i = 255; float f = 123.45678; char str[STRSIZE] = "helloworld"; double dou = 123.456; long long l = 123456; // 不加\n,before while()存入了缓冲区,故不输出 // printf("[%s:%d]before while().", __FUNCTION__, __LINE__); printf("[%s:%d]before while().\n", __FUNCTION__, __LINE__); while (1) { } // printf("[%s:%d]after while().", __FUNCTION__, __LINE__); printf("[%s:%d]after while().\n", __FUNCTION__, __LINE__); // printf("f = %8.1f\n", f); // out:f = 123.5 // printf("i = %2d\n", i); // out: i = 123 // printf("%-10.5s[over]\n", str); // printf("%+10.5s[over]\n", str); // out: // hello [over] // hello[over] // 同样对于i进行八进制和对十六进制的输出 // printf("i = %#o\n", i); // out: i = 0377 // printf("i = %#x\n", i); // out: i = 0xff // printf("dou = %lf\n", dou); // printf("l = %ld\n", l); // out: // dou = 123.456000 // l = 123456 // printf("hello world!\n"); // 实参过多或者过少都会警告 // printf("#d #e\n", i); // printf("#d #e\n", i, f, l); exit(0); } #if 0 func(FILE *fp, long i) { } int main() { FILE *fp; long a = 11; fp = fopen(); // 可行,a定义了long型,与函数要求一致 func(fp, a); // 早期的编译器不认可,这个12没有单位,不同编译器默认类型不同 // 需要加上L修饰符 func(fp, 12); func(fp, 12L); // 对于func(FILE *fp, long long i) func(fp, 12LL); } #endif // 定义一年有多少秒 #define SEC_YEAR (60LL * 60LL * 24LL * 365LL) ``` ### `scanf` ```c int scanf(const char *format, 地址表); format:抑制符* %s非常危险,因为不知道储存空间的大小 放在循环结构中时要注意能否接收到正常内容 ``` - 完整代码 ```c #include #include /** * int scanf(const char *format, 地址表); */ // #define STRSIZE 32 #define STRSIZE 3 // 除掉尾0占一个 // 相当于只能两个字符 // 但是这时候运行输入hello,不会报错 // 实际上是越界了 int main() { int i; float f; char str[STRSIZE]; int ret; char ch; printf("Please enter:\n"); // scanf("%d", &i); // ch = getchar(); // printf("i = %d, ch = %c\n", i, ch); /* out: Please enter: 4 h i = 4, ch = ch被吞掉了 */ // scanf("%d", &i); // scanf("%*c%c", &ch); // 用 '%*c' 吃掉回车 /* out: Please enter: 5 h i = 5, ch = h */ printf("i = %d, ch = %c\n", i, ch); #if 0 // 输入a直接卡死,scanf直接放在循环中很危险 // 需要加入校验,用ret接收scanf返回值 while (1) { // scanf("%d", &i); ret = scanf("%d", &i); if (ret != 1) { printf("Enter Error!\n"); break; } printf("i = %d\n", i); } #endif // printf("Please enter fot str:\n"); // 这种情况下,不能有任何间隔符 // 输入hello world,只会得到hello // 对于scanf,不建议'%s' // scanf("%s", str); // printf("%s\n", str); // printf("Please enter for i[int]:\n"); // 这里两个数字之间有',',输入就一定要有',' // 最好不加,空格符可以是空格 回车 tab // scanf("%d,%f", &i, &f); // printf("i = %d\n", i); // printf("f = %f\n", f); exit(0); } ``` ## 字符输入输出函数 ```c int getchar(void);// 返回int型是考虑出错的情况 int putchar(int c); #include #include int main() { int ch; ch = getchar(); putchar(ch); exit(0); } ``` ## 字符串输入输出函数 ```c char *gets(char *s); // 从标准输入读到内容放到s指针指向的缓冲区 // 停止条件:换行或者EOF // 尾部会追加'\0' int puts(const char *s); // 将s指针缓冲区内容往外写 // 以换行符中止 #include #include #define STRSIZE 32 j int main() { char str[STRSIZE]; gets(str); puts(str); exit(0); } ``` `gets`非常危险,不会检查是否越界。使用的话用`fgets`来代替。 ```c char *fgets(char *s, int size, FILE *stream);// 最多读到'size-1'个 ``` `getline`是`GNU libc`的方言,可以动态内存保证读取一整行。 ## 练习专题 ### 夸脱水 > 一个水分子3.0e-23g,一夸脱水约950g。 > > 变成,输入水的夸脱数,输出其包含的水分子质量。 ```c static void water(void) { float num; float sum; printf("Please input for num:"); scanf("%f", &num); sum = num * KQ / WEIGHT; printf("Total = %e\n", sum); return; } ``` ### 三角形面积 > 输入三角形的三边长,求三角形面积。 ```c static void area(void) { float a, b, c; float s, area; printf("Please input:"); scanf("%f%f%f", &a, &b, &c); // 对scanf做好校验是好习惯 if (a + b <= c || b + c <= a || a + c <= b) { fprintf(stderr, "EINVAL!\n"); exit(0); } printf("a = %f\n", a); printf("b = %f\n", b); printf("c = %f\n", c); // s = 1 / 2 * (a + b + c); s = 1.0 / 2 * (a + b + c); printf("s = %f\n", s); // s=0.000000 // 1 / 2是int型计算,得到的是商0余1 area = sqrt(s * (s - a) * (s - b) * (s - c)); printf("area = %f\n", area); } ``` 编译需要`-lm`。`makefile`如下,千万要注意`makefile`要使用**制表符**缩进,而非空格。 ```makefile CC=cc CFLAGS= LDFLAGS=-lm TARGET=test SRC=test.c OBJ=$(SRC:.c=.o) $(TARGET): $(OBJ) $(CC) -o $@ $^ $(LDFLAGS) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f $(TARGET) $(OBJ) ``` ### 求根公式 > ax^2^ + bx + c = 0,假设b^2^ - 4ac >0 > > x~1~ = (-b + sqrt( b*b - 4ac )) / 2a > > x~2~ = (-b - sqrt( b*b - 4ac )) / 2a ```C void root(void) { float a, b, c; float disc; float p, q; float x1, x2; printf("Please input :\n"); scanf("%f%f%f", &a, &b, &c); if (b * b - 4 * a * c < 0) { fprintf(stderr, "EINVAL\n"); exit(1); } disc = b * b - 4 * a * c; p = -b / 2 * a; q = sqrt(disc) / 2 * a; x1 = p + q; x2 = p - q; printf("x1 = %f\n", x1); printf("x2 = %f\n", x2); } ```