可移植类型:stdint.h和inttypes.h
C99 新增了两个头文件stdint.h和inttypes.h, 以确保C语言的类型在各系统中的功能相同语言为现有类型创建了更多类型名。 这些新的类型名定义在stdint.h头文件中。 例如, int32_t表示32位的有符号整数类型。 在使用32位int的系统中, 头文件会把int32_t作为int的别名。
不同的系统也可以定义相同的类型名。
C99和C11提供了第2类别名集合。 一些类型名保证所表示的类型一定是至少有指定宽度的最小整数类型。 这组类型集合被称为最小宽度类型(minimum width type) 。例如,int_least8_t是可容纳8位有符号整数值的类型中宽度最小的类型的一个别名。 如果某系统的最小整数类型是16位, 可能不会定义int8_t类型。
C99和C11定义了一组可使计算达到最快的类型集合。 这组类型集合被称为最快最小宽度类型(fastst minimum width type) 。
C99定义了最大的有符号整数类型intmax_t, 可储存任何有效的有符号整数值。 类似地,unitmax_t表示最大的无符号整数类型。
C编译器除了实现标准规定的类型以外,还可利用C语言实现其他类型。
C99 和 C11 不仅提供可移植的类型名, 还提供相应的输入和输出。
有些定义使用%d, 而有些定义使用%ld, 怎么办? C 标准针对这一情况, 提供了一些字符串宏 来显示可移植类型。 例如, inttypes.h头文件中定义了PRId32字符串宏, 代表打印32位有符号值的合适转换说明(如d或l) 。
该程序最后一个printf()中, 参数PRId32被定义在inttypes.h中的"d"替换,因而这条语句等价于:
printf("me16 = %" "d" "\n", me16);
在C语言中, 可以把多个连续的字符串组合成一个字符串, 所以这条语句又等价于:
printf("me16 = %d\n", me16);
程序中使用了\"转义序列来显示双引号:
float,double和long double
C语言中的浮点类型有float、 double和long double类型。 它们与FORTRAN和Pascal中的real类型一致浮点类型能表示包括小数在内更大范围的数。 浮点数的表示类似于科学记数法(即用小数乘以10的幂来表示数字) 。 该记数系统常用于表示非常大或非常小的数。
第1列是一般记数法; 第2列是科学记数法; 第3列是指数记数法(或称为e记数法) , 这是科学记数法在计算机中的写法, e后面的数字代表10的指数。
C标准规定, float类型必须至少能表示6位有效数字, 且取值范围至少是10-37~10+37。 前一项规定指float类型必须至少精确表示小数点后的6位有效数字, 如33.333333。 后一项规定用于方便地表示诸如太阳质量(2.0e30千克) 、 一个质子的电荷量(1.6e-19库仑) 或国家债务之类的数字。 通常,系统储存一个浮点数要占用32位。 其中8位用于表示指数的值和符号, 剩下24位用于表示非指数部分(也叫作尾数或有效数) 及其符号
C语言提供的另一种浮点类型是double(意为双精度) 。 double类型和float类型的最小取值范围相同, 但至少必须能表示10位有效数字。
C语言的第3种浮点类型是long double, 以满足比double类型更高的精度要求。 不过, C只保证long double类型至少与double类型的精度相同。
声明浮点型变量
浮点型变量的声明和初始化方式与整型变量相同
float noah, jonah;
double trouble;
float planck = 6.63e-34;
long double gnp;
浮点型常量
在代码中, 可以用多种形式书写浮点型常量。 浮点型常量的基本形式是: 有符号的数字(包括小数点) , 后面紧跟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类型。建议使用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) 。
打印浮点值
printf()函数使用%f转换说明打印十进制记数法的float和double类型浮点数, 用%e打印指数记数法的浮点数。 如果系统支持十六进制格式的浮点数, 可用a和A分别代替e和E。 打印long double类型要使用%Lf、 %Le或%La转换说明。
那些未在函数原型中显式说明参数类型的函数(如, printf())传递参数时, C编译器会把float类型的值自动转换成double类型。
浮点值的上溢和下溢
假设系统的最大float类型值是3.4E38, 编写如下代码:
float toobig = 3.4E38 * 100.0f;
printf("%e\n", toobig);
当计算导致数字过大, 超过当前类型能表达的范围时, 就会发生上溢。
在这种情况下会给toobig赋一个表示无穷大的特定值, 而且printf()显示该值为inf或infinity(或者具有无穷含义的其他内容)
float类型的数以指数和尾数部分来储存。 存在这样一个数, 它的指数部分是最小值, 即由全部可用位表示的最小尾数值。
该数字是float类型能用全部精度表示的最小数字。 现在把它除以 2。 通常, 这个操作会减小指数部分, 但是假设的情况中, 指数已经是最小值了。 所以计算机只好把尾数部分的位向右移, 空出第1 个二进制位, 并丢弃最后一个二进制数。
C语言把损失了类型全精度的浮点值称为低于正常的(subnormal) 浮点值。 因此, 把最小的正浮点数除以 2将得到一个低于正常的值。 如果除以一个非常大的值, 会导致所有的位都为0。 现在, C库已提供了用于检查计算是否会产生低于正常值的函数。
还有另一个特殊的浮点值NaN(not a number的缩写) 。 例如, 给asin()函数传递一个值, 该函数将返回一个角度, 该角度的正弦就是传入函数的值。 但是正弦值不能大于1, 因此, 如果传入的参数大于1, 该函数的行为是未定义的。 在这种情况下, 该函数将返回NaN值, printf()函数可将其显示为nan、 NaN或其他类似的内容。
浮点数舍入错误
得出这些奇怪答案的原因是, 计算机缺少足够的小数位来完成正确的运算。 2.0e20是 2后面有20个0。 如果把该数加1, 那么发生变化的是第21位。要正确运算, 程序至少要储存21位数字。 而float类型的数字通常只能储存按指数比例缩小或放大的6或7位有效数字。计算结果一定是错误的。 另一方面, 如果把2.0e20改成2.0e4, 计算结果就没问题。 因为2.0e4加1只需改变第5位上的数字, float类型的精度足够进行这样的计算。
浮点数表示法
由于计算机使用的系统不同, 一个程序有不同的输出。 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)。如果使用新的关键字, 会导致以该关键字作为标识符的现有代码全部失效。
complex成为关键字会导致之前的这些代码出现语法错误。
其他类型
C语言还有一些从基本类型衍生的其他类型, 包括数组、 指针、 结构和联合。
小结: 基本数据类型
基本数据类型由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类型相同。
无符号整型:
无符号整型只能用于表示零和正整数, 因此无符号整型可表示的正整数比有符号整型的大。 在整型类型前加上关键字unsigned表明该类型是无符号整型: unsignedint、 unsigned long、 unsigned short。 单独的unsigned相当于unsignedint。
字符类型:
可打印出来的符号(如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 long ——储存浮点数的范围(可能) 比double更大, 能表示比double更多的有效数字和更大的指数。
复数和虚数浮点数:
虚数类型是可选的类型。 复数的实部和虚部类型都基于实浮点类型来构成:
float _Complex
double _Complex
long double _Complex
float _Imaginary
double _Imaginary
long long _Imaginary
如何声明简单变量
1.选择需要的类型。
2.使用有效的字符给变量起一个变量名。
3.按以下格式进行声明:
类型说明符 变量名;
类型说明符由一个或多个关键字组成。 下面是一些示例:
int erest;
unsigned short cash;
可以同时声明相同类型的多个变量, 用逗号分隔各变量名, 如下所示:
char ch, init, ans;
5.在声明的同时还可以初始化变量:
float mass = 6.0E24;
类型大小
sizeof是C语言的内置运算符, 以字节为单位给出指定类型的大小。 C99和C11提供%zd转换说明匹配sizeof的返回类型[2]。 一些不支持C99和C11的编译器可用%u或%lu代替%zd。
该程序列出了6种类型的大小,C语言定义了char类型是1字节, 所以char类型的大小一定是1字节。 而在char类型为16位、 double类型为64位的系统中, sizeof给出的double是4字节。
使用数据类型
编写程序时, 应注意合理选择所需的变量及其类型。 通常, 用int或float类型表示数字, char类型表示字符。在使用变量之前必须先声明, 并选择有意义的变量名。 初始化变量应使用与变量类型匹配的常数类型。
int apples = 3; /* 正确 */
int oranges = 3.0; /* 不好的形式 */
C在检查类型匹配方面不太严格。C编译器甚至允许二次初始化, 但在激活了较高级别警告时, 会给出警告。
把一个类型的数值初始化给不同类型的变量时, 编译器会把值转换成与变量匹配的类型, 这将导致部分数据丢失。
int cost = 12.99; /* 用double类型的值初始化int类型的变量 */
float pi = 3.1415926536; /* 用double类型的值初始化float类型的变量 */
第1个声明, cost的值是12。 C编译器把浮点数转换成整数时, 会直接丢弃(截断) 小数部分, 而不进行四舍五入。 第2个声明会损失一些精度, 因为C只保证了float类型前6位的精度。 编译器对这样的初始化可能给出警告。
参数和陷阱
传递给函数的信息被称为参数。 例如, printf("Hello, pal.")函数调用有一个参数: "Hello,pal."。 双引号中的字符序列(如, "Hello,pal.") 被称为字符串(string)无论双引号中包含多少个字符和标点符号, 一个字符串就是一个参数
scanf("%d", &weight)函数调用有两个参数: "%d"和&weight。 C语言用逗号分隔函数中的参数。 printf()和scanf()函数与一般函数不同, 它们的参数个数是可变的。printf()和scanf()函数用第1个参数表明后续有多少个参数, 即第1个字符串中的转换说明与后面的参数一一对应。
printf("%d cats ate %d cans of tuna\n", cats, cans);
C 语言通过函数原型机制检查函数调用时参数的个数和类型是否正确。 但是, 该机制对printf()和scanf()不起作用, 因为这两个函数的参数个数可变。
用%d显示float类型的值, 其值不会被转换成int类型。
转义序列示例
程序运行情况
第1条printf()语句(注释中标为1) 发出一声警报(因为使用了\a) , 然后打印下面的内容:
Enter your desired monthly salary:
因为printf()中的字符串末尾没有\n, 所以光标停留在冒号后面。
第2条printf()语句在光标处接着打印, 屏幕上显示的内容是:
Enter your desired monthly salary: $_______
冒号和美元符号之间有一个空格, 这是因为第2条printf()语句中的字符串以一个空格开始。 7个退格字符使得光标左移7个位置, 即把光标移至7个下划线字符的前面, 紧跟在美元符号后面。
假设键入的数据是4000.00(并按下Enter键) , 屏幕显示的内容应该是
第3条printf()语句中的字符串以\n\t开始。 换行字符使光标移至下一行起始处。 水平制表符使光标移至该行的下一个制表点, 一般是第9列(但不一定) 。 然后打印字符串中的其他内容。
第4条printf()语句以\r开始。 这使得光标回到当前行的起始处。 然后打印Gee!, 接着\n使光标移至下一行的起始处。
刷新输出
printf()语句把输出发送到一个叫作缓冲区(buffer) 的中间存储区域, 然后缓冲区中的内容再不断被发送到屏幕上。
C 标准明确规定了何时把缓冲区中的内容发送到屏幕: 当缓冲区满、 遇到换行字符或需要输入的时候(从缓冲区把数据发送到屏幕或文件被称为刷新缓冲区) 。
旧式编译器遇到scanf()也不会强行刷新缓冲区, 程序会停在那里不显示任何提示内容, 等待用户输入数据。 在这种情况下, 可以使用换行字符刷新缓冲区。
关键概念
计算机中的浮点数和整数在本质上不同, 其存储方式和运算过程有很大区别。 即使两个32位存储单元储存的位组合完全相同, 但是一个解释为float类型, 另一个解释为long类型, 这两个相同的位组合表示的值也完全不同。
C语言允许编写混合数据类型的表达式,但是会进行自动类型转换, 以便在实际运算时统一使用一种类型。
计算机在内存中用数值编码来表示字符。 美国最常用的是ASCII码, 除此之外C也支持其他编码。 字符常量是计算机系统使用的数值编码的符号表示, 它表示为单引号括起来的字符, 如'A'。
本章小结
C 有多种的数据类型。 基本数据类型分为两大类: 整数类型和浮点数类型。 通过为类型分配的储存量以及是有符号还是无符号, 区分不同的整数类型。 最小的整数类型是char, 因实现不同, 可以是有符号的char或无符号的char, 即unsigned char或signed char。通常用char类型表示小整数时才这样显示说明。 其他整数类型有short、 int、 long和long long类型。 C规定, 后面的类型不能小于前面的类型。上述都是有符号类型, 但也可以使用unsigned关键字创建相应的无符号类型: unsigned short、 unsigned int、unsigned long和unsigned long long。 或者, 在类型名前加上signed修饰符显式表明该类型是有符号类型。 最后, _Bool类型是一种无符号类型, 可储存0或1, 分别代表false和true。
浮点类型有3种: float、 double和C90新增的long double。 后面的类型应大于或等于前面的类型。 有些实现可选择支持复数类型和虚数类型, 通过关键字_Complex和_Imaginary与浮点类型的关键字组合(如, double _Complex类型和float _Imaginary类型) 来表示这些类型。
整数可以表示为十进制、 八进制或十六进制。 0前缀表示八进制数, 0x或0X前缀表示十六进制数。 l或L前缀表明该值是long类型, ll或LL前缀表明该值是long long类型。
在C语言中, 直接表示一个字符常量的方法是: 把该字符用单引号括起来, 如'Q'、 '8'和'$'。 C语言的转义序列(如, '\n') 表示某些非打印字符。 另外, 还可以在八进制或十六进制数前加上一个反斜杠(如, '\007') , 表示ASCII码中的一个字符。
浮点数可写成固定小数点的形式(如, 9393.912) 或指数形式(如,7.38E10) 。 C99和C11提供了第3种指数表示法, 即用十六进制数和2的幂来表示(如, 0xa.1fp10) 。
printf()函数根据转换说明打印各种类型的值。 转换说明最简单的形式由一个百分号(%) 和一个转换字符组成, 如%d或%f。