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)

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

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏你不就像风一样

Java虚拟机性能监测工具Visual VM与OQL对象查询语言

Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具,它集成了多种性能统计工具的功能,使用 Visual VM 可以代替jstat、jmap、...

2383
来自专栏python小白到大牛

零基础学习python编程不可错过的学习总结,小白福利!

通过以上可以看到我们写的很贱的程序随便保存了一个.txt结尾的格式,竟然也执行了,并没有按照统一要求的.py格式来设计, 那是不是说明后缀名可以说是任意的呢?理...

1483
来自专栏小白的技术客栈

Python内置数据结构之字典

今天给大家讲解Python内置数据结构:字典。字典的内容比较多,今天只是简单地介绍一下,明天会继续补充字典相关的内容。 关于Windows的环境安装及配置,小白...

3124
来自专栏程序员互动联盟

【基础编程】聊聊C语言-变量的寿命

上一篇在编程世界的容器中,我们讲述了程序中的数据都存储在变量中,而变量根据数据类型的不同所占用的内存大小也不一样。但是计算机的内存大小是有限的不可能无限的分配下...

3807
来自专栏coolblog.xyz技术专栏

Dubbo 源码分析 - 自适应拓展原理

我在上一篇文章中分析了 Dubbo 的 SPI 机制,Dubbo SPI 是 Dubbo 框架的核心。Dubbo 中的很多拓展都是通过 SPI 机制进行加载的,...

1092
来自专栏大内老A

ASP.NET Web API中的Controller

虽然通过Visual Studio向导在ASP.NET Web API项目中创建的 Controller类型默认派生与抽象类型ApiController,但是A...

19410
来自专栏Golang语言社区

转--Golang语言-- Web 编程

1.golang的安装工具 1.1 GVM 第三方开发的Go多版本管理工具 2.golang环境变量 2.1 GOROOT=D:\go (golang 安装目录...

3636
来自专栏技术总结

iOS不可错过的关键字

建议查看原文:https://www.jianshu.com/p/dce05b24d288(不定时更新)

853
来自专栏Jackson0714

C#多线程之旅(4)——APM初探

40813
来自专栏Golang语言社区

转-Go语言开发常见陷阱,你遇到过几个?

Go作为一种简便灵巧的语言,深受开发者的喜爱。但对于初学者来说,要想轻松驾驭它,还得做好细节学习工作。 初学者应该注意的地方: 大括号不能独立成行。 未使用变量...

3539

扫码关注云+社区

领取腾讯云代金券