Fork me on GitHub

字符串2

字符串输入

在程序中输入字符串之前必须先预留存储该字符串的空间,然后用输入函数获取该字符串。

分配空间

计算机不会在读取字符串时自动计算字符串的的长度,除非特意编写一个处理相应任务的函数

 char *name;
 scanf("%s", name);

在读入name时,name可能会擦写掉程序中的数据或代码,从而导致程序异常中止。因为scanf()要把信息拷贝至参数指定的地址上,而此时该参数是个未初始化的指针,name可能会指向任何地方。
要给字符串分配足够的空间最简单的做法是在声明时显式指明数组的大小:

 char name[81]

还有一种方法是使用C库函数来分配内存

gets函数

在读取字符串时,scanf()和转换说明%s只能读取一个单词,对字符串长度十分限制。gets()函数可用于解决此问题,gets()函数读取整行输入,直至遇到换行符,然后丢弃换行符,存储其余字符,并在这些字符的末尾添加一个空字符使其成为一个C字符串,它经常和puts()函数配对使用

 /* getsputs.c  -- 使用 gets() 和 puts() */
 #include <stdio.h>
 #define STLEN 81
 int main(void)
 {
 char words[STLEN]="I want to learn about string theory !";

 puts("Enter a string, please.");
 gets(words);// 典型用法
 printf("Your string twice:\n");
 printf("%s\n", words);
 puts(words);
 puts("Done.");

 return 0;
 }

显然,运行时遇到了问题。因为该函数的不安全行为导致了安全隐患,再过去有人通过系统编程,利用gets()插入和运行一些破坏系统安全的代码。所以C11标准委员会直接废除了gets()函数

该编译器不支持使用gets()函数

image

gets()函数的替代品

1.fgets()函数和fputs()函数
上代码:

 /*  fgets1.c  -- 使用 fgets() 和 fputs() */
 #include <stdio.h>
 #define STLEN 14
 int main(void)
 {
 char words[STLEN];

 puts("Enter a string, please.");
 fgets(words, STLEN, stdin);
 printf("Your string twice (puts(), then fputs()):\n");
 puts(words);
 fputs(words, stdout);
 puts("Enter another string, please.");
 fgets(words, STLEN, stdin);
 printf("Your string twice (puts(), then fputs()):\n");
 puts(words);
 fputs(words, stdout);
 puts("Done.");

 return 0;
 }

显然fgets()函数通过第2个参数限制读入的的字符的最大数量来解决溢出的问题。如果该参数是n,那么fgets()将读到n-1个字符,或者读到第一个换行符为止。
fgets()函数的地3个参数指明要读入的文件。如果读入从键盘输入的数据,则要以stdin(标准输入),作为参数,该标识符定义在stdio.h中

因为fgets()函数把换行符放在字符串的末尾(假设输入行不会溢出),通常要与fputs()函数(和puts()类似)配对使用,除非该函数不在字符串末尾添加换行符。fputs()函数的第二个参数指明它要写入的文件。当然如果要显示在计算机显示器上,应使用stdout(标准输出)作为参数

上代码:

 /*  fgets1.c  -- 使用 fgets() 和 fputs() */
 #include <stdio.h>
 #define STLEN 14
 int main(void)
 {
 char words[STLEN];

 puts("Enter a string, please.");
 fgets(words, STLEN, stdin);
 printf("Your string twice (puts(), then fputs()):\n");
 puts(words);
 fputs(words, stdout);
 puts("Enter another string, please.");
 fgets(words, STLEN, stdin);
 printf("Your string twice (puts(), then fputs()):\n");
 puts(words);
 fputs(words, stdout);
 puts("Done.");

 return 0;
 }

运行结果是:

image

fgets()与fputs()的使用

显然,在“苹果”下面有一行空行,这是因为,输入的东西比fgets()读入的整型输入短,因此“苹果\n\0”被存储在数组中。所以当puts()显示该字符串时又在末尾添加了换行符,因此打印“苹果”时后面跟了空行。而fputs()不会在字符串末尾添加换行符,所以并未打印出空行。

puts()函数会在待输出字符串末尾添加一个换行符而fputs()函数不会

2.get_s()函数
C11新增的gets()函数和fgets()函数类似,用一个参数限制读入的字符数。两者的区别有:

gets_s()只从标准输入中读取数据,所以不需要第三个参数

如果gets_s()读取到换行符,会丢弃它而不是存储它

如果gets_s()读到最大字符数都没有读到换行符,就会执行以下几步。首先把目标数组中首字符设置成空字符。接着,调用依赖实现的“处理函数”,可能会中止或退出程序。

当输入与预期不符时,gets_s完全没有fgets()函数方便、灵活。
3.s_gets()函数

 char * s_gets(char * st, int n)
 {
 char * ret_val;
 int i = 0;

 ret_val = fgets(st, n, stdin);
 if (ret_val)    // 即,ret_val != NULL
 {
      while (st[i] != '\n' && st[i] != '\0')
           i++;
      if (st[i] == '\n')
           st[i] = '\0';
      else<ins>
 </ins>               while (getchar() != '\n')
                 continue;
 }
 return ret_val;
 }

scanf()函数

scanf()函数中指定字段宽度的用法:

image

scanf()函数指定字段宽度

 /* scan_str.c -- 使用 scanf() */
 #include <stdio.h>
 int main(void)
 {
 char name1[11], name2[11];
 int count;

 printf("Please enter 2 names.\n");
 count = scanf("%5s %10s", name1, name2);
 printf("I read the %d names %s and %s.\n", count, name1, name2);

 return 0;
 }

scanf()函数与gets()函数类似,输入行的内容过长会导致数据溢出,但是在%s转换说明中使用字段宽度可以防止数据溢出

posted @   硫酸钠  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示