表达式(八)
四、一元运算符
C语言中一元运算符包括:++(自增运算符)、--(自减运算符)、&(地址运算符)、*(间接运算符)、+(一元加运算符)、-(一元减运算符)、!(逻辑非运算符)、~(按位取反运算符)、sizeof运算符、alignof运算符。
1、前缀自增运算符和前缀自减运算符
自增运算符(increment operator)分为前缀自增运算符和后缀自增运算符。本节主要讨论前缀自增运算符,前缀自增表达式的语法格式如下所示:
++ unary-expression
其中unary-expression是一元表达式,其类型为原子的、限定的或者非限定的实数类型或者指针类型,并且应为可修改左值(modifiable lvalue);++是自增运算符。
前缀自增运算符会使操作数的值增加1。前缀自增运算符的运算结果是操作数增加1后的新值。
int i = 3; printf("%d\n", ++i); //输出4。 printf("%d\n", i); //输出4。
表达式++E等价于表达式E+=1,其中值1具有适当的类型。
int i = 3; printf("%d\n", i+=1); //输出4。 printf("%d\n", i); //输出4。
前缀自减运算符的语法格式如下所示:
-- unary-expression
前缀自减运算符与前缀自增运算符相似,区别在于前缀自减运算符的操作数值会减少1。
2、地址运算符和间接运算符
地址运算符(address operator)生成操作数的地址;如果操作数的类型为T,结果表达式的类型为指向T的指针类型。
int i; &i; //&i的类型为指向int类型对象的指针类型,值为i的地址。
地址运算符的操作数应是函数指示符、[ ]或者间接运算符(*)的运算结果,或者表示对象的左值(该对象不是位字段,并且声明时未使用register类型限定符限定。)。
int func(int number) { return number; } ... int i; int arr[5]; int *ptr = &i; //合法。 &func; //合法。 &arr[2]; //合法。 &*ptr; //合法。
如果地址运算符的操作数是间接运算符的运算结果,地址运算符和间接运算符都不会被评估,结果与省略这两个运算符的结果相同,只是运算符的限制条件仍然适用,并且结果不是左值。
int i,j; int *ptr = &i; &*ptr == &i; //&*ptr和&i相等。 &*ptr = &j; //非法,&*ptr不是左值。
如果地址运算符的操作数是数组下标运算符([ ])的运算结果,那么数组下标运算符所隐含的间接运算符和地址运算符都不会被评估,其结果就像去掉了地址运算符,并将数组下标运算符改为二元+运算符;例如:&E1[E2]等价于E1+E2。
间接运算符的操作数应是指针类型;如果操作数是指向函数的指针,结果是函数指示符;如果操作数是指向对象的指针,结果是表示对象的左值;如果操作数是指向T类型的指针,结果具有T类型。
int func(int number) { return number; } ... int i = 5; int *ptr = &i; printf("%d\n", *ptr); //输出5,等价于i。 printf("%d\n", (*func)(5)); //输出5,等价于func(5)。
如果指针值是无效值,间接运算符的行为是未定义的;这种情况下无效值包括:空指针、与指向对象类型对齐不匹配的地址、对象生存期(lifetime)结束后的地址。
int *p1 = NULL; printf("%d\n", *p1); //会发生Segmentation fault错误。 { int i = 5; int *p2 = &i; } printf("%d\n", *p2); //指针和指向对象生存期已结束。