c primer plus第11.2字符串输入

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白框讲的很清楚!!

相关推荐
陌小呆^O^6 分钟前
Cmakelist.txt之win-c-udp-client
c语言·开发语言·udp
qystca3 小时前
洛谷 B3637 最长上升子序列 C语言 记忆化搜索->‘正序‘dp
c语言·开发语言·算法
网易独家音乐人Mike Zhou8 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
搬砖的小码农_Sky11 小时前
C语言:数组
c语言·数据结构
ahadee15 小时前
蓝桥杯每日真题 - 第19天
c语言·vscode·算法·蓝桥杯
Theliars15 小时前
C语言之字符串
c语言·开发语言
Reese_Cool15 小时前
【数据结构与算法】排序
java·c语言·开发语言·数据结构·c++·算法·排序算法
搬砖的小码农_Sky16 小时前
C语言:结构体
c语言·数据结构
平头哥在等你18 小时前
求一个3*3矩阵对角线元素之和
c语言·算法·矩阵
尹蓝锐18 小时前
C语言-11-18笔记
c语言