Day1语法:printf 与 scanf 使用
1. 输入输出函数
printf 和 scanf 是 C 语言中最常用的格式化输入输出函数,但使用不当可能导致未定义行为或缓冲区溢出。本文梳理了它们的使用细节、常见错误及最佳实践,并补充了 getchar/putchar、fgets/fputs 等辅助函数,帮助你安全地进行输入输出操作。
2. printf 使用细节
2.1 基本语法
函数原型:int printf(const char *format, ...);
作用:格式化输出到标准输出(屏幕)。返回值:成功时返回打印的字符数,失败时返回负值。
2.2 输出缓冲区
printf 输出时会先存入输出缓冲区,遇到以下情况才刷新到屏幕:
- 缓冲区已满
- 遇到换行符
\n - 程序正常结束
- 显式调用
fflush(stdout)
示例:
printf("Hello"); // 存储在缓冲区,不立即显示 printf(" World\n"); // 遇到 \n,缓冲区刷新,显示 "Hello World"2.3 常见格式控制符
| 格式符 | 说明 | 示例 |
|---|---|---|
%d | 十进制整数 | printf("%d", 42); |
%u | 无符号十进制整数 | printf("%u", 100u); |
%f | 浮点数(默认6位小数) | printf("%f", 3.14); |
%.2f | 保留2位小数 | printf("%.2f", 3.14); |
%c | 单个字符 | printf("%c", 'A'); |
%s | 字符串 | printf("%s", "Hi"); |
%x | 十六进制(小写) | printf("%x", 255); |
%X | 十六进制(大写) | printf("%X", 255); |
%p | 指针地址 | printf("%p", &x); |
%% | 打印%本身 | printf("%%"); |
2.4 宽度与精度控制
%5d:至少占5个字符宽度,右对齐。%-5d:左对齐。%.3f:保留3位小数。
printf("%5d", 10); // 输出 " 10" printf("%-5d", 10); // 输出 "10 " printf("%.3f", 3.14159);// 输出 "3.142"2.5 转义字符
\n:换行\t:制表符\\:打印反斜杠\":打印双引号
2.6 类型匹配
格式符必须与变量类型严格匹配,否则引发未定义行为。例如用%d打印float是错误的:
float f = 3.14; printf("%d", f); // 错误!应使用 %f3. scanf 使用细节
3.1 基本语法
函数原型:int scanf(const char *format, ...);
作用:从标准输入读取格式化数据。返回值:成功读取的变量个数,失败或遇到 EOF 返回 -1。
3.2 输入缓冲区
用户输入的数据首先进入输入缓冲区,scanf 从缓冲区读取并解析,未处理的数据会残留。
3.3 常见格式控制符
| 格式符 | 说明 | 示例 |
|---|---|---|
%d | 读取整数 | scanf("%d", &num); |
%f | 读取float | scanf("%f", &fval); |
%lf | 读取double | scanf("%lf", &dval); |
%c | 读取字符(含空格) | scanf("%c", &ch); |
%s | 读取字符串(无空格) | scanf("%s", str); |
%[^\n] | 读取一行(含空格) | scanf("%[^\n]", line); |
3.4 关键细节
3.4.1 必须传递变量地址
除字符串(字符数组)外,其他变量必须加&。
int num; scanf("%d", num); // 错误!应写成 scanf("%d", &num);3.4.2 缓冲区残留问题
scanf 读取后,输入缓冲区可能残留\n,影响后续输入。可通过以下方式清空:
while (getchar() != '\n'); // 清空缓冲区3.4.3 防止 %s 缓冲区溢出
scanf("%s", str)不检查长度,可能导致越界。建议限制读取长度:
char str[100]; scanf("%99s", str); // 最多读取 99 个字符,留 1 位给 '\0'3.4.4 %c 会读取空白字符
如果前面有\n,%c会直接读取它。解决方法是在%c前加空格:
scanf(" %c", &ch); // 跳过空白字符3.4.5 检查返回值
始终检查输入是否成功,避免非法输入导致程序崩溃:
if (scanf("%d", &num) != 1) { printf("输入错误!\n"); while (getchar() != '\n'); // 清空缓冲区 }4. printf 与 scanf 对比
| 特性 | printf | scanf |
|---|---|---|
| 用途 | 输出数据 | 输入数据 |
| 变量传递 | 直接传值 | 必须传地址 |
| 缓冲区 | 通常自动刷新(带 \n) | 可能残留 \n,需清理 |
| 安全性 | 一般安全 | %s 可能溢出,需限制长度 |
| 返回值 | 成功打印的字符数 | 成功读取的变量个数 |
5. 经典错误示例
5.1 scanf 忘记 &
int num; scanf("%d", num); // 错误,导致未定义行为5.2 %d 读取 float
float f; scanf("%d", &f); // 错误,应使用 %f5.3 %s 不限制长度
char name[10]; scanf("%s", name); // 输入超过 9 个字符导致缓冲区溢出正确做法:
scanf("%9s", name);5.4 scanf 读取整数时的残留字符
scanf 根据%d读取整数,遇到非数字字符会停止,并将该字符放回缓冲区,影响后续读取。
scanf("%d%d", &a, &b); scanf("%c", &c); // 若输入字母 a,c 会直接获取该字符6. 最佳实践
6.1 printf
- 使用
\n确保输出刷新。 - 确保格式符与变量类型匹配。
- 合理控制输出宽度和精度。
6.2 scanf
- 始终检查返回值。
- 对
%s使用长度限制。 - 必要时清空输入缓冲区。
7. 其他相关函数
7.1 getchar 与 putchar
getchar()从标准输入读取一个字符,putchar()输出一个字符。
char ch = getchar(); // 等价于 scanf("%c", &ch); putchar(ch); // 等价于 printf("%c", ch);7.2 getc 与 putc
与 getchar/putchar 类似,但允许指定文件指针(如stdin、stdout或自定义文件流)。
ch = getc(stdin); // 从标准输入读取 ch = getc(fp); // 从文件指针 fp 读取7.3 fgets 与 fputs
fgets从指定流读取一行,通过第二个参数限制长度,安全解决溢出问题。
char ch[20]; fgets(ch, 19, stdin); // 最多读取 19 个字符,保留 '\0' printf("%s", ch);fputs将字符串写入指定流,不自动添加换行符。
fputs(buf, fp); // 将 buf 写入 fp 指向的文件8. 总结
掌握 printf 和 scanf 的细节能有效避免未定义行为和缓冲区溢出。关键点:
- printf 注意格式匹配和缓冲区刷新;
- scanf 必须传地址,限制字符串长度,检查返回值,清理残留缓冲区;
- 必要时使用 getchar/putchar、fgets/fputs 等更安全的函数替代。
希望笔记能帮助你巩固 C 语言输入输出相关知识,减少编码中的低级错误。