C语言整型提升、算术转换、操作符的属性
整型提升
C语言在进行整型算术运算时,总是把字符型、短整型操作数在使用之前转换为普通整型参与运算。
整型提升是按照数据类型的符号位来提升的,正数补0,负数补1。如果是无符号的数,高位也是补0。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
例子:
char a = 10;
00000000 00000000 00000000 00001010 //整型提升
00001010 //a的补码,发生截断只能保存一个字节数据
char b = 125;
00000000 00000000 00000000 01111101 //整型提升
01111101 //b的补码,发生截断只能保存一个字节数据
char res = a + b;
// 整型提升
00000000 00000000 00000000 00001010
00000000 00000000 00000000 01111101
00000000 00000000 00000000 10000111 //a + b的结果
//发生截断 res实际存的值是 10000111
printf("%d" , res);
// 整型提升
11111111 11111111 11111111 10000111
11111111 11111111 11111111 10000110
10000000 00000000 00000000 01111001
// 最终结果是 -121
#include <stdio.h>int main() {char num = 10;/*第一个printf里的num没有参与运算,所以没有整型提升第二个以及第三个printf都是一个表达式,参与运算会整型提升,所以是4个字节*/printf("%d\n", sizeof(num)); // 1printf("%d\n", sizeof(+num)); // 4printf("%d\n", sizeof(-num)); // 4return 0;
}
算术转换(自动类型转换)
1.在进行运算时,不同类型的数据要先转换成相同类型在进行运算。
2.精度低的数据类型向精度高的数据类型转换。
必做的转换
char -> int
short -> int
float -> double
运算对象的类型不同,由低向高转换
int -> unsigned int -> long -> unsigned long -> float -> double -> long double
例子:
int main () {int a = 10;short b = 20;float c = 1.5;double d = 5.6;printf("%lf" , a + b - c / d); // 这个表达式最后的类型是doublereturn 0;
}
操作符的属性
运算符 | 名称或含义 | 使用形式 | 结合方向 |
---|---|---|---|
() | 圆括号 | (表达式) 函数名(形参) | 左到右 |
[] | 下标引用 | 数组名[常量表达式] | 左到右 |
. | 结构体访问成员 | 结构体.成员 | 左到右 |
-> | 结构体指针访问成员 | 结构体指针->成员 | 左到右 |
- | 负号运算符 | -表达式 | 右到左 |
+ | 正号运算符 | -表达式 | 右到左 |
++ | 自增运算符 | ++表达式 表达式++ | 右到左 |
- - | 自增运算符 | –表达式 表达式– | 右到左 |
* | 解引用运算符 | *指针变量 | 右到左 |
& | 取地址运算符 | &变量名 | 右到左 |
! | 逻辑非运算符 | !表达式 | 右到左 |
~ | 按位取反运算符 | ~表达式 | 右到左 |
sizeof | 判断类型、变量字节数 | sizeof(表达式) | 右到左 |
(类型) | 强制类型转换 | (类型)变量 | 右到左 |
/ | 除 | 表达式/表达式 | 左到右 |
* | 乘 | 表达式*表达式 | 左到右 |
% | 取模 | 表达式%表达式 | 左到右 |
+ | 加 | 表达式+表达式 | 左到右 |
- | 减 | 表达式-表达式 | 左到右 |
<< | 左移 | 表达式<<表达式 | 左到右 |
>> | 右移 | 表达式>>表达式 | 左到右 |
> | 大于 | 表达式>表达式 | 左到右 |
>= | 大于等于 | 表达式>=表达式 | 左到右 |
< | 小于 | 表达式<表达式 | 左到右 |
<= | 小于等于 | 表达式<=表达式 | 左到右 |
== | 等于 | 表达式==表达式 | 左到右 |
!= | 不等于 | 表达式!=表达式 | 左到右 |
& | 按位与 | 表达式&表达式 | 左到右 |
^ | 按位异或 | 表达式^表达式 | 左到右 |
| | 按位或 | 表达式|表达式 | 左到右 |
&& | 逻辑与 | 表达式&&表达式 | 左到右 |
|| | 逻辑或 | 表达式||表达式 | 左到右 |
?: | 条件运算符 | 表达式1?表达式2:表达式3 | 右到左 |
= | 赋值运算符 | 变量=表达式 | 右到左 |
+= | 加赋值运算符 | 变量+=表达式 | 右到左 |
-= | 减赋值运算符 | 变量-=表达式 | 右到左 |
*= | 乘赋值运算符 | 变量*=表达式 | 右到左 |
/= | 除赋值运算符 | 变量/=表达式 | 右到左 |
%= | 模赋值运算符 | 变量%=表达式 | 右到左 |
<<= | 左移赋值运算符 | 变量<<=表达式 | 右到左 |
>>= | 右移赋值运算符 | 变量>>=表达式 | 右到左 |
&= | 按位与赋值运算符 | 变量&=表达式 | 右到左 |
^= | 按位异或赋值运算符 | 变量^=表达式 | 右到左 |
|= | 按位或赋值运算符 | 变量|=表达式 | 右到左 |
, | 逗号运算符 | (表达式,表达式,表达式,…) | 左到右 |
注:即使我们知道了操作符的优先级和结合性,但是对于一些奇怪的表达式,根据计算路径的不一致,我们依然求不出唯一值。