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

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

目录

第七章 基本类型 🚀

7.1 整数类型 🚀

7.1.1 C99 中的整数类型 🚀

7.1.2 整数常量 🚀

7.1.4 整数溢出 🚀

7.2 浮点类型 🚀

7.2.1 浮点常量 🚀

7.2.2 读/写浮点数 🚀

7.3.1 字符操作 🚀

7.3.2 有符号字符和无符号字符 🚀

7.3.3 算术类型 🚀

7.3.4 转义序列 🚀

7.3.6 用 scanf 和 printf 读/写字符 🚀

7.3.7 用 getchar 和 putchar 读/写字符 🚀

7.4 类型转换 🚀

7.4.1 常用算术转换 🚀

7.4.2 赋值过程中的转换 🚀

7.4.4 强制类型转换 🚀

7.5.2 类型定义和可移植性 🚀

7.6 sizeof 运算符 🚀

问与答 🚀


各书读书笔记已经陆陆续续开展了哈(本书为《C语言程序设计现代方法(第二版)》)(一本书一个专栏,订阅会第一时间推荐更新哈),主要会把作者认为比较重要或者比较新奇的知识点记录下来。但是要想真的了解一本书,自己去看可能才会有更深的体会哈。


C语言入门篇进阶篇深剖篇都整理在这里了哈。然后这里是个人主页,比点头像更好找文章哈。

作者新建立的社区:非科班转码社区-CSDN社区云💖💛💙

期待hxd的支持哈🎉 🎉 🎉


最后是打鸡血环节:改变的确很难,但结果值得冒险,拿出点勇气来。路还很长,现在才刚开始而已。过去无可挽回,未来可以改变。🚀 🚀 🚀

第七章 基本类型 🚀

7.1 整数类型 🚀

C 语言支持两种根本不同的数值类型:整数类型(也称整型)和浮点类型(也称浮点型)。 整数类型 的值是整数,而 浮点类型 的值则可能还有小数部分。整数类型又分为两大类:有符号 型和无符号型。

(例如,除非额外说明,否则所有整数都是有符号的。因此,long signed int long int 是一样的类型。)另外,说明符的顺序没什么影响,所以unsigned short int和 short unsigned int 是一样的。

C语言允许通过省略单词int来缩写整数类型的名字。C程序员经常会省略int;一些新出现的基于C的语言(包括Java)甚至不允许程序员使用short intlong int这样的名字,而必须写成shortlong

6 种整数类型的每一种所表示的取值范围都会根据机器的不同而不同,但是有两条所有编译 器都必须遵守的原则首先 C 标准要求 short int int long int 中的每一种类型都要覆盖 一个确定的最小取值范围(详见 23.2 节)。其次,标准要求 int 类型不能比 short int 类型短, long int 类型不能比 int 类型短。但是, short int 类型的取值范围有可能和 int 类型的范围是 一样的, int 类型的取值范围也可以和 long int 的一样。

取值范围不是 C 标准强制的,会随着编译器的不同而不同。对于特定的实现,确定整数类型范围的一种方法是检查 头( 23.2 节)。该头是标准库的一部分,其中定义了表示每种整数类型的最大值和最小值的宏。

7.1.1 C99 中的整数类型 🚀

7.1.2 整数常量 🚀

 

请记住八进制和十六进制只是书写数的方式,它们不会对数的实际存储方式产生影响。(整 数都是以二进制形式存储的,跟表示方式无关。)任何时候都可以从一种书写方式切换到另一种书写方式,甚至可以混合使用:10 + 015 + 0x20 的值为 55 (十进制)。八进制和十六进制更适用于底层程序的编写,本书直到第20 章才会较多地用到它们。 

7.1.4 整数溢出 🚀

对整数执行算术运算时,其结果有可能因为太大而无法表示。例如,对两个 int 值进行算术 运算时,结果必须仍然能用 int 类型来表示;否则(表示结果所需的数位太多)就会发生 溢出 整数溢出时的行为要根据操作数是有符号型还是无符号型来确定。有符号整数运算中发生溢 出时,程序的行为是未定义的。回顾 4.4 节的介绍可知,未定义行为的结果是不确定的。最可能的情况是,仅仅是运算的结果出错了,但程序也有可能崩溃,或出现其他意想不到的状况。 无符号整数运算过程中发生溢出时,结果是有定义的:正确答案对 2 n 取模,其中 n 是用于存储结果的位数。例如,如果对无符号的16 位数 65 535 1 ,其结果可以保证为 0

7.2 浮点类型 🚀

整数类型并不适用于所有应用。有些时候需要变量能存储带小数点的数,或者能存储极大 数或极小数。这类数可以用浮点(因小数点是“浮动的”而得名)格式进行存储。 C 语言提供 3浮点类型 ,对应三种不同的浮点格式。   当精度要求不严格时(例如,计算带一位小数的温度), float 类型是很适合的类型。 double 提供更高的精度,对绝大多数程序来说都够用了。 long double 支持极高精度的要求,很少会 用到。         C标准没有说明 float double long double 类型提供的精度到底是多少,因为不同的计 算机可以用不同方法存储浮点数。大多数现代计算机都遵循 IEEE 754 标准(即 IEC 60559 )的规范,所以这里也用它作为一个示例。 可以在头 ( 23.1节)中找到定义浮点类型特征的宏。

7.2.1 浮点常量 🚀

浮点常量可以有许多种书写方式。例如,下面这些常量全都是表示数57.0的有效方式:

浮点常量必须包含小数点或指数;其中,指数指明了对前面的数进行缩放所需的 10 的幂次。如果有指数,需要在指数数值前放置字母E (或 e )。可选符号 + - 可以出现在字母 E (或 e )的后边。默认情况下,浮点常量都以双精度数的形式存储。 换句话说,当 C 语言编译器在程序中发 现常量 57.0 时,它会安排数据以double 类型变量的格式存储在内存中。这条规则通常不会 引发任何问题,因为在需要时double类型的值可以自动转化为float类型值。 在某些极个别的情况下,可能会需要强制编译器以 float long double 格式存储浮点常 量。为了表明只需要单精度,可以在常量的末尾处加上字母 Ff (如 57.0F );而为了说明常量必须以long double 格式存储,可以在常量的末尾处加上字母 Ll (如 57.0L )。 C99 提供了十六进制浮点常量的书写规范。十六进制浮点常量以0x 0X 开头(跟十六进制整数常量类似)。这一特性很少用到。

7.2.2/写浮点数 🚀

前面已讨论过,转换说明符 %e %f %g 用于读写单精度浮点数。读写 double long double 类型的值所需的转换说明符略有不同。

读取double类型的值时,在efg前放置字母l 

注意 只能在 scanf 函数格式串中使用 l ,不能在 printf 函数格式串中使用。在printf函数格式串中,转换 e f g 可以用来写 float 类型或 double 类型的值。(C99允许printf 函数调用中使用 %le %lf %lg ,不过字母 l 不起作用。)

读写long double类型的值时,在efg前放置字母L 

7.3.1 字符操作 🚀

C 语言中字符的操作非常简单,因为存在这样一个事实: C语言把字符当作小整数进行处 毕竟所有字符都是以二进制的形式进行编码的,而且无需花费太多的想象就可以将这些二进制代码看成整数。例如,在ASCII 码中,字符的取值范围是 0000000~1111111 ,可以看成是 0~ 127的整数。字符'a' 的值为97'A'的值为65'0'的值为48,而' '的值为32 C 中,字符和整数之间的关联是非常强的, 字符常量事实上是int类型而不是char类型 (这是一个非常有趣的现象,但对我们并无影响)。

当计算中出现字符时,C 语言只是使用它对应的整数值。思考下面这个例子,假设采用 ASCII 字符集:

可以像比较数那样对字符进行比较。下面的 if语句测试ch中是否含有小写字母,如果有, 那么它会把ch转化为相应的大写字母。

        诸如'a'<= ch 这样的比较使用的是字符所对应的整数值,这些数值依据使用的字符集有所不同,所以程序使用< <= > >= 来进行字符比较可能不易移植。         字符拥有和数相同的属性,这一事实会带来一些好处。例如,可以让for 语句中的控制变 量遍历所有的大写字母:         另一方面,以数的方式处理字符可能会导致编译器无法检查出来的多种编程错误,还可能会导致我们编写出诸如'a' * 'b' / 'c' 这类无意义的表达式。此外,这样做也可能会妨碍程 序的可移植性,因为程序可能会基于一些对字符集的假设。(例如,上述 for 循环假设从字母 A到字母Z 的代码都是连续的。)

7.3.2 有符号字符和无符号字符 🚀

C语言标准没有说明普通char类型数据是有符号型还是无符号型 ,有些编译器把它们当作 有符号型来处理,有些编译器则将它们当作无符号型来处理。(甚至还有一些编译器允许程序员通过编译器选项来选择把char 类型当成有符号型还是无符号型。)          由于字符和整数之间有密切关系,C89 采用术语 整值类型 integral type )来(统称)包含整数类型和字符类型。枚举类型( 16.5 节)也属于整值类型。         C99不使用术语“整值类型”,而是扩展了整数类型的含义使其包含字符类型和枚举类 型。 C99 中的 _ Bool 型( 5.2 节)是无符号整数类型。 

7.3.3 算术类型 🚀

7.3.4 转义序列 🚀

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

        字符转义序列使用起来很容易,但是它们有一个问题:转义序列列表没有包含所有无法打印的ASCII 字符,只包含了最常用的字符。字符转义序列也无法用于表示基本的 128 ASCII 字符以外的字符。数字转义序列可以表示任何 字符,所以它可以解决上述问题。         为了把特殊字符书写成数字转义序列,首先需要在类似附录E那样的表中查找字符的八进制或十六进制值。例如,某个ASCII码转义字符(十进制值为27)对应的八进制值为33,对应的十六进制值为1B。上述八进制或十六进制码可以用来书写转义序列。         作为字符常量使用时,转义序列必须用一对单引号括起来。例如,表示转义字符的常量可以写成'\33' (或 '\x1b' )的形式。转义序列可能有点隐晦,所以采用 #define 的方式给它们 命名通常是个不错的主意:

7.3.6scanf printf /写字符 🚀

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

在读入字符前, scanf 函数不会跳过空白字符。如果下一个未读字符是空格,那么在前面 的例子中, scanf 函数返回后变量 ch 将包含一个空格。为了强制 scanf 函数在读入字符前跳过空白字符,需要在格式串中的转换说明%c 前面加上一个空格:

回顾 3.2 节的内容, scanf格式串中的空白意味着“跳过零个或多个空白字符”。         因为通常情况下scanf 函数不会跳过空白,所以它很容易检查到输入行的结尾:检查刚读 入的字符是否为换行符。例如,下面的循环将读入并且忽略掉当前输入行中剩下的所有字符:

下次调用scanf函数时,将读入下一输入行中的第一个字符。  

7.3.7getchar putchar /写字符 🚀

C 语言还提供了另外一些读 / 写单个字符的方法。特别是,可以使用getchar 函数和 putchar函数来取代scanf函数和printf函数。 putchar 函数用于写单个字符:         每次调用getchar函数时,它会读入一个字符并将其返回。为了保存这个字符,必须使用赋值 操作将其存储到变量中: 事实上,getchar函数返回的是一个int类型的值而不是char 类型的值(因为读取失败返回-1)。因此,如果一个变量用于存储getchar 函数读取的字符,其类型设置为 int 而不是 char 也 没啥好奇怪的。和scanf 函数一样,getchar函数也不会在读取时跳过空白字符。         执行程序时,使用getchar函数和putchar函数(胜于scanf函数和printf 函数)可以节约时间。getchar 函数和 putchar 函数执行速度快有两个原因。第一个原因是,这两个函数比 scanf函数和printf 函数简单得多,因为 scanf 函数和 printf 函数是设计用来按不同的格式读 / 写多种不同类型数据的。第二个原因是,为了额外的速度提升,通常getchar 函数和 putchar 函数是作为宏( 14.3 节)来实现的。         getchar函数还有一个优于 scanf 函数的地方:因为返回的是读入的字符,所以 getchar 数可以应用在多种不同的 C语言惯用法中,包括用在搜索字符或跳过所有出现的同一字符的循环中。思考下面这个 scanf 函数循环,前面我们曾用它来跳过输入行的剩余部分: 这个循环读入一个字符,把它存储在变量 ch 中,然后测试变量 ch 是否不是换行符。如果测试结果为真,那么执行循环体(循环体实际为空),接着再次测试循环条件,从而引发读入新的字符。实际上我们并不需要变量ch,可以把 getchar 函数的返回值与换行符进行比较

7.4 类型转换 🚀

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

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

7.4.1 常用算术转换 🚀

7.4.2 赋值过程中的转换 🚀

        常用算术转换不适用于赋值运算。C 语言会遵循另一条简单的转换规则,那就是把赋值运算右边的表达式转换成左边变量的类型。如果变量的类型至少和表达式类型一样“宽”,那么这种转换将没有任何障碍。 其他情况下是有问题的。把浮点数赋值给整型变量会丢掉该数的小数部分。 此外,把某种类型的值赋给类型更狭小的变量时,如果该值在变量类型范围之外,那么将 会得到无意义的结果(甚至更糟)。

这类赋值可能会导致编译器或lint之类的工具发出警告。

如果浮点常量被赋值给float型变量时,一个很好的方法是在浮点常量尾部加上后辍f ,本 书从第 2 章开始就一直是这么做的:

如果没有后辍,常量3.14159将是double类型,可能会引起警告消息。 

7.4.3 C99 中的隐式转换

C99整数提升 integer promotion )取代了 C89中的整值提升 integral promotion ),可以将任何等级低于int unsigned int 的类型转换为 int (只要该类型的所有值都可以用 int 类型表 示)或 unsigned int

7.4.4 强制类型转换 🚀

强制类型转换表达式 (int)f 表示把 f 的值 转换成 int 类型后的结果。 C 语言的常用算术转换则要 求在进行减法运算前把 (int)f 转换回 float 类型。 f (int)f 的不同之处就在于 f 的小数部分, 这部分在强制类型转换时被丢掉了。

正如现在写的那样,除法的结果是一个整数,在把结果存储在 quotient 变量中之前,要把结果 转换成 float 格式。但是,为了得到更精确的结果,可能需要在除法执行 之前 dividend divisor 的类型转换成 float 格式的。强制类型转换表达式可以完成这一点: 变量 divisor 不需要进行强制类型转换,因为把变量 dividend 强制转换成 float 类型会迫使编 译器把 divisor 也转换成 float 类型。

有些时候,需要使用强制类型转换来避免溢出。思考下面这个例子: 

乍看之下,这条语句没有问题。表达式 j * j 的值是 1 000 000 ,并且变量 i long int 类型的,所 i 应该能很容易地存储这种大小的值,不是吗?问题是,当两个 int类型值相乘时,结果也应 该是int 类型的,但是 j * j 的结果太大,以致在某些机器上无法表示成 int 型,从而导致溢出。 幸运的是,可以使用强制类型转换避免这种问题的发生:

因为强制运算符的优先级高于 * ,所以第一个变量 j 会被转换成 long int 类型,同时也迫使第 二个 j 进行转换。注意,语句

是不对的,因为溢出在强制类型转换之前就已经发生了。

7.5.2 类型定义和可移植性 🚀

C语言库自身使用typedef为那些可能依据C语言实现的不同而不同的类型创建类型名;这 些类型的名字经常以_t结尾 ,比如 ptrdiff_t size_t wchar_t 。这些类型的精确定义不尽 相同,下面是一些常见的例子: C99 中, 头使用 typedef 定义占用特定位数的整数类型名。例如, int32_t 是恰好占用 32 位的有符号整型。这是一种有效的定义方式,能使程序更易于移植。

7.6 sizeof 运算符 🚀

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

的值是一个无符号整数,代表存储属于类型名 的值所需要的字节数。表达式 sizeof(char) 的值 始终为 1 ,但是对其他类型计算出的值可能会有所不同。在 32 位的机器上,表达式 sizeof(int) 的值通常为 4 注意, sizeof运算符是一种特殊的运算符,因为编译器本身通常就能够确 sizeof表达式的值。 通常情况下,sizeof 运算符也可以应用于常量、变量和表达式。如果 i j 是整型变量,那么sizeof ( i) 32 位机器上的值为 4 ,这和表达式 sizeof(i+j) 的值一样。跟应用于类型时不同,sizeof应用于表达式时不要求圆括号,我们可以用 sizeof i代替sizeof(i) 。但是,由于运算符优先级的问题,圆括号有时还是需要的。编译器会把表达式sizeof i + j 解释为 ( sizeof i)+ j,这是因为 sizeof 作为一元运算符的优先级高于二元运算符 + 。为了避免出现此类问题,本书在sizeof 表达式中始终加上圆括号。 显示sizeof值时要注意,因为sizeof表达式的类型是size_t,这是一种由实现定义的类 型。 C89 中,最好在显示前把表达式的值转换成一种已知的类型。 size_t 一定是无符号整型,所以最安全的方法是把sizeof 表达式强制转换成 unsigned long类型(C89 中最大的无符型),然后使用转换说明符%lu 显示:

C99 中, size_t类型可以比unsigned long更长。但C99中的printf可以直接显示出size_t类型值而不需要强制转换。方法是在转换说明中的一般整数(通常用u)代码前使用字母z:

问与答 🚀

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

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

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

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

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

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

答:十六进制浮点常量以 0x0X开头,且必须包含指数(指数跟在字母Pp后面)。指数可以有符号,常量可以以fFlL结尾。指数以十进制数表示,但代表的是2的幂而不是10 的幂。例如, 0x1.Bp3表示1.6875×2 3 = 13.5 。十六进制位 B 对应的位模式为 1011 ;由于 B 出现在小数点的右边,所以其每一位代表一个2 的负整数幂,把它们(2^- 1 + ^- 3 + 2^- 4 )相加得到 0.6875 十六进制浮点常量主要用于指定精度要求较高的浮点常量(包括e π 等数学常量)。十进制数具有精确的二进制表示,而十进制常量在转换为二进制时则可能会受到舍入误差的些许影响。十六进制数对于定义极值(例如 头中宏的值)常量也是很有用的,这些常量很容易用十六进制表示但难以用十进制表示。

*问:为什么使用%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 类型值。这里 floatdouble 的区别是非常重要的。如果给出了错误的转换说明,那么 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 答:类型定义和宏定义存在两个重要的不同点。首先,类型定义比宏定义功能更强大。特别是,数组和指针类型是不能定义为宏的。假设我们试图使用宏来定义一个“指向整数的指针”类型:

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

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

问:你提到“编译器本身通常就能够确定sizeof表达式的值”。难道编译器不总能确定sizeof表达式的值吗?(p.106) 答:在 C89 中编译器总是可以的,但在 C99 中有一个例外。编译器不能确定变长数组( 8.3 节)的大小,因为数组中的元素个数在程序执行期间是可变的。

最后的最后,创作不易,希望读者三连支持💖

赠人玫瑰,手有余香💖