3.C Primer Plus 第三章(2)

《C Primer Plus》第三章(2)

3.4.c语言基本数据类型

3.4.1.int类型

c语言中的整数类型可表示不同的取值范围(即int的取值范围)和正负值,一般情况使用int类型即可,但有时为满足特定任务和机器的要求,也可选择其他类型。int类型是有符号的整型数据,即int类型的值必须是整数,可以是正整数、负整数或零。其取值范围依计算机系统而异。一般而言,储存一个int要占用一个机器的字长,因此,早期的16位IBM pc兼容机使用16位来储存一个int值,其取值范围为-32768~ 32767。目前个人计算机字长为32位,因此用32位储存一个int值,现在,个人计算机的字长正朝向64位发展,因而能储存更大的整数。而c语言中规定最小的int的取值范围为-32768~32767****。一般而言,系统需要用一个特殊位的值来表示有符号整数的正负号。

1.声明int变量

1.声明单变量

先写上int,然后写变量名,最后加上一个分号。

例如:int erns

2.声明多变量

要声明多个变量,可以单独声明每个变量,也可在int后面列出多个变量名,变量名之间用逗号分隔。

例如:“int hogs,cows,goats;”=”int hogs;

​ int cows

​ int goats“

可以分别在4条声明中声明各变量,也可以在一条声明中声明4个变量。两种方法的效果相同,即都是4个int大小的变量赋予名称并分配内存空间。

2.赋值方法

1.直接赋值即可

例如:cows=112;

2.通过函数(如,scanf())获取值。
3.初始化变量,如下讲解

3.初始化变量

初始化变量即为为变量赋一个初始值。在c语言中,初始化可以直接在声明中完成。只要在变量名后面加上赋值运算符(=)和待赋给变量的值即可。如下所示:

int hogs= 21;

int cows = 32,goats = 14;

int dogs,cats = 94
以上示例中最后一行,只初始化了cats,并未初始化dogs,而这种写法很容易让人理解为dogs也被初始化为94,所以最好不要把未初始化的变量和初始化的变量放在同一条声明中。

简单来说,初始化变量就是声明为变量创建和标记储存空间,并为其指定初始值。过程如下图:

4.int类型常量

上面示例中出现的整数(21、32、14和94)都是整型常量或整型字面量。c语言把不含小数点和指数的数作为整数。因此,-22和22都是整型常量,而22.0和2.2E1则不是。c语言把大多数整型常量视为int类型,但是非常大的整数除外,这样的常数需要考虑到:”long常量“和”long long 常量"对long int类型的讨论。

5.打印int值

可以用printf()函数打印int类型的值。%d指明了在一 行中打印整数的位置。%d称为转换说明,它指定了printf()应使用什么格式 来显示一个值。格式化字符串中的每个%d都与待打印变量列表中相应的int 值匹配。这个值可以是int类型的变量、int类型的常量或其他任何值为int类型 的表达式。作为程序员,要确保转换说明的数量与待打印值的数量相同,编 译器不会捕获这类型的错误。

程序清单3.2print1.c程序


在第一行输入中,第一个%d对应int类型变量ten;第二个%d对应int类型常量2;第三个%d对应int类型表达式ten-two的值。

而第二行输入中,第一个%d对应的int类型变量ten,第二及第三个%d没有对应任何变量及任何值,所以打印出的值为内存中的任意值(读者在运行该程序时显示这两个数值会与输出示例中的数值不同,由于内存中储存的数值不同,而编译器管理内存的位置也不同)。

使用printf()函数时注意:。由于,printf()函数的参数数目不定,可以有1个、2个、 3个或更多,编译器也爱莫能助于你遗漏参数的问题,编译器无法捕捉这样的问题。**记住,使用printf()函数时,要确保转换说 明的数量与待打印值的数量相等。

6.八进制和十六进制

通常,c语言都假定整型常量是十进制数。然而,很多程序员很喜欢使用八进制和十六进制数,因为8和16都是二的幂,而10却不是。显然,八进制 和十六进制记数系统在表达与计算机相关的值时很方便。。例如,十进制数 65536经常出现在16位机中,用十六进制表示正好是10000。另外,十六进制 数的每一位的数恰好由4位二进制数表示。例如,十六进制数3是0011,十六 进制数5是0101。因此,十六进制数35的位组合(bit pattern)是00110101, 十六进制数53的位组合是01010011。这种对应关系使得十六进制和二进制的 转换非常方便。在C语言中,用特定的前缀表示使用哪种进制。0x或0X前缀表示十六进 制值,所以十进制数16表示成十六进制是0x10或0X10。与此类似,0前缀表 示八进制。例如,十进制数16表示成八进制是020。要清楚,使用不同的进制数是为了方便,不会影响数被储存的方式。也 就是说,无论把数字写成16、020或0x10,储存该数的方式都相同,因为计 算机内部都以二进制进行编码。

7.显示八进制和十六进制

在C程序中,既可以使用和显示不同进制的数。不同的进制要使用不同 的转换说明。以十进制显示数字,使用%d;以八进制显示数字,使用%0; 以十六进制显示数字,使用%x。另外,要显示各进制数的前缀0、0x和0X, 必须分别使用%#0、%#x、%#X。如下图示例:

该程序以3种不同记数系统显示同一个值。printf()函数做了相应的转 换。注意,如果要在八进制和十六进制值前显示0和0x前缀,要分别在转换 说明中加入#。

3.4.2其他整数类型

其他基本整数类型

1.short int类型

short int 类型(可简写为short)占用的储存空间可能比int类型少,常用于较小数值的场合以节省空间。与int类似,short是有符号类型。

2.long int 类型

long int或者long占用的储存空间可能比int类型多,适用于较大数值的场合。与int、short类似,long也是有符号类型。

3.long long int类型(C99标准加入)

long long int 或long long(C99标准加入)占用的存储空间可能比long多,适用于更大数值的场合。该类型至少占64位(二进制码)。与int、short、long类似,long long也是有符号类型。

4.unsigned int 或者unsigned

unsigned int 或者unsigned只用于非负值的场合。这种类型与有符号类型表示的范围不同。例如:16位unsigned int允许的取值范围是065535,而非-3276832767.用于表示符号的位现在用于表示另一个二进制位,所以无符号整型可以表示更大的数。
C90标准中,添加了unsigned long int或者unsigned long 和unsigned int 或者unsigned short类型。C99标准中又添加了unsigned long long int或者unsigned long long。

5.signed int等类型

在任何有符号类型前面添加关键字signed,可以强调使用又符号类型的意图。例如:**short、short int、signed short、signed short int 都表示同一种类型。

1.声明其他整数类型

其他整数类型的声明方式与int类型相同,下面列出了一些例子。不是所有的c编译器都能识别最后三条声明,最后一个例子所有的类型是C99标准新增的。示例:

long int estine;

long johns;

short int erns;

short ribs;

unsigned int s_count;

unsigned players;

unsigned long headcount;

unsigned short yesvotes;

long long ago;

2.使用多种整数类型的原因

32位储存 int类型和long类型(使用32位可以表示的整数数值超过20亿)。现在,计算 机普遍使用64位处理器,为了储存64位的整数,才引入了long long类型。

现在,个人计算机上最常见的设置是,long long占64位,long占32位, short占16位,int占16位或32位(依计算机的自然字长而定)。原则上,这4种类型代表4种不同的大小,但是在实际使用中,有些类型之间通常有重叠。

C 标准对基本数据类型只规定了允许的最小大小**。对于 16 位机,short 和 int 的最小取值范围是[−32767,32767];对于32位机,long的最小取值范围 是[−2147483647,2147483647]。对于unsigned short和unsigned int,最小取值范 围是[0,65535];对于unsigned long,最小取值范围是[0,4294967295]。longlong类型是为了支持64位的需求,最小取值范围是 [−9223372036854775807,9223372036854775807];unsigned long long的最小取 值范围是[0,18446744073709551615]。

那么在我们考虑使用这些整数类型时,首先,考虑unsigned类型。这种类型的 数常用于计数,因为计数不用负数。而且,unsigned类型可以表示更大的正 数。如果一个数超出了int类型的取值范围,且在long类型的取值范围内时, 使用long类型。然而,对于那些long占用的空间比int大的系统,使用long类 型会减慢运算速度。因此,如非必要,请不要使用long类型。

另外要注意一 点:如果在long类型和int类型占用空间相同的机器上编写代码,当确实需要 32位的整数时,应使用long类型而不是int类型,以便把程序移植到16位机后 仍然可以正常工作。类似地,如果确实需要64位的整数,应使用long long类 型。

如果在int设置为32位的系统中要使用16位的值,应使用short类型,原因:1.节省空间(一般在占用内存较大的数组中考虑)2.计算机中某些组件使用的硬件寄存器是16位。

3.long常量和long long常量

通常,程序代码中使用的数字(如,2345)都被储存为int类型。如果使用的数值太大,编译器会尝试使用 unsigned int。如果还不够大,编译器会依次使用long、unsigned long、long long和unsigned long long类型。

有些情况下,需要编译器以long类型储存一个小数字。例如,编程时要 显式使用IBM PC上的内存地址时。另外,一些C标准函数也要求使用long类 型的值。要把一个较小的常量作为long类型对待,可以在值的末尾加上l(小 写的L)或L后缀。使用L后缀更好,因为l看上去和数字1很像。因此,在int 为16位、long为32位的系统中,会把7作为16位储存,把7L作为32位储存。l 或L后缀也可用于八进制和十六进制整数,如020L和0x10L。

类似地,在支持long long类型的系统中,也可以用ll或者LL后缀来表示long long类型的值,如5LL。且可以用ull或者ULL后缀来表示unsigned long long类型

整数溢出


可以把这些类型的数据看作汽车的里程表。当它达到它能表示的最大值时,会重新从起始点开始。

注意,当i超出(溢出)其相应类型所能表示的最大值 时,系统并未通知用户。因此,在编程时必须自己注意这类问题,溢出行为是未定义的行为C 标准并未定义有符号类型的溢出规则以 上描述的溢出行为比较有代表性,但是也可能会出现其他情况。

4.打印short、long、long long和unsigned类型

%u转换说明--unsigned int类型

%ld转换说明--打印long类型(若int和long类型的大小相同,则可以使用%d表示long类型,但这样的程序移植到其他系统可能无法正常使用)

%lx表示以十六进制格式打印long类型整 数

%lo表示以八进制格式打印long类型整数

h前缀--对应short类型

%hd表示以 十进制显示short类型的整数

%ho表示以八进制显示short类型的整数

h、l等前缀都可以与u前缀一起使用,用于表示无符号类型,如:%lu表示打印unsigned long 类型的数值

对于支持long long 的系统,%lld和%llu分别表示有符号类型和无符号类型

注意:虽然C允许使用大写或 小写的常量后缀,但是在转换说明中只能用小写。

示例如下图:


该例表明,使用错误的转换说明会得到意想不到的结果。第 1 行输出, 对于无符号变量 un,使用%d会生成负值!其原因是,无符号值 3000000000 和有符号值−129496296 在系统内存中的内部表示完全相同。因此,如果告诉printf()该数是无符号数,它打印一个值;如果告诉它 该数是有符号数,它将打印另一个值。待打印的值大于有符号值的最大值 时,会发生这种情况对于较小的正数(如96),有符号和无符号类型的存 储、显示都相同。

第2行输出,对于short类型的变量end,在printf()中无论指定以short类型 (%hd)还是int类型(%d)打印,打印出来的值都相同。这是因为在给函 数传递参数时,C编译器把short类型的值自动转换成int类型的值,发生转换的原因是:int类型被认为是计算机处理整数类型时最高效的类型。因此,在short和int类 型的大小不同的计算机中,用int类型的参数传递速度更快。

short类型使用h修饰符可以显示较大整数被截断成 short 类型值的情况,其他整数类型也相似。例如:把 65537 以二进制格式写成一个 32 位数是 00000000000000010000000000000001。使用%hd,printf()只会查看后 16 位,所以显示的值是 1。与此类似,输出的最后一行先显示了verybig的完整 值,然后由于使用了%ld,printf()只显示了储存在后32位的值。

提醒:1.程序员必须确保转换说明的数量和待打印值的数量相 同。

2.程序员还必须根据待打印值的类型使用正确的转换说明。

3.在使用 printf()函数时,切记检查每个待打印值都有对应的转换说明, 还要检查转换说明的类型是否与待打印值的类型相匹配。(匹配printf()说明符的类型)

3.4.3使用字符:char类型

char类型用于储存字符(如,字母或标点符号),但是从技术层面看, char是整数类型。因为char类型实际上储存的是整数而不是字符即用特定的编码例如ASCII码将字符转化为数字,然后将数字存储到计算机中。

标准ASCII码的范围是0~127,只需7位二进制数即可表示。通常,char 类型被定义为8位的存储单元,因此容纳标准ASCII码绰绰有余。一般而言,C语言会保证char类型足够大,以储存系统(实现C语言的系 统)的基本字符集。C语言把1字节定义为char类型占用的位(bit)数,因此无论是16位还是 32位系统,都可以使用char类型。

1.声明char类型

与其他类型变量声明方式相同,例如:

char response;

char itable,iatan;

2.字符常量和初始化

如果要把一个字符常量初始化为字母 A,不必背下 ASCII 码,用计算机 语言很容易做到。通过以下初始化把字母A赋给grade即可:

char grade = 'A';

在C语言中,用单引号括起来的单个字符被称为字符常量(character constant)。编译器一发现'A',就会将其转换成相应的代码值。单引号必不 可少。

如果省略单引号,编译器认为T是一个变量名;如果把T用 双引号括起来,编译器则认为"T"是一个字符串。实际上,字符是以数值形式储存的,所以也可使用数字代码值来赋值;其实,用'A'代替65 才是较为妥当的做法,这样在任何系统中都不会出问题。因此,最好使用字 符常量,而不是数字代码值。

奇怪的是,C语言将字符常量视为int类型而非char类型。利用字符常量的这种特性,可以定义一个字符常 量'FATE',即把4个独立的8位ASCII码储存在一个32位存储单元中。如果把 这样的字符常量赋给char类型变量grade,只有最后8位有效。因此,grade的 值是'E'。

3.非打印字符

单引号只适用于字符、数字和标点符号,浏览ASCII表会发现,有些 ASCII字符打印不出来。例如,一些代表行为的字符(如,退格、换行、终 端响铃或蜂鸣)。

三种打印方法:1.使用ASCII码,如蜂鸣字符的ASCII码为7,因此可记作”char beep=7;“

2.使用特殊的符号序列表示一些特殊的字符。这些符号序 列叫作转义序列(escape sequence)。

把转义序列赋给字符变量时,必须用单引号把转义序列括起来。例如:char nerf=’\n‘;

稍后打印变量nerf的效果是,在打印机或屏幕上另起一行。
该例表明,使用错误的转换说明会得到意想不到的结果。第 1 行输出, 对于无符号变量 un,使用%d会生成负值!其原因是,无符号值 3000000000 和有符号值−129496296 在系统内存中的内部表示完全相同。因此,如果告诉printf()该数是无符号数,它打印一个值;如果告诉它 该数是有符号数,它将打印另一个值。待打印的值大于有符号值的最大值 时,会发生这种情况对于较小的正数(如96),有符号和无符号类型的存 储、显示都相同。

第2行输出,对于short类型的变量end,在printf()中无论指定以short类型 (%hd)还是int类型(%d)打印,打印出来的值都相同。这是因为在给函 数传递参数时,C编译器把short类型的值自动转换成int类型的值,发生转换的原因是:int类型被认为是计算机处理整数类型时最高效的类型。因此,在short和int类 型的大小不同的计算机中,用int类型的参数传递速度更快。

short类型使用h修饰符可以显示较大整数被截断成 short 类型值的情况,其他整数类型也相似。例如:把 65537 以二进制格式写成一个 32 位数是 00000000000000010000000000000001。使用%hd,printf()只会查看后 16 位,所以显示的值是 1。与此类似,输出的最后一行先显示了verybig的完整 值,然后由于使用了%ld,printf()只显示了储存在后32位的值。

提醒:1.程序员必须确保转换说明的数量和待打印值的数量相 同。

2.程序员还必须根据待打印值的类型使用正确的转换说明。

3.在使用 printf()函数时,切记检查每个待打印值都有对应的转换说明, 还要检查转换说明的类型是否与待打印值的类型相匹配。(匹配printf()说明符的类型)

3.4.3使用字符:char类型

char类型用于储存字符(如,字母或标点符号),但是从技术层面看, char是整数类型。因为char类型实际上储存的是整数而不是字符即用特定的编码例如ASCII码将字符转化为数字,然后将数字存储到计算机中。

标准ASCII码的范围是0~127,只需7位二进制数即可表示。通常,char 类型被定义为8位的存储单元,因此容纳标准ASCII码绰绰有余。一般而言,C语言会保证char类型足够大,以储存系统(实现C语言的系 统)的基本字符集。C语言把1字节定义为char类型占用的位(bit)数,因此无论是16位还是 32位系统,都可以使用char类型。

1.声明char类型

与其他类型变量声明方式相同,例如:

char response;

char itable,iatan;

2.字符常量和初始化

如果要把一个字符常量初始化为字母 A,不必背下 ASCII 码,用计算机 语言很容易做到。通过以下初始化把字母A赋给grade即可:

char grade = 'A';

在C语言中,用单引号括起来的单个字符被称为字符常量(character constant)。编译器一发现'A',就会将其转换成相应的代码值。单引号必不 可少。

如果省略单引号,编译器认为T是一个变量名;如果把T用 双引号括起来,编译器则认为"T"是一个字符串。实际上,字符是以数值形式储存的,所以也可使用数字代码值来赋值;其实,用'A'代替65 才是较为妥当的做法,这样在任何系统中都不会出问题。因此,最好使用字 符常量,而不是数字代码值。

奇怪的是,C语言将字符常量视为int类型而非char类型。利用字符常量的这种特性,可以定义一个字符常 量'FATE',即把4个独立的8位ASCII码储存在一个32位存储单元中。如果把 这样的字符常量赋给char类型变量grade,只有最后8位有效。因此,grade的 值是'E'。

3.非打印字符

单引号只适用于字符、数字和标点符号,浏览ASCII表会发现,有些 ASCII字符打印不出来。例如,一些代表行为的字符(如,退格、换行、终 端响铃或蜂鸣)。

三种打印方法:1.使用ASCII码,如蜂鸣字符的ASCII码为7,因此可记作”char beep=7;“

2.使用特殊的符号序列表示一些特殊的字符。这些符号序 列叫作转义序列(escape sequence)。

把转义序列赋给字符变量时,必须用单引号把转义序列括起来。例如:char nerf=’\n‘;

稍后打印变量nerf的效果是,在打印机或屏幕上另起一行。

转义序列


转义字符\b、\f、\n、\r、\t和\v是常用的输出设备控制字符**。了 解它们最好的方式是查看它们对活跃位置的影响。换页符(\f)把活跃位置 移至下一页的开始处;换行符(\n)把活跃位置移至下一行的开始处;回车 符(\r)把活跃位置移动到当前行的开始处;水平制表符(\t)将活跃位置 移至下一个水平制表点(通常是第1个、第9个、第17个、第25个等字符位 置);垂直制表符(\v)把活跃位置移至下一个垂直制表点。

这些转义序列字符不一定在所有的显示设备上都起作用。例如,换页符 和垂直制表符在PC屏幕上会生成奇怪的符号,光标并不会移动。只有将其 输出到打印机上时才会产生前面描述的效果。

(\、'、")用于打印\、'、"字符(由于这些字符用于定义字符常量,是printf()函数的一部分,若直接使用它们会造成混乱。

最后两个转义序列(\0oo和\xhh)是ASCII码的特殊表示。如 果要用八进制ASCII码表示一个字符,可以在编码值前面加一个反斜杠(\) 并用单引号括起来

c语言中不仅可以用十进制、八进制形式表示字符常量,C语言还 提供了第3种选择——用十六进制形式表示字符常量,即反斜杠后面跟一个x 或X,再加上1~3位十六进制数字。

使用ASCII码时,注意数字和数字字符的区别。例如,在ASCII码中,字符4对应的ASCII码为52.’4‘表示字符4,而不是数值4.

ASCII码编写代码时要将032代码写成’\032‘,以便计算机更明白程序员的意图。

4.打印字符

printf()函数用%c指明待打印的字符。一个字符变量实际 上被储存为1字节的整数值。因此,如果用%d转换说明打印 char类型变量的 值,打印的是一个整数。而%c转换说明告诉printf()打印该整数值对应的字符。


注意:运行该程序时,在输入字母后不要忘记按下Enter或Return键。printf() 函数中的转换说明决定了数据的显示方式,而不是数据的储存方式,无论何种显示方式,其存储方式都相同。

5.有符号还是无符号

有些C编译器把char实现为有符号类型,这意味着char可表示的范围 是-128~127。而有些C编译器把char实现为无符号类型,那么char可表示的 范围是0~255。

C语言允许在关键字char前面使用signed或unsigned。这 样,无论编译器默认char是什么类型,signed char表示有符号类型,而 unsigned char表示无符号类型。这在用char类型处理小整数时很有用。如果 只用char处理字符,那么char前面无需使用任何修饰符。

其他类型:

1.—Bool类型:_Bool类型,用于表示布尔值,即逻辑值true和false。因 为C语言用值1表示true,值0表示false,所以_Bool类型实际上也是一种整数 类型,一位的存储空间足矣。

2.可移植类型:stdint.h和inttypes.h

3.float、double和long double类型

4.复数和虚数类型

C语言有3种复数类型:float_Complex、double_Complex和 long double _Complex。

C语言的3种虚数类型是float _Imaginary、double _Imaginary和long double _Imaginary。

float _Complex类型的变量应包含两个float类型 的值,分别表示复数的实部和虚部。

如果包含complex.h头文件,便可用complex代替_Complex,用imaginary 代替_Imaginary,还可以用I代替-1的平方根。

5.其他更多的类型

3.5使用数据类型

编写程序时,应注意合理选择所需的变量及其类型。通常,用int或float 类型表示数字,char类型表示字符。在使用变量之前必须先声明,并选择有 意义的变量名。初始化变量应使用与变量类型匹配的常数类型。例如:

int apples = 3; /* 正确 */

int oranges = 3.0; /* 不好的形式 */

把一个类型的数值初始化给不同类型的变量时,编译器会把值转换成与 变量匹配的类型,这将导致部分数据丢失。例如:

int cost = 12.99; /* 用double类型的值初始化int类型的变量 */

float pi = 3.1415926536; /* 用double类型的值初始化float类型的变量 */

许多程序员和公司内部都有系统化的命名约定,在变量名中体现其类 型。例如,用 i_前缀表示 int类型,us_前缀表示 unsigned short 类型。这样, 一眼就能看出来 i_smart 是 int 类型的变量, us_versmart是unsigned short类型 的变量。

3.6参数与陷阱

printf("Hello, pal.")函数调用有一个参 数:"Hello,pal."。双引号中的字符序列(如,"Hello,pal.")被称为字符串 (string),现在,关键是要理解无论双引号中 包含多少个字符和标点符号,一个字符串就是一个参数。

与此类似,scanf("%d", &weight)函数调用有两个参数:"%d"和 &weight。C语言用逗号分隔函数中的参数。printf()和scanf()函数与一般函数 不同,它们的参数个数是可变的。

程序要知道函数的参数个数才能正常 工作。printf()和scanf()函数用第1个参数表明后续有多少个参数,即第1个字 符串中的转换说明与后面的参数一一对应。例如,下面的语句有两个%d转 换说明,说明后面还有两个参数:

printf("%d cats ate %d cans of tuna\n", cats, cans);

后面的确还有两个参数:cats和cans。

程序员要负责确保转换说明的数量、类型与后面参数的数量、类型相匹配。现在,C 语言通过函数原型机制检查函数调用时参数的个数和类型是否正确。

所有编译器都能顺利编译并运行该程序,但其中大部分会给出警告。的确,有些编译器会捕获到这类问题,然而C标准对此未作要求。因此,计算机在运行时可能不会捕获这类错误。如果程序正常运行,很难觉察出来。

posted @ 2022-02-06 00:33  喻雅芬  阅读(71)  评论(0)    收藏  举报