/* join_chk.c -- joins two strings, check size first */#include <stdio.h>#include <string.h>#define SIZE 30#define BUGSIZE 13char * s_gets(char * st, int n);int main(void){char flower[SIZE];char addon[] = "s smell like old shoes.";char bug[BUGSIZE];int available;puts("What is your favorite flower?");s_gets(flower, SIZE);if ((strlen(addon) + strlen(flower) + 1) <= SIZE)strcat(flower, addon);puts(flower);puts("What is your favorite bug?");s_gets(bug, BUGSIZE);available = BUGSIZE - strlen(bug) - 1;strncat(bug, addon, available);puts(bug);return 0;}char * s_gets(char * st, int n){char * ret_val;int i = 0;ret_val = fgets(st, n, stdin);if (ret_val){while (st[i] != 'n' && st[i] != '0')i++;if (st[i] == 'n')st[i] = '0';else // must have words[i] == '0'while (getchar() != 'n')continue;}return ret_val;}
下面是该程序的运行示例:
What is your favorite flower?RoseRoses smell like old shoes.What is your favorite bug?AphidAphids smell
读者可能已经注意到 , strcat()和gets()类似 , 也会导致缓冲区溢出 。为什么C11标准不废弃strcat() , 只留下strncat()?为何对gets()那么残忍?这也许是因为gets()造成的安全隐患来自于使用该程序的人 , 而strcat()暴露的问题是那些粗心的程序员造成的 。无法控制用户会进行什么操作 , 但是 , 可以控制你的程序做什么 。C语言相信程序员 , 因此程序员有责任确保strcat()的使用安全 。
4 strcmp()函数假设要把用户的响应与已存储的字符串作比较 , 如程序nogo.c所示 。
/* nogo.c -- will this work? */#include <stdio.h>#define ANSWER "Grant"#define SIZE 40char * s_gets(char * st, int n);int main(void){char try[SIZE];puts("Who is buried in Grant's tomb?");s_gets(try, SIZE);while (try != ANSWER){puts("No, that's wrong. Try again.");s_gets(try, SIZE);}puts("That's right!");return 0;}char * s_gets(char * st, int n){char * ret_val;int i = 0;ret_val = fgets(st, n, stdin);if (ret_val){while (st[i] != 'n' && st[i] != '0')i++;if (st[i] == 'n')st[i] = '0';else // must have words[i] == '0'while (getchar() != 'n')continue;}return ret_val;}
这个程序看上去没问题 , 但是运行后却不对劲 。ANSWER和try都是指针 , 所以try !=ANSWER检查的不是两个字符串是否相等 , 而是这两个字符串的地址是否相同 。因为ANSWE和try存储在不同的位置 , 所以这两个地址不可能相同 , 因此 , 无论用户输入什么 , 程序都提示输入不正确 。这真让人沮丧 。
该函数要比较的是字符串的内容 , 不是字符串的地址 。读者可以自己设计一个函数 , 也可以使用C标准库中的strcmp()函数(用于字符串比较) 。该函数通过比较运算符来比较字符串 , 就像比较数字一样 。如果两个字符串参数相同 , 该函数就返回0 , 否则返回非零值 。修改后的版本如程序compare.c 所示 。
/* compare.c -- this will work */#include <stdio.h>#include <string.h>// declares strcmp()#define ANSWER "Grant"#define SIZE 40char * s_gets(char * st, int n);int main(void){char try[SIZE];puts("Who is buried in Grant's tomb?");s_gets(try, SIZE);while (strcmp(try,ANSWER) != 0){puts("No, that's wrong. Try again.");s_gets(try, SIZE);}puts("That's right!");return 0;}char * s_gets(char * st, int n){char * ret_val;int i = 0;ret_val = fgets(st, n, stdin);if (ret_val){while (st[i] != 'n' && st[i] != '0')i++;if (st[i] == 'n')st[i] = '0';else // must have words[i] == '0'while (getchar() != 'n')continue;}return ret_val;}
注意strcmp()函数比较的是字符串 , 不是整个数组 , 这是非常好的功能 。虽然数组try占用了40字节 , 而存储在其中的"Grant"只占用了6字节(还有一个用来放空字符) , strcmp()函数只会比较try中第1个空字符前面的部分 。所以 , 可以用strcmp()比较存储在不同大小数组中的字符串 。
由于非零值都为“真” , 所以许多经验丰富的C程序员会把该例main()中的while循环头写成:while (strcmp(try, ANSWER))
如果用户输入GRANT、grant或Ulysses S. Grant会怎样?程序会告知用户输入错误 。希望程序更友好 , 必须把所有正确答案的可能性包含其中 。这里可以使用一些小技巧 。例如 , 可以使用#define定义类似GRANT这样的答案 , 并编写一个函数把输入的内容都转换成大写 , 就解决了大小写的问题 。但是 , 还要考虑一些其他错误的形式 , 这些留给读者完成 。
推荐阅读
- C语言的自定义输入和输出的三种方法
- 旧硬盘应该如何处理,阿卡西斯的这款雷电3硬盘盒给出了答案
- 编写C程序控制LED
- 还不知道MySQL怎么给字符串加索引?
- C语言的指针与多维数组
- 架构大更新的英特尔Rocket Lake处理器可将频率提升至5.0GHz
- 被蛇咬伤的处理方法 被蛇咬伤应该怎么处理?
- 使用pandas处理时间变量
- C语言的数组的构建与打印
- 驾考5次不过咋处理?