C 语言中的基本数据类型
本文将详细介绍 c 语言的基本数据类型,包括如何声明变量、如何表示字面值常量(如,5 或 2.78),以及典型的用法。一些老式的 c 语言编译器无法支持这里提到的所有类型,请查阅你使用的编译器文档,了解可以使用哪些类型。
一、int 类型
c 语言提供了许多整数类型,为什么一种类型不够用?因为 c 语言让程序员针对不同情况选择不同的类型。特别是,c 语言中的整数类型可表示不同的取值范围和正负值。一般情况使用 int 类型即可,但是为满足特定任务和机器的要求,还可以选择其他类型。
int 类型是有符号整型,即 int 类型的值必须是整数,可以是正整数、负整数或零。其取值范围依计算机系统而异。一般而言,存储一个 int 要占用一个机器字长。因此,早期的 16 位 ibm pc 兼容机使用 16 位来存储一个 int 值,其取值范围(即 int 值的取值范围)是 -32768~32767。目前的个人计算机一般是 32 位,因此用 32 位存储一个 int 值。现在,个人计算机产业正逐步向着 64 位处理器发展,自然能存储更大的整数。iso c 规定 int 的取值范围最小为 -32768~32767。一般而言,系统用一个特殊位的值表示有符号整数的正负号。
1.1 声明 int 变量
先写上 int,然后写变量名,最后加上一个分号。要声明多个变量,可以单独声明每个变量,也可在 int 后面列出多个变量名,变量名之间用逗号分隔。下面都是有效的声明:
int erns; int hogs, cows, goats;
可以分别在 4 条声明中声明各变量,也可以在一条声明中声明 4 个变量。两种方法的效果相同,都为 4 个 int 大小的变量赋予名称并分配内存空间。
以上声明创建了变量,但是并没有给它们提供值。变量如何获得值?前面介绍过在程序中获取值的两种途径。第 1 种途径是赋值:
cows = 112;
第 2 种途径是,通过函数(如,scanf())获得值。接下来,我们着重介绍第 3 种途径。
1.2 初始化变量
初始化(initialize)变量就是为变量赋一个初始值。在 c 语言中,初始化可以直接在声明中完成。只需在变量名后面加上赋值运算符(=)和待赋给变量的值即可。如下所示:
int hogs = 21; int cows = 32, goats = 14; int dogs, cats = 94; /* 有效,但是这种格式很糟糕 */
以上示例的最后一行,只初始化了 cats,并未初始化 dogs。这种写法很容易让人误认为 dogs 也被初始化为 94,所以最好不要把初始化的变量和未初始化的变量放在同一条声明中。
简而言之,声明为变量创建和标记存储空间,并为其指定初始值(如图 4 所示)。
1.3 int 类型常量
上面示例中出现的整数(21、32、14 和 94)都是整型常量或整型字面量。c 语言把不含小数点和指数的数作为整数。因此,22 和 -44 都是整型常量,但是 22.0 和 2.2e1 则不是。c 语言把大多数整型常量视为 int 类型,但是非常大的整数除外。详见后面“long 常量和 long long 常量”小节对 long int 类型的讨论。
1.4 打印 int 值
可以使用 printf() 函数打印 int 类型的值。%d
指明了在一行中打印整数的位置。%d
称为转换说明,它指定了 printf() 应使用什么格式来显示一个值。格式化字符串中的每个 %d
都与待打印变量列表中相应的 int 值匹配。这个值可以是 int 类型的变量、int 类型的常量或其他任何值为 int 类型的表达式。作为程序员,要确保转换说明的数量与待打印值的数量相同,编译器不会捕获这类型的错误。程序清单 2 演示了一个简单的程序,程序中初始化了一个变量,并打印该变量的值、一个常量值和一个简单表达式的值。另外,程序还演示了如果粗心犯错会导致什么结果。
程序清单 2 print1.c 程序
/* print1.c - 演示printf()的一些特性 */ #include <stdio.h> int main(void) { int ten = 10; int two = 2; printf("doing it right: "); printf("%d minus %d is %d\n", ten, 2, ten - two); printf("doing it wrong: "); printf("%d minus %d is %d\n", ten); // 遗漏2个参数 return 0; }
编译并运行该程序,输出如下:
doing it right: 10 minus 2 is 8 doing it wrong: 10 minus 16 is 1650287143
在第一行输出中,第 1 个 %d
对应 int 类型变量 ten;第 2 个 %d
对应 int 类型常量 2;第 3 个 %d
对应 int 类型表达式 ten - two
的值。在第二行输出中,第 1 个 %d
对应 ten 的值,但是由于没有给后两个 %d
提供任何值,所以打印出的值是内存中的任意值(读者在运行该程序时显示的这两个数值会与输出示例中的数值不同,因为内存中存储的数据不同,而且编译器管理内存的位置也不同)。
你可能会抱怨编译器为何不能捕获这种明显的错误,但实际上问题出在 printf() 不寻常的设计。大部分函数都需要指定数目的参数,编译器会检查参数的数目是否正确。但是,printf() 函数的参数数目不定,可以有 1 个、2 个、3 个或更多,编译器也爱莫能助。记住,使用 printf() 函数时,要确保转换说明的数量与待打印值的数量相等。
1.5 八进制和十六进制
通常,c 语言都假定整型常量是十进制数。然而,许多程序员很喜欢使用八进制和十六进制数。因为 8 和 16 都是 2 的幂,而 10 却不是。显然,八进制和十六进制记数系统在表达与计算机相关的值时很方便。例如,十进制数 65536 经常出现在 16 位机中,用十六进制表示正好是 10000。另外,十六进制数的每一位的数恰好由 4 位二进制数表示。例如,十六进制数 3 的二进制数是 0011,十六进制数 5 的二进制数是 0101。因此,十六进制数35的位组合(bit pattern)是 00110101,十六进制数 53 的位组合是 01010011。这种对应关系使得十六进制和二进制的转换非常方便。但是,计算机如何知道 10000 是十进制、十六进制还是二进制?在 c 语言中,用特定的前缀表示使用哪种进制。0x 或 0x 前缀表示十六进制值,所以十进制数 16 表示成十六进制是 0x10 或 0x10。与此类似,0 前缀表示八进制。例如,十进制数 16 表示成八进制是 020。
要清楚,使用不同的进制数是为了方便,不会影响数被存储的方式。也就是说,无论把数字写成 16、020 或 0x10,存储该数的方式都相同,因为计算机内部都以二进制进行编码。
1.6 显示八进制和十六进制
在 c 程序中,既可以使用也可以显示不同进制的数。不同的进制要使用不同的转换说明。以十进制显示数字,使用 %d
;以八进制显示数字,使用 %o
;以十六进制显示数字,使用 %x
。另外,要显示各进制数的前缀 0、0x 和 0x,必须分别使用 %#o
、%#x
、%#x
。程序清单 3 演示了一个小程序(回忆一下,在某些集成开发环境(ide)下编写的代码中插入 getchar();
语句,程序在执行完毕后不会立即关闭执行窗口)。
程序清单 3 bases.c 程序
/* bases.c--以十进制、八进制、十六进制打印十进制数100 */ #include <stdio.h> int main(void) { int x = 100; printf("dec = %d; octal = %o; hex = %x\n", x, x, x); printf("dec = %d; octal = %#o; hex = %#x\n", x, x, x); return 0; }
编译并运行该程序,输出如下:
dec = 100; octal = 144; hex = 64 dec = 100; octal = 0144; hex = 0x64
该程序以 3 种不同记数系统显示同一个值。printf() 函数做了相应的转换。注意,如果要在八进制和十六进制值前显示 0 和 0x 前缀,要分别在转换说明中加入 #。
二、其他整数类型
初学 c 语言时,int 类型应该能满足大多数程序的整数类型需求。尽管如此,还应了解一下整型的其他形式。
c 语言提供 3 个附属关键字修饰基本整数类型:short、long 和 unsigned。应记住以下几点。
- short int 类型(或者简写为 short)占用的存储空间可能比 int 类型少,常用于较小数值的场合以节省空间。与 int 类似,short 是有符号类型。
- long int 或 long 占用的存储空间可能比 int 多,适用于较大数值的场合。与 int 类似,long 是有符号类型。
- long long int 或 long long(c99 标准加入)占用的存储空间可能比 long 多,适用于更大数值的场合。该类型至少占 64 位。与 int 类似,long long 是有符号类型。
- unsigned int 或 unsigned 只用于非负值的场合。这种类型与有符号类型表示的范围不同。例如,16 位 unsigned int 允许的取值范围是 0~65535,而不是 -32768~32767。用于表示正负号的位现在用于表示另一个二进制位,所以无符号整型可以表示更大的数。
- 在 c90 标准中,添加了 unsigned long int 或 unsigned long 和 unsigned short int 或 unsigned short 类型。c99 标准又添加了 unsigned long long int 或 unsigned long long。
- 在任何有符号类型前面添加关键字 signed,可强调使用有符号类型的意图。例如,short、short int、signed short、signed short int 都表示同一种类型。
2.1 声明其他整数类型
其他整数类型的声明方式与 int 类型相同,下面列出了一些例子。不是所有的 c 编译器都能识别最后 3 条声明,最后一个例子所有的类型是 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.2 使用多种整数类型的原因
为什么说 short 类型“可能”比 int 类型占用的空间少,long 类型“可能”比 int 类型占用的空间多?因为 c 语言只规定了 short 占用的存储空间不能多于 int,long 占用的存储空间不能少于 int。这样规定是为了适应不同的机器。例如,过去的一台运行 windows 3.x 的机器上,int 类型和 short 类型都占 16 位,long 类型占 32 位。后来,windows 和苹果系统都使用 16 位存储 short 类型,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 的最小取值范围是 [−32768,32767]
;对于 32 位机,long 的最小取值范围是 [−2147483648,2147483647]
。对于 unsigned short 和 unsigned int,最小取值范围是 [0,65535]
;对于 unsigned long,最小取值范围是 [0,4294967295]
。long long 类型是为了支持 64 位的需求,最小取值范围是 [−9223372036854775808,9223372036854775807]
;unsigned long long 的最小取值范围是 [0,18446744073709551615]
。如果要开支票,这个数是一千八百亿亿六千七百四十四万亿零七百三十七亿零九百五十五万一千六百一十五。但是,谁会去数?
int 类型那么多,应该如何选择?首先,考虑 unsigned 类型。这种类型的数常用于计数,因为计数不用负数。而且,unsigned 类型可以表示更大的正数。
如果一个数超出了 int 类型的取值范围,且在 long 类型的取值范围内时,使用 long 类型。然而,对于那些 long 占用的空间比 int 大的系统,使用 long 类型会减慢运算速度。因此,如非必要,请不要使用 long 类型。另外要注意一点:如果在 long 类型和 int 类型占用空间相同的机器上编写代码,当确实需要 32 位的整数时,应使用 long 类型而不是 int 类型,以便把程序移植到 16 位机后仍然可以正常工作。类似地,如果确实需要 64 位的整数,应使用 long long 类型。
如果在 int 设置为 32 位的系统中要使用 16 位的值,应使用 short 类型以节省存储空间。通常,只有当程序使用相对于系统可用内存较大的整型数组时,才需要重点考虑节省空间的问题。使用 short 类型的另一个原因是,计算机中某些组件使用的硬件寄存器 是16 位。
2.3 long 常量和 long long 常量
通常,程序代码中使用的数字(如,2345)都被存储为 int 类型。如果使用 1000000 这样的大数字,超出了 int 类型能表示的范围,编译器会将其视为 long int 类型(假设这种类型可以表示该数字)。如果数字超出 long 可表示的最大值,编译器则将其视为 unsigned long 类型。如果还不够大,编译器则将其视为 long long 或 unsigned long long 类型(前提是编译器能识别这些类型)。
八进制和十六进制常量被视为 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 类型的值,如 3ll。另外,u 或 u 后缀表示 unsigned long long,如 5ull、10llu、6llu 或 9ull。
整数溢出
如果整数超出了相应类型的取值范围会怎样?下面分别将有符号类型和无符号类型的整数设置为比最大值略大,看看会发生什么(printf() 函数使用
%u
说明显示 unsigned int 类型的值)。/* toobig.c-- 超出系统允许的最大int值*/ #include <stdio.h> int main(void) { int i = 2147483647; unsigned int j = 4294967295; printf("%d %d %d\n", i, i+1, i+2); printf("%u %u %u\n", j, j+1, j+2); return 0; }在我们的系统下输出的结果是:
2147483647 -2147483648 -2147483647 4294967295 0 1可以把无符号整数 j 看作是汽车的里程表。当达到它能表示的最大值时,会重新从起始点开始。整数 i 也是类似的情况。它们主要的区别是,在超过最大值时,unsigned int 类型的变量 j 从 0 开始;而 int 类型的变量 i 则从 −2147483648 开始。注意,当 i 超出(溢出)其相应类型所能表示的最大值时,系统并未通知用户。因此,在编程时必须自己注意这类问题。
溢出行为是未定义的行为,c 标准并未定义有符号类型的溢出规则。以上描述的溢出行为比较有代表性,但是也可能会出现其他情况。
2.4 打印 short、long、long long 和 unsigned 类型
打印 unsigned int 类型的值,使用 %u
转换说明;打印 long 类型的值,使用 %ld
转换说明。如果系统中 int 和 long 的大小相同,使用 %d
就行。但是,这样的程序被移植到其他系统(int 和 long 类型的大小不同)中会无法正常工作。在 x 和 o 前面可以使用 l 前缀,%lx
表示以十六进制格式打印 long 类型整数,%lo
表示以八进制格式打印 long 类型整数。注意,虽然 c 允许使用大写或小写的常量后缀,但是在转换说明中只能用小写。
c 语言有多种 printf() 格式。对于 short 类型,可以使用h前缀。%hd
表示以十进制显示 short 类型的整数,%ho
表示以八进制显示 short 类型的整数。h 和 l 前缀都可以和 u 一起使用,用于表示无符号类型。例如,%lu
表示打印 unsigned long 类型的值。程序清单 4 演示了一些例子。对于支持 long long 类型的系统,%lld
和 %llu
分别表示有符号和无符号类型。
程序清单 4 print2.c 程序
/* print2.c--更多printf()的特性 */ #include <stdio.h> int main(void) { unsigned int un = 3000000000; /* int为32位和short为16位的系统 */ short end = 200; long big = 65537; long long verybig = 12345678908642; printf("un = %u and not %d\n", un, un); printf("end = %hd and %d\n", end, end); printf("big = %ld and not %hd\n", big, big); printf("verybig= %lld and not %ld\n", verybig, verybig); return 0; }
在特定的系统中输出如下(输出的结果可能不同):
un = 3000000000 and not -1294967296 end = 200 and 200 big = 65537 and not 1 verybig= 12345678908642 and not 1942899938
该例表明,使用错误的转换说明会得到意想不到的结果。第 1 行输出,对于无符号变量 un,使用 %d
会生成负值!其原因是,无符号值 3000000000 和有符号值 −129496296 在系统内存中的内部表示完全相同。因此,如果告诉 printf() 该数是无符号数,它打印一个值;如果告诉它该数是有符号数,它将打印另一个值。在待打印的值大于有符号值的最大值时,会发生这种情况。对于较小的正数(如 96),有符号和无符号类型的存储、显示都相同。
第 2 行输出,对于 short 类型的变量 end,在 printf() 中无论指定以 short 类型(%hd)还是 int 类型(%d)打印,打印出来的值都相同。这是因为在给函数传递参数时,c 编译器把 short 类型的值自动转换成 int 类型的值。你可能会提出疑问:为什么要进行转换?h 修饰符有什么用?第 1 个问题的答案是,int 类型被认为是计算机处理整数类型时最高效的类型。因此,在 short 和 int 类型的大小不同的计算机中,用 int 类型的参数传递速度更快。第 2 个问题的答案是,使用 h 修饰符可以显示较大整数被截断成 short 类型值的情况。第 3 行输出就演示了这种情况。把 65537 以二进制格式写成一个 32 位数是 00000000000000010000000000000001。使用 %hd
,printf() 只会查看后 16 位,所以显示的值是 1。与此类似,输出的最后一行先显示了 verybig 的完整值,然后由于使用了 %ld
,printf() 只显示了存储在后 32 位的值。
前面介绍过,程序员必须确保转换说明的数量和待打印值的数量相同。以上内容也提醒读者,程序员还必须根据待打印值的类型使用正确的转换说明。
提示 匹配 printf() 说明符的类型
在使用printf()函数时,切记检查每个待打印值都有对应的转换说明,还要检查转换说明的类型是否与待打印值的类型相匹配。
三、使用字符:char 类型
char 类型用于存储字符(如,字母或标点符号),但是从技术层面看,char 是整数类型。因为 char 类型实际上存储的是整数而不是字符。计算机使用数字编码来处理字符,即用特定的整数表示特定的字符。美国最常用的编码是 ascii 编码。例如,在 ascii 码中,整数 65 代表大写字母 a。因此,存储字母 a 实际上存储的是整数 65(许多 ibm 的大型主机使用另一种编码——ebcdic,其原理相同。另外,其他国家的计算机系统可能使用完全不同的编码)。
标准 ascii 码的范围是 0~127,只需 7 位二进制数即可表示。通常,char 类型被定义为 8 位的存储单元,因此容纳标准 ascii 码绰绰有余。许多其他系统(如 imb pc 和苹果 macs)还提供扩展 ascii 码,也在 8 位的表示范围之内。一般而言,c 语言会保证 char 类型足够大,以存储系统(实现 c 语言的系统)的基本字符集。
许多字符集都超过了 127,甚至多于 255。例如,日本汉字(kanji)字符集。商用的统一码(unicode)创建了一个能表示世界范围内多种字符集的系统,目前包含的字符已超过 110000 个。国际标准化组织(iso)和国际电工技术委员会(iec)为字符集开发了 iso/iec 10646 标准。统一码标准也与 iso/iec 10646 标准兼容。
c 语言把 1 字节定义为 char 类型占用的位(bit)数,因此无论是 16 位还是 32 位系统,都可以使用 char 类型。
3.1 声明 char 类型变量
char 类型变量的声明方式与其他类型变量的声明方式相同。下面是一些例子:
char response; char itable, latan;
以上声明创建了 3 个 char 类型的变量:response、itable 和 latan。
3.2 字符常量和初始化
如果要把一个字符常量初始化为字母 a,不必背下 ascii 码,用计算机语言很容易做到。通过以下初始化把字母 a 赋给 grade 即可:
char grade = 'a';
在 c 语言中,用单引号括起来的单个字符被称为字符常量(character constant)。编译器一发现 'a'
,就会将其转换成相应的代码值。单引号必不可少。下面还有一些其他的例子:
char broiled; /* 声明一个char类型的变量 */ broiled = 't'; /* 为其赋值,正确 */ broiled = t; /* 错误!此时t是一个变量 */ broiled = "t"; /* 错误!此时"t"是一个字符串 */
如上所示,如果省略单引号,编译器认为 t 是一个变量名;如果把 t 用双引号括起来,编译器则认为 "t"
是一个字符串。
实际上,字符是以数值形式存储的,所以也可使用数字代码值来赋值:
char grade = 65; /* 对于ascii,这样做没问题,但这是一种不好的编程风格 */
在本例中,虽然 65 是 int 类型,但是它在 char 类型能表示的范围内,所以将其赋值给 grade 没问题。由于 65 是字母 a 对应的 ascii 码,因此本例是把 a 赋给 grade。注意,能这样做的前提是系统使用 ascii 码。其实,用 'a'
代替 65 才是较为妥当的做法,这样在任何系统中都不会出问题。因此,最好使用字符常量,而不是数字代码值。
奇怪的是,c 语言将字符常量视为 int 类型而非 char 类型。例如,在 int 为 32 位、char 为 8 位的 ascii 系统中,有下面的代码:
char grade = 'b';
本来 'b'
对应的数值 66 存储在 32 位的存储单元中,现在却可以存储在 8 位的存储单元中(grade)。利用字符常量的这种特性,可以定义一个字符常量 'fate'
,即把 4 个独立的 8 位 ascii 码存储在一个 32 位存储单元中。如果把这样的字符常量赋给 char 类型变量 grade,只有最后 8 位有效。因此,grade 的值是 'e'
。
3.3 非打印字符
单引号只适用于字符、数字和标点符号,浏览 ascii 表会发现,有些 ascii 字符打印不出来。例如,一些代表行为的字符(如,退格、换行、终端响铃或蜂鸣)。c 语言提供了 3 种方法表示这些字符。
第 1 种方法前面介绍过——使用 ascii 码。例如,蜂鸣字符的 ascii 值是 7,因此可以这样写:
char beep = 7;
第 2 种方法是,使用特殊的符号序列表示一些特殊的字符。这些符号序列叫作转义序列(escape sequence)。表 2 列出了转义序列及其含义。
把转义序列赋给字符变量时,必须用单引号把转义序列括起来。例如,假设有下面一行代码:
char nerf = '\n';
稍后打印变量 nerf 的效果是,在打印机或屏幕上另起一行。
表 2 转义序列
转义序列 | 含义 |
---|---|
\a
|
警报(ansi c) |
\b
|
退格 |
\f
|
换页 |
\n
|
换行 |
\r
|
回车 |
\t
|
水平制表符 |
\v
|
垂直制表符 |
\\
|
反斜杠(\) |
\'
|
单引号 |
\"
|
双引号 |
\?
|
问号 |
\0oo
|
八进制值(oo 必须是有效的八进制数,即每个o 可表示0 ~7 中的一个数)
|
\xhh
|
十六进制值(hh 必须是有效的十六进制数,即每个h 可表示0 ~f 中的一个数)
|
现在,我们来仔细分析一下转义序列。使用 c90 新增的警报字符(\a)是否能产生听到或看到的警报,取决于计算机的硬件,蜂鸣是最常见的警报(在一些系统中,警报字符不起作用)。c 标准规定警报字符不得改变活跃位置。标准中的活跃位置(active position)指的是显示设备(屏幕、电传打字机、打印机等)中下一个字符将出现的位置。简而言之,平时常说的屏幕光标位置就是活跃位置。在程序中把警报字符输出在屏幕上的效果是,发出一声蜂鸣,但不会移动屏幕光标。
接下来的转义字符 \b
、\f
、\n
、\r
、\t
和 \v
是常用的输出设备控制字符。了解它们最好的方式是查看它们对活跃位置的影响。换页符(\f)把活跃位置移至下一页的开始处;换行符(\n)把活跃位置移至下一行的开始处;回车符(\r)把活跃位置移动到当前行的开始处;水平制表符(\t)将活跃位置移至下一个水平制表点(通常是第 1 个、第 9 个、第 17 个、第 25 个等字符位置);垂直制表符(\v)把活跃位置移至下一个垂直制表点。
这些转义序列字符不一定在所有的显示设备上都起作用。例如,换页符和垂直制表符在 pc 屏幕上会生成奇怪的符号,光标并不会移动。只有将其输出到打印机上时才会产生前面描述的效果。
接下来的 3 个转义序列(\\
、\'
、\"
)用于打印 \
、'
、"
字符(由于这些字符用于定义字符常量,是 printf() 函数的一部分,若直接使用它们会造成混乱)。如果打印下面一行内容:
gramps sez, "a \ is a backslash."
应这样编写代码:
printf("gramps sez, \"a \\ is a backslash.\"\n");
表 2 中的最后两个转义序列(\0oo 和 \xhh)是 ascii 码的特殊表示。如果要用八进制 ascii 码表示一个字符,可以在编码值前面加一个反斜杠(\)并用单引号括起来。例如,如果编译器不识别警报字符(\a),可以使用 ascii 码来代替:
beep = '\007';
可以省略前面的 0,'\07'
甚至 '\7'
都可以。即使没有前缀 0,编译器在处理这种写法时,仍会解释为八进制。
从 c90 开始,不仅可以用十进制、八进制形式表示字符常量,c 语言还提供了第 3 种选择——用十六进制形式表示字符常量,即反斜杠后面跟一个 x 或 x,再加上 1~3 位十六进制数字。例如,ctrl+p 字符的 ascii 十六进制码是 10(相当于十进制的 16),可表示为 '\x10'
或 '\x010'
。图 5 列出了一些整数类型的不同进制形式。
使用 ascii 码时,注意数字和数字字符的区别。例如,字符 4 对应的 ascii 码是 52。'4'
表示字符 4,而不是数值 4。
关于转义序列,读者可能有下面 3 个问题。
- 上面最后一个例子(
printf("gramps sez, \"a \\ is a backslash.\"\n")
),为何没有用单引号把转义序列括起来?无论是普通字符还是转义序列,只要是双引号括起来的字符集合,就无需用单引号括起来。双引号中的字符集合叫作字符串。注意,该例中的其他字符(g、r、a、m、p、s 等)都没有用单引号括起来。与此类似,printf("hello!\007\n");
将打印 hello! 并发出一声蜂鸣,而printf("hello!7\n");
则打印 hello!7。不是转义序列中的数字将作为普通字符被打印出来。 - 何时使用 ascii 码?何时使用转义序列?如果要在转义序列(假设使用 '\f')和 ascii 码('\014')之间选择,请选择前者(即 '\f')。这样的写法不仅更好记,而且可移植性更高。
'\f'
在不使用 ascii 码的系统中,仍然有效。 - 如果要使用 ascii 码,为何要写成
'\032'
而不是 032?首先,'\032'
能更清晰地表达程序员使用字符编码的意图。其次,类似\032
这样的转义序列可以嵌入 c 的字符串中,如printf("hello!\007\n");
中就嵌入了\007
。
3.4 打印字符
printf() 函数用 %c
指明待打印的字符。前面介绍过,一个字符变量实际上被存储为 1 字节的整数值。因此,如果用 %d
转换说明打印 char 类型变量的值,打印的是一个整数。而 %c
转换说明告诉 printf() 打印该整数值对应的字符。程序清单 5 演示了打印 char 类型变量的两种方式。
程序清单 5 charcode.c 程序
/* charcode.c-显示字符的代码编号 */ #include <stdio.h> int main(void) { char ch; printf("please enter a character.\n"); scanf("%c", &ch); /* 用户输入字符 */ printf("the code for %c is %d.\n", ch, ch); return 0; }
运行该程序后,输出示例如下:
please enter a character. c the code for c is 67.
运行该程序时,在输入字母后不要忘记按下 enter 或 return 键。随后,scanf() 函数会读取用户输入的字符,& 符号表示把输入的字符赋给变量 ch。接着,printf() 函数打印 ch 的值两次,第 1 次打印一个字符(对应代码中的 %c
),第 2 次打印一个十进制整数值(对应代码中的 %d)。注意,printf() 函数中的转换说明决定了数据的显示方式,而不是数据的存储方式(见图 6)。
3.5 有符号还是无符号
有些 c 编译器把 char 实现为有符号类型,这意味着 char 可表示的范围是 -128~127。而有些 c 编译器把 char 实现为无符号类型,那么 char 可表示的范围是 0~255。请查阅相应的编译器手册,确定正在使用的编译器如何实现 char 类型。或者,可以查阅 limits.h 头文件。
根据 c90 标准,c 语言允许在关键字 char 前面使用 signed 或 unsigned。这样,无论编译器默认 char 是什么类型,signed char 表示有符号类型,而 unsigned char 表示无符号类型。这在用 char 类型处理小整数时很有用。如果只用 char 处理字符,那么 char 前面无需使用任何修饰符。
四、_bool 类型
c99 标准添加了 _bool 类型,用于表示布尔值,即逻辑值 true 和 false。因为 c 语言用值 1 表示 true,值 0 表示 false,所以 _bool 类型实际上也是一种整数类型。但原则上它仅占用 1 位存储空间,因为对 0 和 1 而言,1 位的存储空间足够了。
程序通过布尔值可选择执行哪部分代码。
五、可移植类型:stdint.h 和 inttypes.h
c 语言提供了许多有用的整数类型。但是,某些类型名在不同系统中的功能不一样。c99 新增了两个头文件 stdint.h 和 inttypes.h,以确保 c 语言的类型在各系统中的功能相同。
c 语言为现有类型创建了更多类型名。这些新的类型名定义在 stdint.h 头文件中。例如,int32_t 表示 32 位的有符号整数类型。在使用 32 位 int 的系统中,头文件会把 int32_t 作为 int 的别名。不同的系统也可以定义相同的类型名。例如,int 为 16 位、long 为 32 位的系统会把 int32_t 作为 long 的别名。然后,使用 int32_t 类型编写程序,并包含 stdint.h 头文件时,编译器会把 int 或 long 替换成与当前系统匹配的类型。
上面讨论的类型别名是精确宽度整数类型(exact-width integer type)的示例。int32_t 表示整数类型的宽度正好是 32 位。但是,计算机的底层系统可能不支持。因此,精确宽度整数类型是可选项。
如果系统不支持精确宽度整数类型怎么办?c99 和 c11 提供了第 2 类别名集合。一些类型名保证所表示的类型一定是至少有指定宽度的最小整数类型。这组类型集合被称为最小宽度类型(minimum width type)。例如,int_least8_t 是可容纳 8 位有符号整数值的类型中宽度最小的类型的一个别名。如果某系统的最小整数类型是 16 位,可能不会定义 int8_t 类型。尽管如此,该系统仍可使用 int_least8_t 类型,但可能把该类型实现为 16 位的整数类型。
当然,一些程序员更关心速度而非空间。为此,c99 和 c11 定义了一组可使计算达到最快的类型集合。这组类型集合被称为最快最小宽度类型(fastst minimum width type)。例如,int_fast8_t 被定义为系统中对 8 位有符号值而言运算最快的整数类型的别名。
另外,有些程序员需要系统的最大整数类型。为此,c99 定义了最大的有符号整数类型 intmax_t,可存储任何有效的有符号整数值。类似地,uintmax_t 表示最大的无符号整数类型。顺带一提,这些类型有可能比 long long 和 unsigned long 类型更大,因为 c 编译器除了实现标准规定的类型以外,还可利用 c 语言实现其他类型。例如,一些编译器在标准引入 long long 类型之前,已提前实现了该类型。
c99 和 c11 不仅提供可移植的类型名,还提供相应的输入和输出。例如,printf() 打印特定类型时要求与相应的转换说明匹配。如果要打印 int32_t 类型的值,有些定义使用 %d
,而有些定义使用 %ld
,怎么办?c 标准针对这一情况,提供了一些字符串宏来显示可移植类型。例如,inttypes.h 头文件中定义了 prid32 字符串宏,代表打印 32 位有符号值的合适转换说明(如 d 或 l)。程序清单 6 演示了一种可移植类型和相应转换说明的用法。
程序清单 6 altnames.c 程序
/* altnames.c -- 可移植整数类型名 */ #include <stdio.h> #include <inttypes.h> // 支持可移植类型 int main(void) { int32_t me32; // me32是一个32位有符号整型变量 me32 = 45933945; printf("first, assume int32_t is int: "); printf("me32 = %d\n", me32); printf("next, let's not make any assumptions.\n"); printf("instead, use a \"macro\" from inttypes.h: "); printf("me32 = %" prid32 "\n", me32); return 0; }
该程序最后一个 printf() 中,参数 prid32 被定义在 inttypes.h 中的 "d" 替换,因而这条语句等价于:
printf("me32 = %" "d" "\n", me32);
在 c 语言中,可以把多个连续的字符串组合成一个字符串,所以这条语句又等价于:
printf("me32 = %d\n", me32);
下面是该程序的输出,注意,程序中使用了 \"
转义序列来显示双引号:
first, assume int32_t is int: me32 = 45933945 next, let's not make any assumptions. instead, use a "macro" from inttypes.h: me32 = 45933945
篇幅有限,无法介绍扩展的所有整数类型。本节主要是为了让读者知道,在需要时可进行这种级别的类型控制。
注意 对 c99/c11 的支持
c 语言发展至今,虽然 iso 已发布了 c11 标准,但是编译器供应商对 c99 的实现程度却各不相同。一些编译器仍未实现 inttypes.h 头文件及其相关功能。
六、float、double 和 long double
各种整数类型对大多数软件开发项目而言够用了。然而,面向金融和数学的程序经常使用浮点数。c 语言中的浮点类型有 float、double 和 long double 类型。它们与 fortran 和 pascal 中的 real 类型一致。前面提到过,浮点类型能表示包括小数在内更大范围的数。浮点数的表示类似于科学记数法(即用小数乘以 10 的幂来表示数字)。该记数系统常用于表示非常大或非常小的数。表 3 列出了一些示例。
表 3 记数法示例
数字 | 科学记数法 | 指数记数法 |
---|---|---|
1000000000
|
1.0 ×109
|
1.0e9
|
123000
|
1.23 ×105
|
1.23e5
|
322.56
|
3.2256 ×102
|
3.2256e2
|
0.000056
|
5.6 ×10-5
|
5.6e-5
|
第 1 列是一般记数法;第 2 列是科学记数法;第 3 列是指数记数法(或称为 e 记数法),这是科学记数法在计算机中的写法,e 后面的数字代表 10 的指数。图 7 演示了更多的浮点数写法。
c 标准规定,float 类型必须至少能表示 6 位有效数字,且取值范围至少是 10-37~10+37。前一项规定指 float 类型必须能够表示 33.333333 的前 6 位数字,而不是精确到小数点后 6 位数字。后一项规定用于方便地表示诸如太阳质量(2.0e30 千克)、一个质子的电荷量(1.6e-19 库仑)或国家债务之类的数字。通常,系统存储一个浮点数要占用 32 位。其中 8 位用于表示指数的值和符号,剩下 24 位用于表示非指数部分(也叫作尾数或有效数)及其符号。
c 语言提供的另一种浮点类型是 double(意为双精度)。double 类型和 float 类型的最小取值范围相同,但至少必须能表示 10 位有效数字。一般情况下,double 占用 64 位而不是 32 位。一些系统将多出的 32 位全部用来表示非指数部分,这不仅增加了有效数字的位数(即提高了精度),而且还减少了舍入误差。另一些系统把其中的一些位分配给指数部分,以容纳更大的指数,从而增加了可表示数的范围。无论哪种方法,double 类型的值至少有 13 位有效数字,超过了标准的最低位数规定。
c 语言的第3种浮点类型是 long double,以满足比 double 类型更高的精度要求。不过,c 只保证 long double 类型至少与 double 类型的精度相同。
6.1 声明浮点型变量
浮点型变量的声明和初始化方式与整型变量相同,下面是一些例子:
float noah, jonah; double trouble; float planck = 6.63e-34; long double gnp;
6.2 浮点型常量
在代码中,可以用多种形式书写浮点型常量。浮点型常量的基本形式是:有符号的数字(包括小数点),后面紧跟 e 或 e,最后是一个有符号数表示 10 的指数。下面是两个有效的浮点型常量:
-1.56e+12 2.87e-3
正号可以省略。可以没有小数点(如,2e5)或指数部分(如,19.28),但是不能同时省略两者。可以省略小数部分(如,3.e16)或整数部分(如,.45e-6),但是不能同时省略两者。下面是更多的有效浮点型常量示例:
3.14159 .2 4e16 .8e-5 100.
不要在浮点型常量中间加空格:1.56 e+12(错误!)
默认情况下,编译器假定浮点型常量是 double 类型的精度。例如,假设 some 是 float 类型的变量,编写下面的语句:
some = 4.0 * 2.0;
通常,4.0 和 2.0 被存储为 64 位的 double 类型,使用双精度进行乘法运算,然后将乘积截断成 float 类型的宽度。这样做虽然计算精度更高,但是会减慢程序的运行速度。
在浮点数后面加上 f 或 f 后缀可覆盖默认设置,编译器会将浮点型常量看作 float 类型,如 2.3f 和 9.11e9f。使用 l 或 l 后缀使得数字成为 long double 类型,如 54.3l和4.32l。注意,建议使用 l 后缀,因为字母 l 和数字 1 很容易混淆。没有后缀的浮点型常量是 double 类型。
c99 标准添加了一种新的浮点型常量格式——用十六进制表示浮点型常量,即在十六进制数前加上十六进制前缀(0x 或 0x),用 p 和 p 分别代替 e 和 e,用 2 的幂代替 10 的幂(即,p 计数法)。如下所示:
0xa.1fp10
十六进制 a 等于十进制 10,.1f 是 1/16 加上 15/256(十六进制 f 等于十进制 15),p10 是 210 或 1024。0xa.1fp10 表示的值是 (10 + 1/16 + 15/256)×1024(即,十进制 10364.0)。
注意,并非所有的编译器都支持 c99 的这一特性。
6.3 打印浮点值
printf() 函数使用 %f
转换说明打印十进制记数法的 float 和 double 类型浮点数,用 %e
打印指数记数法的浮点数。如果系统支持十六进制格式的浮点数,可用 a 和 a 分别代替 e 和 e。打印 long double 类型要使用 %lf
、%le
或 %la
转换说明。给那些未在函数原型中显式说明参数类型的函数(如,printf())传递参数时,c 编译器会把 float 类型的值自动转换成 double 类型。程序清单 7 演示了这些特性。
程序清单 7 showf_pt.c 程序
/* showf_pt.c -- 以两种方式显示float类型的值 */ #include <stdio.h> int main(void) { float aboat = 32000.0; double abet = 2.14e9; long double dip = 5.32e-5; printf("%f can be written %e\n", aboat, aboat); // 下一行要求编译器支持c99或其中的相关特性 printf("and it's %a in hexadecimal, powers of 2 notation\n", aboat); printf("%f can be written %e\n", abet, abet); printf("%lf can be written %le\n", dip, dip); return 0; }
该程序的输出如下,前提是编译器支持 c99/c11:
32000.000000 can be written 3.200000e+04 and it's 0x1.f4p+14 in hexadecimal, powers of 2 notation 2140000000.000000 can be written 2.140000e+09 0.000053 can be written 5.320000e-05
该程序示例演示了默认的输出效果。
6.4 浮点值的上溢和下溢
假设系统的最大 float 类型值是 3.4e38,编写如下代码:
float toobig = 3.4e38 * 100.0f; printf("%e\n", toobig);
会发生什么?这是一个上溢(overflow)的示例。当计算导致数字过大,超过当前类型能表达的范围时,就会发生上溢。这种行为在过去是未定义的,不过现在 c 语言规定,在这种情况下会给 toobig 赋一个表示无穷大的特定值,而且 printf() 显示该值为 inf 或 infinity(或者具有无穷含义的其他内容)。
当对一个很小的数做除法时,情况更为复杂。回忆一下,float 类型的数以指数和尾数部分来存储。存在这样一个数,它的指数部分是最小值,即由全部可用位表示的最小尾数值。该数字是 float 类型能用全部精度表示的最小数字。现在把它除以 2。通常,这个操作会减小指数部分,但是假设的情况中,指数已经是最小值了。所以计算机只好把尾数部分的位向右移,空出第 1 个二进制位,并丢弃最后一个二进制数。以十进制为例,把一个有 4 位有效数字的数(如,0.1234e-10)除以 10,得到的结果是 0.0123e-10。虽然得到了结果,但是在计算过程中却损失了原末尾有效位上的数字。这种情况叫作下溢(underflow)。c 语言把损失了类型全精度的浮点值称为低于正常的(subnormal)浮点值。因此,把最小的正浮点数除以 2 将得到一个低于正常的值。如果除以一个非常大的值,会导致所有的位都为 0。现在,c 库已提供了用于检查计算是否会产生低于正常值的函数。
还有另一个特殊的浮点值 nan(not a number 的缩写)。例如,给 asin() 函数传递一个值,该函数将返回一个角度,该角度的正弦就是传入函数的值。但是正弦值不能大于 1,因此,如果传入的参数大于 1,该函数的行为是未定义的。在这种情况下,该函数将返回 nan 值,printf() 函数可将其显示为 nan、nan 或其他类似的内容。
浮点数舍入错误
给定一个数,加上 1,再减去原来给定的数,结果是多少?你一定认为是 1。但是,下面的浮点运算给出了不同的答案:
/* floaterr.c--演示舍入错误 */ #include <stdio.h> int main(void) { float a,b; b = 2.0e20 + 1.0; a = b - 2.0e20; printf("%f \n", a); return 0; }该程序的输出如下:
0.000000 ←linux系统下的老式gcc -13584010575872.000000 ←turbo c 1.5 4008175468544.000000 ←xcode 4.5、visual studio 2012、当前版本的gcc得出这些奇怪答案的原因是,计算机缺少足够的小数位来完成正确的运算。2.0e20 是 2 后面有 20 个 0。如果把该数加 1,那么发生变化的是第 21 位。要正确运算,程序至少要存储 21 位数字。而 float 类型的数字通常只能存储按指数比例缩小或放大的 6 或 7 位有效数字。在这种情况下,计算结果一定是错误的。另一方面,如果把 2.0e20 改成 2.0e4,计算结果就没问题。因为 2.0e4 加 1 只需改变第 5 位上的数字,float 类型的精度足够进行这样的计算。
浮点数表示法
上一个方框中列出了由于计算机使用的系统不同,一个程序有不同的输出。原因是,根据前面介绍的知识,实现浮点数表示法的方法有多种。为了尽可能地统一实现,电子和电气工程师协会(ieee)为浮点数计算和表示法开发了一套标准。现在,许多硬件浮点单元都采用该标准。2011 年,该标准被 iso/iec/ieee 60559:2011 标准收录。该标准作为 c99 和 c11 的可选项,符合硬件要求的平台可开启。floaterr.c 程序的第 3 个输出示例即是支持该浮点标准的系统显示的结果。
七、复数和虚数类型
许多科学和工程计算都要用到复数和虚数。c99 标准支持复数类型和虚数类型,但是有所保留。一些独立实现,如嵌入式处理器的实现,就不需要使用复数和虚数(vcr 芯片就不需要复数)。一般而言,虚数类型都是可选项。c11 标准把整个复数软件包都作为可选项。
简而言之,c 语言有 3 种复数类型:float _complex、double _complex 和 long double _complex。例如,float _complex 类型的变量应包含两个 float 类型的值,分别表示复数的实部和虚部。类似地,c 语言的3种虚数类型是 float _imaginary、double _imaginary 和 long double _imaginary。
如果包含 complex.h 头文件,便可用 complex 代替 _complex,用 imaginary 代替 _imaginary,还可以用 i 代替 -1 的平方根。
为何 c 标准不直接用 complex 作为关键字来代替 _complex,而要添加一个头文件(该头文件中把 complex 定义为 _complex)?因为标准委员会考虑到,如果使用新的关键字,会导致以该关键字作为标识符的现有代码全部失效。例如,之前的 c99,许多程序员已经使用 struct complex 定义一个结构来表示复数或者心理学程序中的心理状况。让 complex 成为关键字会导致之前的这些代码出现语法错误。但是,使用 struct _complex 的人很少,特别是标准使用首字母是下划线的标识符作为预留字以后。因此,标准委员会选定 _complex 作为关键字,在不用考虑名称冲突的情况下可选择使用 complex。
八、其他类型
现在已经介绍完 c 语言的所有基本数据类型。有些人认为这些类型实在太多了,但有些人觉得还不够用。注意,虽然 c 语言没有字符串类型,但也能很好地处理字符串。
c 语言还有一些从基本类型衍生的其他类型,包括数组、指针、结构和联合。尽管后面会详细介绍这些类型,但是本文的程序示例中已经用到了指针〔指针(pointer)指向变量或其他数据对象位置〕。例如,在 scanf() 函数中用到的前缀 &,便创建了一个指针,告诉 scanf() 把数据放在何处。
九、类型大小
如何知道当前系统的指定类型的大小是多少?运行程序清单 8,会列出当前系统的各类型的大小。
程序清单 8 typesize.c 程序
/* typesize.c -- 打印类型大小 */ #include <stdio.h> int main(void) { /* c99为类型大小提供%zd转换说明 */ printf("type int has a size of %zd bytes.\n", sizeof(int)); printf("type char has a size of %zd bytes.\n", sizeof(char)); printf("type long has a size of %zd bytes.\n", sizeof(long)); printf("type long long has a size of %zd bytes.\n", sizeof(long long)); printf("type double has a size of %zd bytes.\n", sizeof(double)); printf("type long double has a size of %zd bytes.\n", sizeof(long double)); return 0; }
sizeof 是 c 语言的内置运算符,以字节为单位给出指定类型的大小。c99 和 c11 提供 %zd
转换说明匹配 sizeof 的返回类型。一些不支持 c99 和 c11 的编译器可用 %u
或 %lu
代替 %zd
。
该程序的输出如下:
type int has a size of 4 bytes. type char has a size of 1 bytes. type long has a size of 8 bytes. type long long has a size of 8 bytes. type double has a size of 8 bytes. type long double has a size of 16 bytes.
该程序列出了 6 种类型的大小,你也可以把程序中的类型更换成感兴趣的其他类型。注意,因为 c 语言定义了 char 类型是 1 字节,所以 char 类型的大小一定是 1 字节。而在 char 类型为 16 位、double 类型为 64 位的系统中,sizeof 给出的 double 是4字节。在 limits.h 和 float.h 头文件中有类型限制的相关信息。
顺带一提,注意该程序最后几行 printf() 语句都被分为两行,只要不在引号内部或一个单词中间断行,就可以这样写。
十、小结:基本数据类型
关键字:
基本数据类型由 11 个关键字组成:int、long、short、unsigned、char、float、double、signed、_bool、_complex 和 _imaginary。
有符号整型:
有符号整型可用于表示正整数和负整数。
- int ——系统给定的基本整数类型。c 语言规定 int 类型不小于 16 位。
- short 或 short int ——最大的 short 类型整数小于或等于最大的 int 类型整数。c 语言规定 short 类型至少占 16 位。
- long 或 long int ——该类型可表示的整数大于或等于最大的 int 类型整数。c 语言规定 long 类型至少占 32 位。
- long long 或 long long int ——该类型可表示的整数大于或等于最大的 long 类型整数。long long 类型至少占 64 位。
一般而言,long 类型占用的内存比 short 类型大,int 类型的宽度要么和 long 类型相同,要么和 short 类型相同。例如,旧 dos 系统的 pc 提供 16 位的 short 和 int,以及 32 位的 long;windows 95 系统提供 16 位的 short 以及 32 位的 int 和 long。
无符号整型:
无符号整型只能用于表示零和正整数,因此无符号整型可表示的正整数比有符号整型的大。在整型类型前加上关键字 unsigned 表明该类型是无符号整型:unsigned int、unsigned long、unsigned short。单独的 unsigned 相当于 unsigned int。
字符类型:
可打印出来的符号(如 a、& 和 +)都是字符。根据定义,char 类型表示一个字符要占用 1 字节内存。出于历史原因,1 字节通常是 8 位,但是如果要表示基本字符集,也可以是 16 位或更大。
- char ——字符类型的关键字。有些编译器使用有符号的 char,而有些则使用无符号的 char。在需要时,可在 char 前面加上关键字 signed 或 unsigned 来指明具体使用哪一种类型。
布尔类型:
布尔值表示 true 和 false。c 语言用 1 表示 true,0 表示 false。
- _bool ——布尔类型的关键字。布尔类型是无符号 int 类型,所占用的空间只要能存储 0 或 1 即可。
实浮点类型:
实浮点类型可表示正浮点数和负浮点数。
- float ——系统的基本浮点类型,可精确表示至少 6 位有效数字。
- double ——存储浮点数的范围(可能)更大,能表示比 float 类型更多的有效数字(至少 10 位,通常会更多)和更大的指数。
- long double ——存储浮点数的范围(可能)比 double 更大,能表示比 double 更多的有效数字和更大的指数。
复数和虚数浮点数:
虚数类型是可选的类型。复数的实部和虚部类型都基于实浮点类型来构成:
- float _complex
- double _complex
- long double _complex
- float _imaginary
- double _imaginary
- long double _imaginary
十一、小结:如何声明简单变量
1.选择需要的类型。
2.使用有效的字符给变量起一个变量名。
3.按以下格式进行声明:
类型说明符 变量名;
类型说明符由一个或多个关键字组成。下面是一些示例:
int erest; unsigned short cash;
4.可以同时声明相同类型的多个变量,用逗号分隔各变量名,如下所示:
char ch, init, ans;
5.在声明的同时还可以初始化变量:
float mass = 6.0e24;
原文:c 语言中的基本数据类型
(完)
上一篇: 浅谈Shell 流程控制