C语言中的类型转换远比其他语言更为常见,其他语言一般将类型转换只用于操作数上,使操作符两端数据类型保持一致。C语言也执行该项任务,但它同时也提升比规范类型int或double更小的数据类型。
一、类型转换的种类
(1)操作符的操作数类型不一致时所发生的类型提升(通常存在表达式中)
(2)函数原型声明不当时发生的参数提升(函数的参数也是表达式)。
二、类型转换的目的
隐式类型转换是语言中的一种临机手段,起源于简化最初的编译器的想法。把所有的操作数转换为统一的长度极大地简化了代码的生成。这样,压到堆栈中的参数都是同一长度的,所以运行时系统只需要知道参数的数目,而不需要知道它们的长度。
(注:在K&R C中,函数传递过程中实际传递的参数类型与实际接收的参数类型会发生转换。在被调用函数的函数体内,提升后的类型会根据函数定义时参数的声明类型自动裁剪为该类型,之所以这样麻烦,目的还是为了简化编译器。)
三、类型转换规则
(1)整型提升:char、short和位段类型(无论signed或unsigned)以及枚举类型将被提升为int,前提是int能够完整地容纳原先的数据,否则将被转换为unsigned int(ANSI C表示如果编译器能够保证运算结果一致,也可以省略类型提升);
(2)float类型将会被提升为double;
(3)任何类型的数组将会被提升为相应类型的指针。
具体可以参照下表:
| 源类型 | 通常提升后的类型 |
|---|---|
| char | int |
| 位段 | int |
| 枚举 | int |
| unsigned char | int |
| short | int |
| unsigned short | int |
| float | double |
| 任意数组 | 相应类型的指针 |
四、函数原型的引入
ANSI C函数原型目的是使C语言成为一种更加可靠的语言。建立原型就是为了消除形参与实参之间类型不匹配。
(1)K&R C的函数声明和ANSI C原型的对比
K&R C**———————————**ANSI C
声明: 原型:
int foo(); int foo(int a, int b);
或
int foo(int, int);
定义: 定义:
int foo(a, b) int foo(int a, int b)
int a; {
int b; ......
{ }
......
}
值得在意的是:ANSI C并没有也不可能排它性地使用函数原型,包括以后不断出现的新标准,因为这样做使它们无法兼容数以十亿行的在ANSI C之前便存在的C代码。也就是说,在未来两种风格会并存。另外,函数原型的引入也非必须,如果使用函数原型,缺省参数提升就不会发生(这与前边提到的简化编译器存在实践上的差异,可能是为了产生安全、可靠的代码所引入的吧)。
五、原型注意事项
原型在以下2种情况下会失败(产生的结果与预期不一致或直接出错)
ANSI C函数声明(原型)和K&R C函数定义
如果使用较窄的类型就会失败!函数调用时所传递的是实际类型,而函数期望接收的是提升后的类型。K&R C函数声明和ANSI C函数定义
如果使用较窄的类型就会失败!函数调用时所传递的是提升后类型,而函数期望接收的是实际类型。
具体差异形式可以参考《C专家编程》第8章(176~177页),或者随时call我。
六、转换结果产生差异的原因(究其根本是每种类型的底层设计不同)
下一篇我会进一步介绍类型转换之间的相关计算原理问题。
本文详细介绍了C语言中的类型转换,包括类型转换的种类、目的和规则,并探讨了函数原型的作用及其注意事项。此外,还解释了类型转换结果产生差异的根本原因。
1264





