操作符
接上次的课:操作符详解, 还有几个操作符没有讲完,这里一并讲完,然后进入操作符的优先级与结合性。
剩余操作符
下标访问操作符 [ ]
这个很简单,它是和数组一起使用的,用于访问数组中的特定元素。例如:arr[9]
访问的是数组中的第10个元素。需要说明的是, [ ]
的操作数是arr
和 9
。
函数调用操作符 ()
这个在调用函数的时候必须使用的,操作数是函数名和其余参数。这个操作符至少有1个操作数,就是函数名。
优先级
优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不一样的。不需要去强行记忆各个运算符的优先级,大概记住下表中操作符的优先级就可以了。
优先级 | 操作符 | 操作符名称 |
---|---|---|
1 | () | 圆括号 |
2 | ++ – | 自增运算,自减运算 |
3 | + - | 单目运算 +、- |
4 | * / | 乘法、除法 |
5 | < > | 关系运算符 |
6 | = | 赋值运算符 |
结合性
如果两个运算符优先级相同,那么就使用结合性来对表达式进行运算。看运算符是左结合还是右结合。例如:
5*6/2
乘与除优先级相同,它们都是左结合运算符,所以从左向右执行。
表达式求值
整型提升
C语言中整型算术运算总是至少以缺省精度(一般是整型)来进行的。为了得到这个精度,表达式中低于这个精度的操作数在使用之前会被转换成普通的整型,这个转换就是整型提升。
需要整型提升是因为ALU的操作数一般是int的字节长度,同时也是通用寄存器的长度。举个例子:
char a,b,c;
...
a = b+c;
b和c在整型提升后执行加法,结果被截断,存储在a中。
整型提升的过程:
- 有符号数按照变量数据类型的符号位来提升;
- 无符号数高位直接补0
截断:
舍弃高位,只存储与数据类型对应的低位。
算术转换
如果某个操作符的各个操作数属于不同的类型,那么在进行操作前,会按照下表将排位靠下的操作数类型转换为排位靠上的操作数类型。
级别 | 数据类型 |
---|---|
1 | long double |
2 | double |
3 | float |
4 | unsigned long int |
5 | long int |
6 | unsigned int |
7 | int |
例如一个float
类型的变量和一个int
类型的变量相加,int
类型的变量会发生算术转换,转换为float
类型后再执行相加。
表达式求值
有了以上内容,在表达式求值的时候还是会有不确定的地方。以下是几个例子和相应的分析。
int main()
{
a*b + c*d + e*f; //通过优先级和结合顺序无法确认上述表达式的唯一计算路径
c + --c;//加法左边的c值未知
return 0;
}
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("ret = %d\n", ret);
printf("i = %d\n", i);
return 0;
}
先计算3个++i
呢 还是计算(++i)+(++i)
之后再计算最后一个++i
,然后再相加?
这里一定要注意不要书写类似的代码。这种复合型的表达式可以拆分成多个表达式分别计算,避免让自己和编译器混乱。
指针
终于要开始指针了。指针不难,认真学就好。
内存和地址
内存中最小单元是字节,内存中每个字节的编号就是指针。指针也是地址。一句话就是内存中最小单元的编号 == 指针 == 地址。所谓的32位系统和64位系统,从内存的角度理解就是32位系统可以给0~232 个单元编号,64位的就是264 个。
这里不需要将这些地址都存下来,因为一块内存在出厂的时候,其中最小单元的编号就是固定的。CPU可以通过这个编号来访问内存中的对应空间里的数据。
指针变量和地址
取地址操作符 &
创建变量的本质是向内存中申请空间,那么如何得到这个变量的地址呢?
int a = 10;
&a;
这里 &a
就得到了a的地址。注意,这里虽然变量a占用了4个字节,我们只需要知道第一个字节的地址就可以了。
指针变量
存放指针的变量就是指针变量。上述例子中,如何将a的地址存起来呢?
int a = 10;
int* p = &a;
这里 p
就是指针变量。p
的类型就是int*
。p
的特殊之处在于,存放在它里面的值都被理解为地址。*
说明p
是指针,int
说明p
指向的对象是整型。那么如何写一个指向char
类型的指针呢?
char ch = 'w';
char* pc = &ch;
解引用操作符 *
保存了指针,那么就一定是需要通过这个指针来找到它所指向的对象的,这就需要解引用操作符*
。看一下代码:
int a = 10;
int* pa = &a;
*pa = 0;
上述代码的最后一句就是通过解引用操作符将pa
指向的对象a
修改为0
。