目录
单目操作符(sizeof与数组)
下面有一段代码:
void test1(int* arr)
{
printf("%zd\n", sizeof(arr));
}
void test2(char* ch)
{
printf("%zd\n", sizeof(ch));
}
int main()
{
int arr[10] = { 0 };
char ch[10] = { 0 };
printf("%zd\n", sizeof(arr));
printf("%zd\n", sizeof(ch));
test1(arr);
test2(ch);
return 0;
}
打印结果为
32位表示CPU能放下最大2^32(字节)的数值
64位表示CPU能放下最大2^64(字节)的数值
数组传参,形参可以写成数组,也可以写成指针,但本质都是数组首元素地址
逻辑操作符
&& 并且
|| 或
逻辑表达式返回值为 1 / 0 ,真或假
看这样一段代码
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
return 0;
}
打印结果分别为1 2 3 4
由于赋值操作符=优先级很低,所以先执行右边表达式。a++为后置++,先使用,但a=0为假,右边表达式一定为假,所以b,d并未进行操作,故结果为1 2 3 4 ,则逻辑表达式返回0,即i=0
同样的,下面这段代码
int main()
{
int i = 0, a = 1, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
return 0;
}
打印结果为2 2 3 4。a=1为真,则该表达式一定为真,所以b,d不进行操作
条件操作符
exp1 ?exp2 :exp3
若exp1成立,则执行exp2,否则执行exp3
表达式求值
1).整型提升
C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
整型提升的意义
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。 通用CPU(general-purposeCPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)
所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算
a 和 b 的值先被提升为普通整型,然后再执行加法运算
整型提升的过程
1. 有符号整数提升是按照变量的数据类型的符号位来提升的
2. 无符号整数提升,高位补0
char a = 3;
3的二进制序列为
00000000000000000000000000000011
截断(char占8个字节)
00000011 —> a
char b = 127;
127的二进制为
00000000000000000000000001111111
截断
01111111 —> b
char c = a + b;
00000011 a
01111111 b
进行运算,要整型提升,高位补0
00000000000000000000000000000011 a
00000000000000000000000001111111 b
相加
00000000000000000000000010000010 c
截断
10000010 —> c
printf(" %d\n " , c);
%d 是打印十进制的整数
整型提升,高位补1
11111111111111111111111110000010 — 补码
11111111111111111111111110000011
10000000000000000000000011111110 — 原码
即-126
来看一段代码
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)
printf("c");
return 0;
}
运行结果只有c被打印
0xb6为十六进制数字(0x表示b6是十六进制)
0xb6的二进制序列为
00000000000000000000000010110110
截断
10110110
整型提升,高位补1
11111111111111111111111110110110 ——> a
显然 00000000000000000000000010110110 ! = 11111111111111111111111110110110
故a不打印
同理,b 也不打印
c为整形类型,不进行整型提升,故打印
再看这样一个运行结果
(%u打印无符号整数)
+c ,-c 使c发生整型提升,int类型占4个字节
2).算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行,下面的层次体系称为寻常算术转换
1 long double
2 double
3 float
4 unsigned long int
5 long int
6 unsigned int
7 int
如果某个操作数的类型在上面这个列表中排名靠后,那么首先要转换为另外一个操作数的类型后执行运算
3).操作符的属性
1 操作符的优先级
2 操作符的结合性
优先级,即决定谁先算,谁后算
结合性,即优先级相同时,决定左结合(从左往右算)还是右结合(从右往左算)
大部分运算符是左结合,少部分为右结合
参考: https://zh.cppreference.com/w/c/language/operator_precedence
常用的优先级顺序
• 圆括号( () )
• 自增运算符( ++ ),自减运算符( -- )
• 单目运算符( + 和 - )
• 乘法( * ),除法( / )
• 加法( + ),减法( - )
• 关系运算符( < 、 > 等)
• 赋值运算符( = )