在C语言编程中,输入输出(I/O)函数是与用户交互的基础工具。作为系统级语言,C没有内置的I/O功能,而是通过标准库<stdio.h>提供了一系列函数。这些函数可以分为两大类:面向字符的I/O和格式化I/O。
字符I/O函数如putchar()和getchar()处理单个字符,效率高但功能简单;格式化I/O函数如printf()和scanf()则能处理复杂的数据格式转换。理解这些函数的底层机制对编写健壮的程序至关重要。例如,当使用getchar()时,实际上是在与输入缓冲区交互,而不是直接读取键盘输入。
提示:所有标准I/O函数都使用缓冲机制,这意味着你的输入并不会立即被程序处理,而是先存储在内存缓冲区中。这是许多初学者困惑的根源。
putchar()是最基础的输出函数,其原型为:
c复制int putchar(int char);
虽然参数和返回值都是int类型,但实际处理的是字符。这种设计源于历史原因——早期C语言没有明确的char类型,且需要EOF(-1)作为错误标志。典型用法包括:
c复制// 输出ASCII字符
putchar(65); // 输出'A'
putchar('A'); // 同上
// 表达式输出
char c = 'a';
putchar(c + 1); // 输出'b'
注意:putchar()返回的是写入的字符,而非打印的个数。连续调用时可以利用返回值:
c复制putchar(putchar('A')); // 先输出A,再用A的ASCII码65作为参数输出
getchar()的原型同样简单:
c复制int getchar(void);
但使用时有许多注意事项:
常见错误示例:
c复制char ch = getchar(); // 错误!无法检测EOF
// 正确做法
int ch;
while ((ch = getchar()) != EOF) {
putchar(ch);
}
实用技巧:清空输入缓冲区的标准方法:
c复制void clear_input_buffer() {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
printf()是可变参数函数,其原型为:
c复制int printf(const char *format, ...);
格式字符串包含两种元素:
底层工作原理:
| 说明符 | 类型 | 示例 | 输出示例 |
|---|---|---|---|
| %d | int | printf("%d",255) | 255 |
| %04d | 前导零填充 | printf("%04d",12) | 0012 |
| %x/%X | 十六进制 | printf("%X",255) | FF |
| %#x | 带前缀十六进制 | printf("%#x",255) | 0xff |
c复制double pi = 3.1415926535;
printf("%.2f", pi); // 3.14
printf("%10.4f", pi); // " 3.1416"
printf("%e", pi); // 3.141593e+00
注意:%f默认保留6位小数,不是四舍五入而是截断
c复制int width = 8, precision = 3;
printf("%*.*f", width, precision, 3.14159); // " 3.142"
c复制printf("%1$d %1$d %2$d", 10, 20); // "10 10 20"
c复制#include <locale.h>
setlocale(LC_NUMERIC, "de_DE");
printf("%'d", 1000000); // 输出"1.000.000"(德语格式)
scanf()原型:
c复制int scanf(const char *format, ...);
关键特性:
典型场景:
c复制int age;
char name[20];
scanf("%d", &age); // 输入42\n
scanf("%s", name); // 直接读取到换行符
解决方案:
c复制while (getchar() != '\n');
c复制scanf("%d\n", &age); // \n会消耗所有空白字符
c复制char buffer[10];
scanf("%9s", buffer); // 最多读取9个字符
c复制if (scanf("%d", &num) != 1) {
// 处理输入错误
}
c复制fgets(buffer, sizeof(buffer), stdin);
sscanf(buffer, "%d", &num);
c复制char digits[20];
scanf("%[0-9]", digits); // 只读取数字
c复制scanf("%[^\n]", str); // 读取整行(包括空格)
c复制int y,m,d;
scanf("%d/%d/%d", &y, &m, &d); // 匹配日期格式2023/05/15
c复制void display_menu() {
printf("\n=== 主菜单 ===\n");
printf("1. 新建项目\n");
printf("2. 打开项目\n");
printf("3. 退出\n");
printf("请选择(1-3): ");
}
int get_choice() {
int choice;
while (1) {
if (scanf("%d", &choice) == 1 && choice >=1 && choice <=3) {
clear_input_buffer();
return choice;
}
clear_input_buffer();
printf("无效输入,请重新选择: ");
}
}
在需要处理大量数据时,I/O函数的选择显著影响性能:
| 操作 | 执行时间(100万次) |
|---|---|
| putchar() | 0.12s |
| printf("%c") | 0.45s |
| printf("%s") | 0.38s |
| puts() | 0.20s |
结论:批量输出时,考虑使用puts()或fwrite()代替多次调用putchar()
解决方案:
c复制#ifdef _WIN32
#define NEWLINE "\r\n"
#else
#define NEWLINE "\n"
#endif
printf("Line1" NEWLINE "Line2");
c复制setlocale(LC_ALL, "en_US.UTF-8");
wprintf(L"中文测试"); // 宽字符输出
c复制if (scanf("%d", &num) != 1) {
// 错误处理
}
c复制char name[20];
scanf("%19s", name); // 预留空间给终止符
c复制unsigned int val;
if (scanf("%u", &val) == 1 && val <= UINT_MAX) {
// 安全使用val
}
c复制double d;
scanf("%f", &d); // 错误!应用%lf
c复制int num;
scanf("%d", num); // 错误!缺少&
c复制printf("Enter name: ");
fgets(name, sizeof(name), stdin); // 可能读取到前次输入的换行符
c复制void hexdump(const void *data, size_t size) {
const unsigned char *p = data;
while (size--) {
printf("%02x ", *p++);
}
printf("\n");
}
c复制#define DEBUG_PRINT(fmt, ...) \
printf("[%s:%d] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
c复制freopen("debug.log", "a", stdout);
printf("调试信息"); // 将输出到文件