C语言非常道
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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命令打印各个表达式的类型。