大家好o(*^▽^*)┛,我是呆博~很开心又和大家见面啦~
最近一直在为自己的浏览量而担忧啦,都快被厂长大人约谈了……我真的有尽力在写稿子哦,所以也请各位老铁,如果觉得我的文章还不错就转发到朋友圈或者微信群之类的,让更多人的和我们一起学C语言。
言归正传,让我们回到今天的内容上来。今天将主要讲解字符类型和字符串类型,这两个类型看起来很简单,但却是经常发生问题的地方,所以一定要仔细阅读哦。
1
字符类型
回顾上篇博文《4. C语言 -- 一个由数据类型和取值范围引发的 BUG》中的 “2.3 基本数据类型的取值范围” 部分,可以知道字符类型也是有取值范围,signed char
的取值范围是 -128 ~ 127;unsigned char
的是 0 ~ 255。
1.1
存放在字符类型中的变量,都可以被解释为 ASCII 字符表中的对应字符。标准 ASCII 字符表使用 7 位二进制数来表示所有的大写和小写字母、数字 0 到 9、标点符号以及在美式英语中使用的特殊控制字符。其中,ASCII 字符表上的数字 0 ~ 31 以及 127(共 33 个)分配给了控制字符,用于控制像打印机等一些外围设备。这些是看不到的。数字 32 ~ 126 分配给了能在键盘上找到的字符,这些是所见即所得的。
所以大家可以思考一下,执行下面的代码会得到什么结果
其中等号左边输出字符 “C
” 是没有问题的,等号右边输出的是字符a
的 ASCII 码,所以将会输出C = 67
。同理可以给字符型的变量赋值一个正整数(ASCII 码),再以字符型的形式输出,如下
输出结果为FishC
1.2
字符类型与普通整数类型还是有不同之处的。C 标准规定普通整数类型默认使用signed
修饰符,但没有规定char
的默认修饰符。因此,字符类型使用signed
或unsigned
修饰符,是由编译系统自行决定。如下面的代码所示,大家可以想一下输出的结果会是什么?
输出结果为:身高是-86厘米!。
这是为什么呢?有没有觉得很迷(๑→ܫ←)
这里的原因就是字符型的signed
或 unsigned
修饰符是由编译系统自行决定,在 Ubuntu16.04 中使用 gcc 进行编译执行的时候默认字符型是有符号的。因为是有符号的字符型,所以170 的二进制表达 10101010 对应一个负数的补码,而printf
输出的该补码所对应的数字。
在《4. C语言 -- 一个由数据类型和取值范围引发的 BUG》的“2.2 符号位”部分我们有讲过如何将原码变为补码。这里我们需要反过来,将补码变为原码,才能获得 printf 的输出值。
具体的,首先保持符号位不变,将0101010
- 1,得0101001
,然后得到的结果按位取反得1010110
,即十进制的86,将符号位不上可知其对应的数字就是 -86。
正确的修改方法是将char height;
改为unsigned char height;
,通过指定是否为有符号数来解决这个问题。
2
字符串类型
字符串实际上是由一些字符组成的,比如说 “Hello World” 是由下面的字符组成的。
因为在读入字符串的过程中,机器需要知道他从哪里读到哪里,这个时候需要'\0'
表示一个字符串的结束。
定义字符串有以下2种方式:
这里的定义利用了一些数组的知识,将在后面介绍。
2.1
下面的程序使用了上面的两种方式分别创建了两个字符串
除此之外还打印输出了字符串中首位和末位的字符,实验结果如下图所示
可以发现通过两种方式创建的字符串是一样的;is”直接与“space”,中间没有任何字符,所以字符串末位的\0
并不会输出什么(当然也不会是空格之类的)。
3
C语言的三种输入函数
下面介绍 C 语言中的三种标准输入函数scanf(),getchar()
和gets()
。
3.1
为了便于理解,首先介绍输入操作的原理,程序的输入都建有一个输入缓冲区。当一次键盘输入结束时会将输入的数据存入输入缓冲区,cin
函数直接从输入缓冲区中取数据,所以当缓冲区中有残留数据时,cin
函数会直接取得这些残留数据而不会请求键盘输入。所以我们在使用不同的输入函数时,要考虑在输入缓冲区是否会造成残留的问题。
3.2
gets()
用于从标准输入流stdin读入一个整行 (以\n
或EOF
) 结束,写入ptr
指向的字符数组,并返回这个指针;出错或遇到文件结束时则返回NULL。行末的 \n 从流中取出,但不写入数组。gets()不检查被写入的数组大小。
getchar()
用于从标准输入流stdin读入一个字符,并返回这个字符。如果读到文件结尾,则返回EOF。注意到EOF不能用char
类型表示,所以getchar()
函数返回的是一个int
型的数。
scanf()
函数返回成功赋值的数据项数,出错时则返回EOF,与上面相同,scanf()
函数返回的也是一个int
型的数。
或许上面看的有点迷,没关系目前只需要记住getchar()
常用于接收字符,而gets()
常用于接受字符串,scanf()
可用于接受字符也可以用于接收字符串,还有上面的黑体部分即可。
3.3
首先要注意不同的函数是否接受空格符、是否舍弃最后的回车符的问题。
读取字符时:
(1)scanf()
以 Enter 结束一次输入,不会舍弃最后的回车符(即回车符会残留在缓冲区中);
(2)getchar()
以 Enter 结束输入,不会舍弃最后的回车符。
读取字符串时:
(1) scanf()
以Space、Enter、Tab结束一次输入,不会舍弃最后的回车符;
(2) gets()
以Enter结束输入(空格不结束),接受空格,会舍弃最后的回车符!
所以读取一个字符串中的每一个字符的时候,往往使用getchar()
而不使用scanf()
,因为一个字符串中是会出现空格的。如果使用scanf()
会造成将一个字符串首先读入第一个空格前的部分,其余部分会残留在输入缓冲区的情况,在下次读取时可能会出现错误。
其次为了避免出现上述问题,必须要在读取输入前,清空缓冲区的残留数据,可以用以下的方法解决:
(1) 使用fflush(stdin)
函数。某些编译器(如VC6)支持用 fflush(stdin)
来清空输入缓冲,但是并非所有编译器都要支持这个功能(如linux 下的gcc),因为标准中根本没有定义 fflush(stdin),所以这种方法的移植性不是很好不建议使用;
(2) 自己取出缓冲区里的残留数据,即使用
while ((c = getchar()) != EOF && c != '\n');
不停地使用getchar()
获取缓冲中字符,直到获取的c是“\n”
或文件结尾符EOF
为止。
3.4
使用 scanf()
和getchar()
输入读入字符的示例如下所示
执行上面的代码可以获得如下的结果
这里可以看到,对于输入 “a b”, scanf()
将空格与回车符均作为输入,并且打印输出他们的 ascii 码;之后的 scanf()
没有打印输出空格的 ascii 码,是因为定义的输入是 int 型,所以没有将空格这个字符读入;getchar()
获得的结果与 scanf()
是一样的。
其次在程序中我们清除了输入缓冲区中的残留,否则 getchar()
会先读取缓冲区残留的回车,然后在读入键盘输入的部分;fflush(stdin)
在 ubuntu 下不可用,所以注释掉了。
使用 scanf()
和fgets()
输入读入字符的示例如下所示
这里有两点需要注意,首先 gets()
在 ubuntu 下事会报错的,所以在这里使用 fgets()
替代,其次由于 scanf
不会清除最后的回车符号,所以这里我们还是手动清除缓冲区残留,执行后的结果如下所示
所以建议使用fgets()
读入字符串。
好啦,今天的内容就到这里了。有什么宝贵意见都可以提出来的~喜欢的话可以转发到朋友圈和微信群哦~