一、先了解几个函数及概念:
1.空字符
ASCII值为0的字符,用’\0’表示,属于不可见字符
2.不可见字符
也叫ASCII控制字符,常见不可见字符有:上面提到的空字符、Tab键的空格,换行符…(更多见百度)
注意!空格键输入的空格ASCII值为32,属于可见字符!
3.空白
空格、制表符和回车统称为空白(white space)
4.strlen函数:
(1)只计算可见字符;
(2)从第一个字符开始计算,到不可见字符为止的字符数(字符数不含不可见字符,因为到不可见字符就停了)
(3)返回字符串的长度
5.sizeof函数:
指出整个数组的长度(包含中间及结尾不可见字符,结尾那个不可见字符指的是系统帮你加的’\0’,你自己就算写了’\0’那也算中间的)。
如果你像char sample[11] = "aabbccdd\0";
这样再sizeof(sample),就等于原来定义的数组长度;
如果你不自己指定数组长度,那就是你字符数组的长度+1(加的这个1是系统帮你加的’\0’)
这里不是很好展开,更多细节及验证可以去这里看一下 字符与字符串中的’\0’, ‘0’, 0;strlen()函数求字符串长度计算方法(sizeof()验证)
二、什么是字符数组,什么是字符串?
(1)这是字符数组,因为没有自己写’\0’
char dog[8] = {'a','b','c'}; // not a string!
(2)这是字符串,因为写了’\0’
char cat[8] = {'f','a','t','\0'} // it a string!
当前,谁想写个长点儿的字符串用上面的方法一个个敲啊?
有个更好的将字符数组初始化为字符串的方法——只需要一个引号括起的字符串即可,这种字符串被称为字符串常量(string constant)或字符串字面值(string literal),如下所示:
char bird[11] = "Mr. Cheeps"; // 中间含空格,是个可见字符。strlen(bird)等于10,最后一个char空间留给'\0'
char fish[] = "Bubbles";
用引号括起来的字符串隐式的包括结尾的空字符,因此不用显示的包括它。
注意!在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内!
另外,char boss[8] = "abc";
的内存空间是这样的:
更多细节及验证还是可以去这里看一下 字符与字符串中的’\0’, ‘0’, 0;strlen()函数求字符串长度计算方法(sizeof()验证)
三、单引号’'和双引号""能不能混用,为什么?
python中我们知道是可以的。但是C\C++中不可以。
例如:
char size = 'S';
这个语句的作用是将83赋值给size(在ASCII系统上,'S’只是83的另一种写法)
但是下面这个例子能有相同的作用么?
char size = "S"; // illegal type mismatch
很遗憾不能,"S"不是字符常量,它表示的是两个字符(字符S和\0)组成的字符串。更糟的是在这个语句中,"S"实际上表示的是字符串所在的内存地址,也就是试图将一个内存地址赋值给char类型的size。
由于地址在C++中是一种独立的类型,因此C++编译器不允许这种不合理的做法!
四、三个C++输入函数cin()、cin.get()、cin.getline()
istream中类(如cin)提供了一些面向行的类成员函数:getline()和get()。
这两个函数都读取一行输入,直到到达换行符。
1.cin()
简单操作大家都会,高级特性还未使用,留个坑后面再填
2.cin.getline();
- 作用:读取整行,通过回车键键入的换行符来确定输入结尾
- 格式:
cin.getline(char* s, streamsize n)
重载形式下: s :用来存储输入行的数组的名称; n :是要读取的字符数 - 细节:
①在读取指定数目的字符或遇到换行符时停止读取。如果参数 n 为20,则函数最多读取19个字符,余下的空间存储自动在结尾处添加的空字符。
②通过回车键键入的换行符来确定输入结尾,读取并丢弃换行符,比如你输入"Jud"后按下了Enter键,Enter键输入的换行符’\n’是不会被保存的,取而代之的是空字符’\0’,最后内存空间也就是"Jud\0"而不是"Jud\n"。丢弃的意思是换行符不仅不保存在内存空间中,也不会留在输入队列中。你下次调用时,看到的第一个字符不再是这个换行符。 - 特性拓展——拼接方式:
cin.getline(name1, size1).getline(name2, size2)
之所以可以这样做,是由于cin.getline(name, size)返回一个cin对象,该对象随后再被用来调用getline()函数。
3.cin.get()
istream类有另一个名为get()的成员函数,该函数有几种变体(就是函数重载的结果)
(1)第一种变体cin.get(char *s, streamsize n)
- 作用:读取整行,通过回车键键入的换行符来确定输入结尾
- 格式:与getline工作方式相似,接受的参数及其参数意义相同
- 细节:
①同上cin.getline()特性①,在读取指定数目的字符或遇到换行符时停止读取。如果参数 n 为20,则函数最多读取19个字符,余下的空间存储自动在结尾处添加的空字符。
②不同于cin.getline()特性②,虽然同样通过回车键键入的换行符来确定输入结尾,但是换行符将留在输入队列中,也就是说第2次调用时看到的第一个字符便是这个换行符。因此get()认为已经到达行尾,而没有发现任何可读取的内容。 - 特性拓展——拼接方式:
cin.get(name1, size1).get(name2, size2)
(2)第二种变体cin.get()
- 作用:读取下一个字符(即使是换行符),是不是有点像C语言中的getchar()?
- 格式:无参数cin.get(),或者一个参数cin.get(ch)
- 细节:
①不管是无参数还是一个参数,你都可以用来吸收一些字符,最常用就是吸收换行符了
②如果是带一个参数的cin.get(ch),你后面也可以合理使用这个字符 - 特性拓展——拼接方式:
cin.get(name1, size1).get()
这种拼接方式对吸收换行符也很有用
4.为什么不完全使用cin.getline()?他看起来比cin.get()要简单啊
那我先问你一个问题,如何确定停止读取的原因,是由于已经读取了整行,而不是由于数组已经填满呢?
cin.get()可以查看下一个字符,如果是换行符,说明已经读取了整行;否则,说明数组填满,该行中还有其他输入。
而cin.getline()会丢弃该换行符,无法判断。
所以稍微总结下,cin.getline()使用起来更简单,但get()使得检查更仔细
五、我看到有getline(cin, str)这个函数,跟cin.getline()有什么区别么?
这里我们看到没有使用句点表示法,说明这个getline()不是istream类的方法(实际上,不是任何类的方法)。
- 作用:读取整行,通过回车键键入的换行符来确定输入结尾,并将该输入存在str字符串对象中
- 格式:
getline(cin, string str)
- 细节:
①将cin作为参数,指出到哪里去查找输入。
②str实际上是个string对象,不需要指出字符串长度,因为string对象将根据字符串的长度自动调整自己的大小
为什么这个getline不是任何类的方法,而cin.getline()中的getline就是个istream类的方法呢?
因为在引入string类之前很久,C++就有istream类,因此istream的设计没有考虑到string类型,所以在istream类中,没有处理string对象的类方法。
你可能又要问了,既然istream类中没有处理string对象的类方法,为什么cin >> str
可行?
这样处理string对象的代码使用string类的友元函数。这里不详细展开,不想深究的朋友们知道可行也就行了。
顺便推荐下我这篇转载的博文,可以了解下什么样的情况下getline会出现问题getline() 函数用法