第七章 基本类型《C语言程序设计现代方法(第2版)》读书笔记(二)

简介: 第七章 基本类型《C语言程序设计现代方法(第2版)》读书笔记(二)

7.3.4 转义序列 🚀

       然而,一些特殊符号(比如换行符)是无法采用上述方式书写的,因为它们不可见(非打印字符),或者无法从键盘输入。因此,为了使程序可以处理字符集中的每一个字符,C语言提供了一种特殊的 表示法——转义序列 ( escape sequence ).。

       转义序列共有两种: 字符转义序列 ( character escape )和 数字转义序列 (numeric escape )。


image.png



       字符转义序列使用起来很容易,但是它们有一个问题:转义序列列表没有包含所有无法打印的ASCII 字符,只包含了最常用的字符。字符转义序列也无法用于表示基本的 128 个 ASCII 字符以外的字符。数字转义序列可以表示任何 字符,所以它可以解决上述问题。

       为了把特殊字符书写成数字转义序列,首先需要在类似附录E那样的表中查找字符的八进制或十六进制值。例如,某个ASCII码转义字符(十进制值为27)对应的八进制值为33,对应的十六进制值为1B。上述八进制或十六进制码可以用来书写转义序列。


image.png


7.3.6scanf printf /写字符 🚀


转换说明%c允许scanf函数和printf函数对单个字符进行读/写操作:


image.png


7.3.7getchar putchar /写字符 🚀

C语言还提供了另外一些读/写单个字符的方法。特别是,可以使用getchar函数和

putchar函数来取代scanf函数和printf函数。putchar函数用于写单个字符:


image.png


image.png


image.png


7.4 类型转换 🚀

C 语言则允许在表达式中混合使用基本类型。在单个表达式中可以组合整数、浮点数,甚至是字符。当然,在这种情况下 C 编译器可能需要生成一些指令将某些操作数转换成不同类型,使得硬件可以对表达式进行计算。例如,如果对16 位 short 型数和 32 位 int 型数进行加法操作,那么编译器将安排把16 位 short 型值转换成 32 位值。如果是 int 型数据和 float 型数据进行加法操作,那么编译器将安排把int 型值转换成为 float 格式。这个转换过程稍微复杂一些,因为 int型值和float 型值的存储方式不同。

因为编译器可以自动处理这些转换而无需程序员介入,所以这类转换称为 隐式转换(implicit conversion )。 C 语言还允许程序员使用强制运算符执行 显式转换 ( explicitconversion)。

当发生下列情况时会进行隐式转换。


image.png


7.4.1 常用算术转换 🚀


image.png


7.4.2 赋值过程中的转换 🚀

       常用算术转换不适用于赋值运算。C 语言会遵循另一条简单的转换规则,那就是把赋值运算右边的表达式转换成左边变量的类型。如果变量的类型至少和表达式类型一样“宽”,那么这种转换将没有任何障碍。


image.png


image.png


7.4.4 强制类型转换 🚀


image.png

image.png

image.png


7.5.2 类型定义和可移植性 🚀

C语言库自身使用typedef为那些可能依据C语言实现的不同而不同的类型创建类型名;这

些类型的名字经常以_t结尾比如ptrdiff_tsize_twchar_t。这些类型的精确定义不尽相同,下面是一些常见的例子:


image.png


C99中,<stdint.h>头使用typedef定义占用特定位数的整数类型名。例如,int32_t是恰好占用32位的有符号整型。这是一种有效的定义方式,能使程序更易于移植。


7.6 sizeof 运算符 🚀


sizeof运算符允许程序存储指定类型值所需空间的大小。表达式



image.png


问与答 🚀

问:7.1节说到%o%x分别用于以八进制和十六进制书写无符号整数。那么如何以八进制和十六进制书写普通的(有符号整数呢?p.92答:只要有符号整数的值不是负值,就可以用 %o和%x 显示。这些转换导致 printf 函数把有符号整数看成是无符号的;换句话说,printf 函数将假设符号位是数的绝对值部分。只要符号位为 0 ,就没有问题。如果符号位为1 ,那么 printf 函数将显示出一个超出预期的大数。


问:但是,如果数是负数该怎么办呢?如何以八进制或十六进制形式书写它?

答:没有直接的方法可以书写负数的八进制或十六进制形式。幸运的是,需要这样做的情况非常少。当然,我们可以判定这个数是否是负数,然后自己显示一个负号:


image.png


问:浮点常量为什么存储成double格式而不是float格式?(p.94)


答:由于历史的原因, C语言更倾向于使用double类型,float 类型则被看成是“二等公民”。思考 Kernighan和Ritchie 的 The C Programming Language 一书中关于 float 的论述:“使用 float 类型的主要原因是节省大型数组的存储空间,或者有时是为了节省时间,因为在一些机器上双精度计算的开销格外大。” 经典C 要求所有浮点计算都采用双精度的格式。(C89和C99没有这样的要求。)


*问:十六进制的浮点常量是什么样子?使用这种浮点常量有什么好处?(p.94)


答:十六进制浮点常量以 0x或0X开头,且必须包含指数(指数跟在字母P或p后面)。指数可以有符号,常量可以以f、F、l或L结尾。指数以十进制数表示,但代表的是2的幂而不是10 的幂。例如, 0x1.Bp3表示1.6875×2 3 = 13.5 。十六进制位 B 对应的位模式为 1011 ;由于 B 出现在小数点的右边,所以其每一位代表一个2 的负整数幂,把它们(2^- 1 + ^- 3 + 2^- 4 )相加得到 0.6875 。

十六进制浮点常量主要用于指定精度要求较高的浮点常量(包括e 和 π 等数学常量)。十进制数具有精确的二进制表示,而十进制常量在转换为二进制时则可能会受到舍入误差的些许影响。十六进制数对于定义极值(例如<float.h> 头中宏的值)常量也是很有用的,这些常量很容易用十六进制表示但难以用十进制表示。



*问:为什么使用%lf读取double类型的值,而用%f进行显示呢?(p.94)


答:这是一个十分难回答的问题。首先,注意, scanf函数和printf 函数都是不同寻常的函数,因为它们都没有将函数的参数限制为固定数量。scanf 函数和 printf 函数有可变长度的参数列表( 26.1节)。当调用带有可变长度参数列表的函数时,编译器会安排float 参数自动转换成为 double 类型,其结果是printf 函数无法区分 float 类型和 double 类型的参数。这解释了在 printf 函数调用中为何可以用%f 既表示 float 类型又表示 double 类型的参数。

另一方面, scanf 函数是通过 指针 指向变量的。 %f 告诉 scanf 函数在所传地址位置上存储一个

float 类型值,而 %lf 告诉 scanf 函数在该地址上存储一个 double 类型值。这里 float和double

的区别是非常重要的。如果给出了错误的转换说明,那么 scanf 函数将可能存储错误的字节数量(没有提到的是,float 类型的位模式可能不同于 double 类型的位模式)。


问:什么时候需要考虑字符变量是有符号的还是无符号的?p.96

答:如果在变量中只存储7位的字符,那么不需要考虑,因为符号位将为零。但是,如果计划存储8位字符,那么将希望变量是unsigned char类型。思考下面的例子:



如果已经把变量 ch 声明成 char 类型,那么编译器可能选择把它看作是有符号的字符来处理(许多编译器这么做)。只要变量ch 只是作为字符来使用,就不会有什么问题。

但是如果 ch 用在一些需要编译器将其值转换为整数的上下文中,那么可能就有问题了:转换为整数的结果将是负数,因为变量ch的符号位为 1 。

还有另外一种情况:在一些程序中,习惯使用 char 类型变量存储单字节的整数。如果编写了这类程序,就需要决定每个变量应该是signed char 类型的还是 unsigned char 类型的,这就像需要决定普通整型变量应该是int 类型还是 unsigned int 类型一样。



问:我无法理解换行 ( new-line ) 符怎么会是 ASCII 码的回行 ( line-feed ) 符。当用户录入输入内容并且按回车键时,程序不会把它作为回车符或者回车加回行符读取吗?( p.97 )

答:不会的。作为 C语言的UNIX 继承部分,行的结束位置标记一直被作为单独的回行符来看待。(在 UNIX文本文件中,单独一个回行符(但不是回车符)会出现在每行的结束处。)C 语言函数库会把用户的按键翻译成回行符。当程序读文件时,输入/输出函数库将文件的行结束标记(不管它是什么)翻译成单个的回行符。与之相对应的反向转换发生在将输出往屏幕或文件中写的时候。(详见22.1节。)

虽然这些翻译可能看上去很混乱,但是它们都为了一个重要的目的:使程序不受不同操作系统的影响。



*问:使用转义序列\?的目的是什么?(p.97)


答:转义序列 \? 与三字符序列( 25.3 节)有关,因为三字符序列以 ?? 开头。如果需要在字符串中加入 ?? ,那么编译器很可能会把它误当作三字符序列的开始。用\? 代替第二个?可以解决这个问题。



问:既然getchar函数的读取速度更快,为什么仍然需要使用scanf函数读取单个的字符呢?(p.98)


答:虽然 scanf 函数没有 getchar 函数读取的速度快,但是它更灵活。正如前面已经看到的,格式串 "%c"可以使scanf函数读入下一个输入字符;" %c"则可以使scanf 函数读入下一个非空白字符。而且,scanf函数也很擅长读取混合了其他数据类型的字符。假设输入数据中包含有一个整数、一个单独的非数值型字符和另一个整数。通过使用格式串"%d%c%d" ,就可以利用 scanf 函数读取全部三项内容。



*问:在什么情况下,整值提升会把字符或短整数转换成unsigned int类型?(p.101)


答:如果 int类型整数没有大到足以包含所有可能的原始类型值,整值提升会产生unsigned int 类型。因为字符通常是8 位的长度,几乎总会转化为 int 类型(可以保证 int 类型至少为 16 位长度)。有符号短整数也总可以转换为int 类型,但无符号短整数却是有疑问的。如果短整数和普通整数的长度相同(例如在16 位机上),那么无符号短整数必须被转化为 unsigned int 类型,因为最大的无符号短整数(在16 位机上为 65 535 )要大于最大的 int 类型数(即 32 767 )。


问:如果把超出变量取值范围的值赋值给变量,究竟会发生什么?(p.102)


答:粗略地讲,如果值是整值类型并且变量是无符号类型,那么会丢掉超出的位数;如果变量是有符号类型,那么结果是由实现定义的。把浮点数赋值给整型或浮点型变量的话,如果变量太小而无法承受,会产生未定义的 行为:任何事情都可能发生,包括程序终止。

* 问:为什么 C 语言要提供类型定义呢?定义一个 BOOL 宏不是和用 typedef 定义一个 Bool 类型一样好用吗?( p.105 )

答:类型定义和宏定义存在两个重要的不同点。首先,类型定义比宏定义功能更强大。特别是,数组和指针类型是不能定义为宏的。假设我们试图使用宏来定义一个“指向整数的指针”类型:


image.png


可惜的是,只有p是指针,q和r都成了普通的整型变量。类型定义不会有这样的问题。


其次, typedef命名的对象具有和变量相同的作用域规则;定义在函数体内的typedef名字在函 数外是无法识别的。另一方面,宏的名字在预处理时会在任何出现的地方被替换掉。



问:你提到“编译器本身通常就能够确定sizeof表达式的值”。难道编译器不总能确定sizeof表达式的值吗?(p.106)

答:在 C89 中编译器总是可以的,但在 C99 中有一个例外。编译器不能确定变长数组( 8.3 节)的大小,因为数组中的元素个数在程序执行期间是可变的。


相关文章
|
1月前
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
63 16
|
1月前
|
算法 C语言
【C语言程序设计——循环程序设计】求解最大公约数(头歌实践教学平台习题)【合集】
采用欧几里得算法(EuclideanAlgorithm)求解两个正整数的最大公约数。的最大公约数,然后检查最大公约数是否大于1。如果是,就返回1,表示。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。作为新的参数传递进去。这个递归过程会不断进行,直到。有除1以外的公约数;变为0,此时就找到了最大公约数。开始你的任务吧,祝你成功!是否为0,如果是,那么。就是最大公约数,直接返回。
77 18
|
1月前
|
Serverless C语言
【C语言程序设计——循环程序设计】利用循环求数值 x 的平方根(头歌实践教学平台习题)【合集】
根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码,求解出数值x的平方根;运用迭代公式,编写一个循环程序,求解出数值x的平方根。注意:不能直接用平方根公式/函数求解本题!开始你的任务吧,祝你成功!​ 相关知识 求平方根的迭代公式 绝对值函数fabs() 循环语句 一、求平方根的迭代公式 1.原理 在C语言中,求一个数的平方根可以使用牛顿迭代法。对于方程(为要求平方根的数),设是的第n次近似值,牛顿迭代公式为。 其基本思想是从一个初始近似值开始,通过不断迭代这个公式,使得越来越接近。
57 18
|
1月前
|
C语言
【C语言程序设计——循环程序设计】统计海军鸣放礼炮声数量(头歌实践教学平台习题)【合集】
有A、B、C三艘军舰同时开始鸣放礼炮各21响。已知A舰每隔5秒1次,B舰每隔6秒放1次,C舰每隔7秒放1次。编程计算观众总共听到几次礼炮声。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。开始你的任务吧,祝你成功!
52 13
|
1月前
|
存储 安全 C语言
【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】
分支的语句,这可能不是预期的行为,这种现象被称为“case穿透”,在某些特定情况下可以利用这一特性来简化代码,但在大多数情况下,需要谨慎使用。编写一个程序,该程序需输入个人数据,进而预测其成年后的身高。根据提示,在右侧编辑器补充代码,计算并输出最终预测的身高。分支下的语句,提示用户输入无效。常量的值必须是唯一的,且在同一个。语句的作用至关重要,如果遗漏。开始你的任务吧,祝你成功!,程序将会继续执行下一个。常量都不匹配,就会执行。来确保程序的正确性。
66 10
|
1月前
|
小程序 C语言
【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】
目录 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果: 任务描述 相关知识 编程编写一个程序,从键盘输入3个变量的值,例如a=5,b=6,c=7,然后将3个变量的值进行交换,使得a=6,b=7,c=5。面积=sqrt(s(s−a)(s−b)(s−c)),s=(a+b+c)/2。使用输入函数获取半径,格式指示符与数据类型一致,实验一下,不一致会如何。根据提示,在右侧编辑器补充代码,计算并输出圆的周长和面积。
39 10
|
1月前
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
36 3
|
1月前
|
存储 算法 安全
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
20 2
|
1月前
|
存储 C语言
【C语言程序设计——循环程序设计】利用数列的累加和求 sinx(头歌实践教学平台习题)【合集】
项的累加和,一般会使用循环结构,在每次循环中计算出当前项的值(可能基于通项公式或者递推关系),然后累加到一个用于存储累加和的变量中。在C语言中推导数列中的某一项,通常需要依据数列给定的通项公式或者前后项之间的递推关系来实现。例如,对于一个简单的等差数列,其通项公式为。的级数,其每一项之间存在特定的递推关系(后项的分子是其前项的分子乘上。,计算sinx的值,直到最后一项的绝对值小于。为项数),就可以通过代码来计算出指定项的值。对于更复杂的数列,像题目中涉及的用于近似计算。开始你的任务吧,祝你成功!
46 6
|
1月前
|
存储 编译器 C语言
【C语言程序设计——函数】回文数判定(头歌实践教学平台习题)【合集】
算术运算于 C 语言仿若精密 “齿轮组”,驱动着数值处理流程。编写函数求区间[100,500]中所有的回文数,要求每行打印10个数。根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码。如果操作数是浮点数,在 C 语言中是不允许直接进行。的结果是 -1,因为 -7 除以 3 商为 -2,余数为 -1;注意:每一个数据输出格式为 printf("%4d", i);的结果是 1,因为 7 除以 -3 商为 -2,余数为 1。取余运算要求两个操作数必须是整数类型,包括。开始你的任务吧,祝你成功!
52 1

热门文章

最新文章