插播小知识
1.%u 十进制无符号整数
%d把对应的整数按有符号十进制输出,%u把对应的整数按无符号十进制输出,比如
printf("%d,%u\n",-123,-123);,输出则是-123,4294967173——因为-123的补码是11111111111111111111111110000101,在%u控制下把它当正数输出了。
条件操作符(三目操作符)
exp1 ? exp2: exp3
exp1为真的话返回exp2,否则返回exp3
#include <stdio.h> int main() { int a = 10; int b = 2; int m = 0; /*if (a > b) m = a; else m = b;*/ m = (a > b ? a : b); printf("%d", m); return 0; }
逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
#include <stdio.h> int main() { int a = 1; int b = 2; int c = (a = 2 + b, b = a + b); printf("%d %d %d", a, b, c); return 0; }
从左到右执行下去,直到最后一个表达式
下标引用、函数调用和结构成员
[ ] 下标引用操作符
操作数:一个数组名+ 一个索引值
#include <stdio.h> int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; printf("%d", arr[2]); return 0; }
[]的操作数是arr 和2
( ) 函数调用操作符
#include <stdio.h> int Add(int a, int b) { return a + b; } int main() { int a = 0; int b = 2; Add(a, b);//()函数调用操作符 return 0; }
访问一个结构的成员
.结构体.成员名
->结构体指针->成员名
#include <stdio.h> struct book { char name[20]; int price; }; int main() { struct book b = { "明解C语言", 55 }; printf("%s\n", b.name); struct book* pb = &b; printf("%s\n", pb -> name); printf("%s\n", (*pb).name); return 0; }
表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
隐式类型转换
1.整形提升(运用于小于一个int 大小的数值)
表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型
提升 。
整型提升的意义: 表达式中各种长度可能小于 int 长度的整型值,都必须先转 换为 int 或 unsigned int ,然后才能送入 CPU 去执行运算
如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的
在int中有signed和unsigend, 所有char也分有符号和无符号,因为C语言没有规定,所以取决于编译器
/ 负数的整形提升
char c1 = - 1 ;
变量 c1 的二进制位 ( 补码 ) 中只有 8 个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为 1
提升之后的结果是:
11111111111111111111111111111111
// 正数的整形提升
char c2 = 1 ;
变量 c2 的二进制位 ( 补码 ) 中只有 8 个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为 0
提升之后的结果是:
00000000000000000000000000000001
// 无符号整形提升,高位补 0
注意一下,对char进行提升要先写出8个bit来确定符号位,然后根据符号位进行整体提升,计算机就是这样计算大小小于int的数值
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; }
a,b 要进行整形提升 , 但是 c 不需要整形提升
a,b 整形提升之后 , 变成了负数 , 所以表达式 a==0xb6 , b==0xb600 的结果是假 , 但是 c 不发生整形提升 , 则表 达式 c==0xb6000000 的结果是真 .
我们只需要知道小于int大小的数值要整形提升
#include <stdio.h> int main() { char a = 1;// 00000000000000000000000000000001 补码 ---a存储的只有00000001 char b = 30;// 00000000000000000000000000011110 补码 ----b存储的只有00011110 char c = a + b;// 参与了运算 //a整形提升为 00000000000000000000000000000001 //b整形提升为 00000000000000000000000000011110 // 结果 00000000000000000000000000011111 //00011111 -char c存储的8个bit //c整形提升 00000000000000000000000000011111 --->补码 //最终原码为 00000000000000000000000000011111 printf("%d", c); return 0; }
所以我们可以理解为当字符或者其他小于int长度的,在参与运算或者判断又或者配合某些操作符就会发生整形提升,提升到4个字节,否则原来是多少字节就是多少字节
2.算术转换(用于大小大于或者等于int 大小的数组)
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
操作符的属性
复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
#include <stdio.h> int main() { int a = 0; int b = 2; //优先级 b = 9 + 5 * 6; //结合性 b = 6 + 5 + 3; return 0; }
在相邻操作符不一样的时候,先考虑优先级
相邻操作符相同时,考虑结合性
结合性就是计算方向
#include <stdio.h> int main() { int i = 1; int ret = (++i) + (++i) + (++i); printf("%d\n", ret); printf("%d\n", i); return 0; }
这个代码在不同编译器结果不一样,所以这个代码的写法不行,我们可以看看vs编译器是怎样运行的,我们看看反汇编
mov :是把后面的值存放到前面去, 把1存放到dword ptr[i]
add:增加
eax、ecx、都是寄存器
总结 :我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题 的。