14.1 确保保存输入值的变量足够大
考虑数组和数据类型大小能够保存输入值
14.2 转换说明符和参数个数应保持一致
C语言的哲学是,为最大限度发挥程序员能动性,向其提供最大限度的自由。C语言相信程序员的能力,此处所说的“能力”同样包含发现并解决可能出现的问题的能力。换言之,C语言认为发现并解决可能出现的问题是程序员的责任。
我们可以从printf()函数中找到体现这一观点的示例。程序员需要保证printf()函数的转换说明符的个数和输出参数列表保持一致,C语言并不会检查二者是否一致。
个数不一致的情况又可以分为两种。一是输出参数的个数多于转换字符。
printf ("输出%d和%d", num1, num2, num3, num4);
此时编译器选择忽略num3和num4,这不会引起任何问题。当然,没有引起问题并不意味着应该编写这种形式的代码。
第二种情况是,转换说明符多余输出参数的个数。这种情况下,容易出现问题。因此,使用printf()函数是,要始终保持转换说明符与输出参数的个数保持一致。这一点同样适用于scanf()函数。
14.3 使用fgets()和sscanf()函数而非scanf()函数
C语言中,最常用的函数应该就是printf()函数和scanf()函数,但scanf()函数存在一个问题,它对文件末尾出现的EOF的处理并不稳定。出现这种情况的原因是,scanf()函数读入字符串的过程城中,中间遇到空白字符或特殊字符后,会停止读入。因此,最好在一开始就按照能够处理空白和特殊字符的方式进行编码,即应该编写能够读取整行输入值的程序。
Windows环境下,EOF对应的键盘输入值为 ^Z(Ctrl+Z),UNIX中对应的是 ^D(Ctrl+D)
要实现这种功能,应该依次使用fgets()函数和sscanf()函数。fgets()函数可以包含特殊字符和空白字符在内的整行内容,并保存到标准输入设备的缓存中的函数,而这些内容又可以被sscanf()函数读取并转存入变量。
#include <stdio.h>
char console_line[80]; // 控制台界面中一行字符串能达到的最大长度
int number;
int main(void) {
printf("请输入数值:");
fgets(console_line, sizeof(console_line), stdin);
sscanf(console_line, "%d", &number);
// …略…
printf("%d的2倍是%d。\n", number, number * 2);
return 0;
}
14.4 使用fflush()函数清空标准输入/输出设备缓冲
出现运行时错误时,程序无法正常终止。运行时错误通常出现在栈溢出、数据一处、除以0、引用错误地址等情况中,而问题在于,出现这种运行时错误后,程序是在没有清空标准输入/输出设备的缓冲的状态下终止的。如下示例所示:
#include <stdio.h>
int main(void) {
int num1 = 1;
int num2 = 0;
printf("运行时错误出现前\n");
num1 = num1 / num2;
printf("运行时错误出现后\n");
return 0;
}
编译执行该程序时,界面上不会出现任何内容。为什么会这样呢?执行除以0操作并触发运行时错误前,明明试图输出“运行时错误出现前”这句信息。根据printf()函数的原理,该语句并不立刻输出到界面,而是将其输出到缓冲。随着程序的正常运行,缓冲保存的语句再输出到界面。但该程序将缓冲保存的语句输出到界面前,就因出现运行时错误而提前终止,这条信息也就因此被遗留在缓冲内。
为了防止出现这种问题,我们需要使用fflush()函数。该函数可以将缓冲内容强制输出到输入/输出设备。因此,执行以下程序可以在界面看到输出信息。
#include <stdio.h>
int main(void) {
int num1 = 1;
int num2 = 0;
printf("运行时错误出现前\n");
fflush(stdout); // 将上一条语句从缓冲强制传送到输出设备
// …略…
num1 = num1 / num2;
printf("运行时错误出现后\n");
return 0;
}
使用fflush()函数可能影响程序运行速度,但它同时可以在出现运行时错误时完全清空缓冲,保证下一个程序的稳定性。
网友评论