专栏首页轮子工厂5. 很“迷”的字符与字符串

5. 很“迷”的字符与字符串

大家好o(*^▽^*)┛,我是呆博~很开心又和大家见面啦~

最近一直在为自己的浏览量而担忧啦,都快被厂长大人约谈了……我真的有尽力在写稿子哦,所以也请各位老铁,如果觉得我的文章还不错就转发到朋友圈或者微信群之类的,让更多人的和我们一起学C语言。

言归正传,让我们回到今天的内容上来。今天将主要讲解字符类型字符串类型,这两个类型看起来很简单,但却是经常发生问题的地方,所以一定要仔细阅读哦。

1

字符类型

回顾上篇博文《4. C语言 -- 一个由数据类型和取值范围引发的 BUG》中的 “2.3 基本数据类型的取值范围” 部分,可以知道字符类型也是有取值范围signed char 的取值范围是 -128 ~ 127;unsigned char的是 0 ~ 255。

1.1

ASCII 码

存放在字符类型中的变量,都可以被解释为 ASCII 字符表中的对应字符。标准 ASCII 字符表使用 7 位二进制数来表示所有的大写和小写字母、数字 0 到 9、标点符号以及在美式英语中使用的特殊控制字符。其中,ASCII 字符表上的数字 0 ~ 31 以及 127(共 33 个)分配给了控制字符,用于控制像打印机等一些外围设备。这些是看不到的。数字 32 ~ 126 分配给了能在键盘上找到的字符,这些是所见即所得的。

所以大家可以思考一下,执行下面的代码会得到什么结果

其中等号左边输出字符 “C” 是没有问题的,等号右边输出的是字符a的 ASCII 码,所以将会输出C = 67。同理可以给字符型的变量赋值一个正整数(ASCII 码),再以字符型的形式输出,如下

输出结果为FishC

1.2

字符型的符号位

字符类型与普通整数类型还是有不同之处的。C 标准规定普通整数类型默认使用signed修饰符,但没有规定char的默认修饰符。因此,字符类型使用signedunsigned修饰符,是由编译系统自行决定。如下面的代码所示,大家可以想一下输出的结果会是什么?

输出结果为:身高是-86厘米!。

这是为什么呢?有没有觉得很迷(๑→ܫ←)

这里的原因就是字符型的signedunsigned 修饰符是由编译系统自行决定,在 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读入一个整行 (以\nEOF) 结束,写入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()读入字符串

好啦,今天的内容就到这里了。有什么宝贵意见都可以提出来的~喜欢的话可以转发到朋友圈和微信群哦~

本文分享自微信公众号 - 轮子工厂(Programmer-ing),作者:轮子工厂CFO

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 我敢打赌,照这种方式去找实习,成功率会提高80%

    伴随这今年哈尔滨的初雪,秋招基本已经结束了。看着身边的同学都是好几个offer在手,而你却一个都没有,咋整?

    谭庆波
  • 高晓松清华谈5G,这些行业将要被颠覆

    前段时间高晓松在清华做了一场“没有诗和远方”的演讲,主题主要是讲5G和区块链。其中大部分观点我是认同的,我将部分演讲内容稍微总结了一下,分享给大家!

    谭庆波
  • 5个相见恨晚的Linux命令

    作为一个开发人员,经常要用到终端命令,最让人头疼的是记不住繁琐的参数。用谷哥度娘检索效率低下,通过man命令显示的结果又不易阅读。

    谭庆波
  • 《笨办法学Python》 第12课手记

    《笨办法学Python》 第12课手记 本节课接着讲raw_input这个函数,其后面的括号里的字符串可以显示在屏幕上。 原代码如下: age = raw_in...

    Steve Wang
  • wchar_t*,wchar_t,wchat_t数组,char,char*,char数组,std::string,std::wstring,CString 以及system("command")

    用户1258909
  • Java字节流与字符流的区别

    字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢? 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而...

    Java团长
  • 深度学习AI美颜系列----AI美发算法(美妆相机/天天P图染发特效)

    给照片或者视频中的人物头发换颜色,这个技术已经在手机app诸如天天P图,美图秀秀等应用中使用,并获得了不少用户的青睐。如何给照片或者视频中的人物头发换发色?换发...

    OpenCV学堂
  • Redis源码学习之字典

    在字典结构体中,包含了一组字典函数(dictType),通过封装的方法处理对应的操作,通常在字典初始化的时候对其进行配置。

    里奥搬砖
  • java常用正则表达式

    只能输入数字:"^[0-9]*$"。 只能输入n位的数字:"^\d{n}$"。 只能输入至少n位的数字:"^\d{n,}$"。 只能输入m~n位的数...

    哲洛不闹
  • 机器人对话和导航任务的学习和推理(cs.AI)

    强化学习和概率推理算法旨在分别从互动体验和概率语境知识中学习推理。在本研究中,我们开发了机器人任务完成算法,同时研究了强化学习和概率推理技术的辅助优势。机器人从...

    Donuts_choco

扫码关注云+社区

领取腾讯云代金券