11.2字符串输入
如果想把一个字符串读入程序,首先必须预留存储该字符串的空间,然后用输入函数获取该字符串。(字符串输入的基本准则)
11.2.1分配空间
下面代码给出错误示范:
// c primer plus 282page test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<stdio.h>
int main()
{
char* name;
scanf_s("%s",name,sizeof(name));
printf("%s\n",name);
std::cout << "Hello World!\n";
}
指针必须初始化!!!!
上图输入hello,仍然打印不出 ,所以char* name=NULL;不可以
这样写仍然不行!!!!
原理:c primer plus 278页:指针形式使得编译器为字符串在静态存储区预留空间。另外,一旦开始执行程序,它会为指针变量name留出一个存储位置,并把字符串的地址存储在指针变量中。该变量最初指向字符串的首字符,但是它的值可以改变,因此可用递增运算符。
最简单解决 方法:
在声明时显示指明数组大小:char name[81];//name是一个已分配块(81字节)的地址。
为字符串分配内存后,便可读入字符串。
总结:如果想把一个字符串读入程序,首先必须预留存储该字符串的空间,下面是定义字符串的3种形式:
char str1[50]="hello hello my mom";
char str2[ ]="hello hello my mom";省略数组初始化声明的大小,编译器自动计算数组大小
char *str="hello hello my mom";
重点和细节:让编译器计算数组的大小只能用在初始化数组时。如果创建一个稍后再填充的数组,必须在声明时指定大小!!!!!
11.2.2不幸的gets()函数
gets()函数简单易用,读取整行输入,直至遇到换行符,然后丢弃换行符,存储其余字符,并在这些字符的末尾添加一个空字符使其成为一个c字符串。它和puts()函数配对使用,该函数用于显示字符串,并在末尾添加换行符。
观察下图:
我输入超过words的长度,却依然运行?
gets()函数无法检查数组是否装的下输入行;gets()函数只知道数组开始处,并不知道数组中有多少个元素。。。如果输入的字符过长,会导致缓冲区溢出,即多余的字符超出了指定的目标空间。
如果这些多余的字符只是占用尚未使用的内存,就不会出现问题;如果它们擦写掉程序中的其他数据,会导致程序中止;或还有其他情况。所以有安全隐患!!!!
// c primer plus 318页编程练习1和2.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<stdio.h>
#define size 10
char* ti02(char a[],int n)
{
int i=0;
do{
a[i]=getchar();
if(a[i]==' '||a[i]=='\t'||a[i]=='\n')break;
//在遇到第一个空白时退出循环;
//该空白还存在数组中未处理输入缓冲区的其他数据,尽在a数组中保存第一个空白之前的字符
i++;
}while(i<n);
a[i]='\0';//不要忘添加结束标志
return a;
}
char* ti01(char a[],int n)
{
int i=0;
do{
a[i]=getchar();i++;
}while(i<n);
a[n]='\0';//不要忘添加结束标志
return a;
}
int main(int argc, char* argv[])
{
char a[size];
int n=6;
ti02(a,n);
puts(a);
printf("Hello World!\n");
return 0;
}
//读取系统输入的n个包括空字符在内的字符,scanf只能读取一个单词不能读取空白字符,gets函数只能用于整行输入
//为解决空白字符的读入和存储,使用字符处理函数getchar自定义一个读取数据并保存在数组中
gets(),scanf(),getchar()注意细节!
11.2.3gets()的替代品
输入小于14,大于14呢?下面:
1.fgets()和fputs()
区别:
a.fgets函数的第2个参数指明了读入字符的最大数量。如果该参数值是n,那么fget将读入n-1个字符,或读到遇到的第一个换行符为止
b.若fgets读到一个换行符,会把它存储在字符串中。这点与gets不同(gets会丢弃换行符);
c.fputs不在字符串末尾添加换行符,puts显示该字符串时会在末尾添加换行符
d.fgets函数的第3个参数指明要读入的文件。如果读入从键盘输入数据则以stdin标准输入作为参数;如果显示在计算器上,使用stdout标准输出作为该参数.
fgets()函数返回指向char的指针。如果一切顺利,该函数的返回地址与传入的第1个参数相同。但是,如果函数读到文件结尾,它将返回一个特殊指针:NULL空指针(该指针不会指向有效的数据)。下面为程序11.8 fgets2.cpp
比较不注释printf("*****\n");
为什么会这样?
虽然LEN 被设置为10,但是该程序似乎在处理过长的输入时完全没问题。程序中的fgets()-次读入LEN-1个字符(该例中为9个字符)。所以,一开始它只读入了"by the wa",并存储为by the wa\0;接着fputs()打印该字符串,而且并未换行。然后while循环进入下一轮迭代,fgets()维续从剩余的输入中读入数据,即读入"y,the ge"并存储为y,the ge\0:接着fputs()在刚才打印字符串的这一行接着打印第2次读入的字符串。然后while进入下一轮迭代,fgets()继续读取输入、fputs()打印字符串,这一过程循环进行,直到读入最后的"tion\n"。fgets()将其存储为tion\n\0,fputs()打印该字符串,由于字符串中的\n,光标被移至下一行开始处。
系统使用缓冲的I/O,用户在按下return键(回车/换行键)之前,输入都被存储在临时存储区(即缓冲区)中。按下return键就在输入中增加一个换行符,并把整行输入发送给fgets()。对于输出,fputs()把字符发送给另一个缓冲区,当发送换行符,缓冲区中的内容被发送至屏幕上!!!!、
fgets的好处和坏处?fgets会把换行存储在字符串里
坏处:可能你不想把换行存储在字符串中
好处:对于存储字符串来说,检查末尾 是否有换行符可以判读是否读取一整行,如果不是一整行要妥善处理一行中剩下的字符。
// c primer plus fgets3 286page.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<stdio.h>
#define LEN 10
int main(int argc, char* argv[])
{
char words[LEN];
int i=0;
puts("enter a string(empty line to quit):");
while(fgets(words,LEN,stdin)!=NULL&&words[0]!='\0')//空行即首字符是换行符
{
i=0;
while(words[i]!='\n'&&words[i]!='\0')
i++;//遍历字符串直到遇到换行或空字符
if(words[i]=='\n')
words[i]='\0';//换行符替换成空字符
else
{//如果words[i]=='\0'则执行这部分代码
while(getchar()!='\n')
continue;//读取但不存入输入,包括\n
}
puts(words);
}
printf("done!\n");
return 0;
}
tips:空字符\0和空指针NULL
从概念上,两者完全不同。空字符'\0'用于标记c字符串末尾的字符,对应字符编码是0.由于其他字符的编码不可能是0,所以不可能是字符串的一部分。
空指针NULL有一个值,该值不会与任何数据的有效地址对应。通常,函数使用它返回一个有效地址表示某些特殊情况发生,例如文件结尾或未能按预期执行。
空字符是整数类型(空字符是一个字符,占1个字节),空指针(它是一个地址,通常占4个字节)是指针类型。
两者混淆原因:因为都可以用0表示
2gets_s()函数
gets_s()和fgets()的区别:
gets_s()只从标准输入读取数据,不需要第3个参数(gets_s(words,LEN))
gets_s()读到换行符,会丢弃不会存储
gets_s()读到最大字符数没有读到换行符,会执行以下几步:首先把目标数组中的首字符设为空字符,读取并丢弃随后的输入直至读到换行符或文件结尾,然后返回空指针;接着调用依赖实现的处理函数实现的"处理函数",可能会中止或退出程序。
缓冲区太小:LEN 10
于是,如果输入太长怎么办?
gets()不安全,会擦写现有数据,存在安全隐患;
gets_s()函数很安全但要编写特殊的特殊的处理函数;
当输入太长超过数组最大可容纳的字符数时,fgets函数最容易使用。
如果目标存储区,就是数组足够大,3个函数均可使用。
3s_gets()
int main()//去掉空白字符
{
char words[LEN]="hello world,input string!";
int length=strlen(words);
for(int i=0;i<length;i++)
{
if(words[i]==' ')
continue;
else
printf("%c",words[i]);
}
return 0;
}//打印结果:helloworld,inputstring!
总结:fgets()函数通常是最佳选择;scanf()典型用法是读取并转换混合数据类型为某种标准形式。例如:如果输入行包含一种工具名,库存量,单价等,既可以使用scanf()
注意为什么错误?
debug白框讲的很清楚!!