4.6.3 表达式的类型
在C语言里,表达式可以计算出一个值,例如表达式5+3的值是8。值是有类型的,所以表达式也有类型,表达式的类型与其值的类型一致。
不同的运算符需要不同类型的操作数,有些表达式具有固定的类型,而有的表达式的类型则需要根据运算符和操作数的类型来共同确定,不一而足。对运算符、表达式及其类型的完整介绍位于后面的章节,现在,我们通过一个小小的示例程序来大体了解一下。
/********c0407.c********/ int max(int a, int b) {
if(a >= b)return a; else return b; } int main(void) { int x = 1, y; unsigned char c; x += c = 56; c += max(x, y); y = ++ x; }
我们已经讲过整型常量,这是一个语法上的概念,指的是一种表示整数的语法成分。当这种语法成分实际出现在程序中时,它就摇身一变成了常量表达式。
在程序中,变量x的初始化器是常量表达式1,其类型为int,所以也可以说它是int类型的常量表达式。这个常量表达式在程序翻译期间由代表数字的符号转换为数字1并用于初始化同类型的变量x。
再来看表达式x += c = 56,它等价于x +=(c = 56),所以我们先分析它的子表达式c = 56。左值c的类型是其所代表的变量c在声明时指定的类型,你可能觉得这样说有些啰唆,因为左值c和变量c是同一种东西。但是,它们的身份不同,在声明里,c是变量,在表达式里,c是左值。
左值c的类型是unsigned char,但是常量表达式56的类型是int,必须要执行整数类型—整数类型转换,将56从int类型转换为unsigned char类型后再赋给左值c所代表的变量。
在C语言里,赋值表达式的类型是赋值运算符左操作数的类型。因此,表达式c = 56的类型是unsigned char;这个子表达式要计算一个值,这个值是赋值运算符的左操作数被赋值之后的存储值56,值的类型是赋值表达式的类型,即unsigned char。
接着来看,子表达式c = 56的值被加到左值x。在运算符+=的右侧,操作数的类型是unsigned char;在左侧,操作数x的类型是int,这必然要将右侧表达式的结果56从它原先的unsigned char转换为int,然后再进行赋值操作。最后,还是那句话,赋值表达式的类型是赋值运算符左操作数的类型,故表达式x += c = 56的类型是int。
继续来看表达式c += max(x, y),函数调用表达式的类型是被调用函数的返回类型,它的值是函数的返回值。所以,子表达式max(x, y)的类型是int,这也是该表达式的值的类型,这个值来自函数max的返回值。
函数调用的返回值被加到左值c,左值c的类型是unsigned char而函数调用表达式的类型是int,必须将后者从int类型转换为unsigned char之后才能赋值。由于赋值运算符的左操作数是unsigned char类型,故表达式c += max(x, y)的类型也是unsigned char。
如果函数的返回类型是void,则函数调用表达式的类型也是void,这样的函数将返回空值,或者说返回不存在的值。这样的表达式可以添加一个分号“;”使其成为表达式语句,但它不能作为所有运算符的操作数。
现在转到max函数内部,if语句的控制表达式是关系表达式a>= b。在C语言里,不管运算符>、>=、<和<=的操作数是什么类型,关系表达式的类型始终为int。关系成立,关系表达式的结果是int类型的值1;否则,关系表达式的结果是int类型的值0。
最后再回到main函数,来看表达式y = ++ x。子表达式++ x是前缀递增表达式,它的类型和前缀递增运算符++的操作数相同。因为左值x的类型是int,故表达式++ x的类型也是int,与另一个左值y的类型一致,可以赋值。
为了更好地理解“表达式的类型”这一主题,我们将上述程序保存为源文件c0407.c并翻译为可执行文件,然后在调试器里用ptype命令逐一验证如下。
(gdb)l 1 int max(int a, int b) 2 { 3 if(a >= b)return a; 4 else return b; 5 } 6 7 int main(void) 8 { 9 int x = 1, y; 10 unsigned char c; (gdb)l 11 x += c = 56; 12 c += max(x, y); 13 y = ++ x; 14 } (gdb)b 3 Breakpoint 1 at 0x40155a: file c0407.c, line 3. (gdb)r Starting program: D:\exampls\a.exe [New Thread 10100.0x2b00] [New Thread 10100.0x294] Thread 1 hit Breakpoint 1, max(a=57, b=0)at c0407.c:3 3 if(a >= b)return a; (gdb)ptype a >= b type = int (gdb)n 5 } (gdb)n main()at c0407.c:12 12 c += max(x, y);
(gdb)ptype c = 56 type = unsigned char (gdb)ptype x += c = 56 type = int (gdb)ptype max(x, y) type = int (gdb)ptype c += max(x, y) type = unsigned char (gdb)ptype ++ x type = int (gdb)ptype y = ++ x type = int (gdb)c Continuing. [Thread 10100.0x294 exited with code 0] [Inferior 1(process 10100)exited normally] (gdb)q
因为函数max的参数只在函数内有效,所以我们先将断点设置在该函数内。当程序的执行到达函数内的断点时,再用ptype命令打印表达式a>= b的类型。
然后,连续用n命令使程序的执行回到main函数,再用ptype命令打印各个表达式的类型。