1. 表达式定义

C语言中的表达式是一种有值的语法结构,它由运算符(变量,常量,函数调用返回值)结合而成,每个表达式一定有一个值

常量表达式

比如 a,12,12.4 值就是变量或者常量本身的值,作为条件的时候,非零为真,零值为假

算术表达式

a+b c*d+a 12/3+d i++ –a 值就是表达式的结果

赋值表达式

a=12 a+=12 a = c = d 表示对 a 变量进行赋值

关系表达式

a > b 2 ==3 值可以认为是布尔值,由于C语言中没有bool 值,用整型值 0 和 1 表示

逻辑表达式

a && b c || d !a 值可以认为是布尔值,由于C语言中没有bool 值,用整型值 0 和 1 表示

复合表达式

x = ( y = (a + b + a > 4), z=10) 依据运算符优先级和结合性得到结果

逗号表达式

(1,2,3,4,a) 从左到右依次求值,表达式的值是逗号最右边的值

有返回值的函数

有返回值的函数也是一个表达式

2. 表达式求值

整型提升

C语言的整数算术运算至少是以缺省的整型类型的精度进行的,为了获得这个精度,表达式中字符型和短整型在使用之前转换为普通整型,这种转换称为整型提升

char a,b,c;

a = b + c

b 和 c 的值被提升为普通整型,然后再执行加法运算,加法运算的结果被截短,然后再存储于 a 中

隐式类型转换

C语言在下面几种情况下进行隐式类型转换:

  1. 算术表达式中,低类型会转换为高类型
  2. 赋值表达式中,右侧表达式的值可以自动转换为左侧变量的类型,赋值给它
  3. 函数调用中参数传递时,系统隐式将实参转换为形参的类型后,赋值给形参
  4. 函数有返回值时,系统隐式将返回表达式类型转换为返回值类型,并赋值给调用函数

表达式求值中的自动类型转换

下面层次体系称为寻常算术转换:

转换规则:

  1. 如果某个操作数类型在上面列表中排名较低,那么首先将其转换为另一个操作数的类型然后再执行操作
  2. 转换按照数据长度增加的方向进行,以保证精度不降低,比如 int 型和 long 型运算时,先把int 类型转换为 long 型后再进行计算
  3. 若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型
  4. 所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算
  5. char 和 short 类型参与运算时,必须先转换为 int 类型(整形提升)

注意float 类型转换为整型值的时候,小数部分会被舍弃(并不进行四舍五入

static void _floatconvert() {float m = 3.14f;int n = m;printf("the value of n = %d \n", n);}

3. 操作符的属性

​​​​​​复杂表达式的求值顺序是由三个因素决定的,操作符的优先级,操作符的结合性以及操作符是否控制执行顺序。

两个相邻的操作符哪个先执行取决于它们的优先级,如果两者优先级相同,那么执行顺序由它们的结合性决定

简单来说,结合性就是一串操作符是从到右依次执行还是从右到左依次执行

  • 初级运算符总共有四个下标访问操作符[ ],括号操作符(),访问结构成员 .,访问结构指针成员 ->,
  • 算术运算符先乘除后加减
  • 关系运算符中先大小后相等(关系运算符中 > = <= 的优先级高于 == 和 ! =
  • 位运算分为四个等级,取反(等级最高是单目运算符),与,异或,或
  • 逻辑运算符分为三个等级,逻辑非(等级最高是单目运算符),逻辑与,逻辑或

运算符中只有三类是从右到左,其余都是从左到右

赋值,条件(?:)和单目运算符 这三类运算符的结合方式是从右到左,其余都是从左到右

常见的优先级问题:

  • 结构体成员访问运算符的优先级高于间接访问 *p.f 等同于*(p.f),使用访问结构体成员运算符

p -> f 可以避免这个问题

  • 下标访问运算符优先级高于间接访问 int *ap[ ],表示的是一个指针数组
  • 函数调用优先级高于间接访问,int *p(),表示返回值是指针的函数 p
  • == 和 != 优先级高于位操作 value & value != 2 等同于value & (value != 2)
  • == 和 != 优先级高于赋值运算符,c = getchar() != EOF 等同于c = (getchar() != EOF)
  • 算术运算符优先级高于位移运算符,msb << 4 + lsb 等同于msb << (4 + lsb)
  • 逗号运算符的优先级最低
  • 间接访问运算符优先级高于算术运算符,*p + 1 表示的是 间接访问 p 的值加1,*(p + 1)表示的是访问 p 指向元素连续内存的下一个元素
void _testOperatorPriority() {int array[] = { 10, 20, 30, 40 };int *p = array;//11printf("*p + 1 = %d \n", *p + 1);// 20printf("*(p + 1) = %d \n", *(p + 1));// *p++printf("*p++ = %d %d %d\n", *p++, *p++, *p++);}