十、下标引用、函数调用和结构成员
0x00 下标引用操作符 [ ]
💬 这个很简单,直接上代码:
int main() { int arr[10] = {1,2,3,4,5,6,7,8,9,10}; // 0 1 2 3 4 5 6 7 8 9 printf("%d\n", arr[4]); // 5 // ↑ 这里的方块,正是下标引用操作符; // [] 的操作数是2个:arr,4 return 0; }
0x01 函数调用操作符 ( )
📚 作用:接受一个或者多个操作数;
① 第一个操作数是函数名;
② 剩余的操作数就是传递给函数的参数;
💬 代码演示:函数调用操作符
int Add(int x, int y) { return x + y; } int main() { int a = 10; int b = 20; int Add(a, b); // 此时()为函数调用操作符; return 0; }
0x02 结构成员访问操作符 - 点操作符 .
📚 作用:访问结构体成员;
😢 如果忘了什么是结构体,可以去回顾第一章(初识C语言)
https://blog.csdn.net/weixin_50502862/article/details/115426860;
💬 代码演示:点操作符的使用
struct Book { char name[20]; char id[20]; int price; }; int main() { struct Book b = {"C语言", "C20210509", 55}; printf("书名:%s\n", b.name); printf("书号:%s\n", b.id); printf("定价:%d\n", b.price); return 0; }
🚩 运行结果: 书名:C语言
书号:C20210509
定价:55
0x03 结构成员访问操作符 - 箭头操作符 ->
📚 作用:通过结构体指针访问成员;
💬 代码演示
1. 仍然可以用点操作符来写,但是略显冗琐;❎(可以但不推荐)
📌 注意事项: (*p).name ✅ *p.name ❌ 注意优先级问题!
struct Book { char name[20]; char id[20]; int price; }; int main() { struct Book b = {"C语言", "C20210509", 55}; struct Book* pb = &b; printf("书名:%s\n", (*pb).name); printf("书号:%s\n", (*pb).id); printf("定价:%d\n", (*pb).price); return 0; }
2. 使用箭头操作符,更加直观; ✅
struct Book { char name[20]; char id[20]; int price; }; int main() { struct Book b = {"C语言", "C20210509", 55}; struct Book* pb = &b; printf("书名:%s\n", pb->name); printf("书号:%s\n", pb->id); printf("定价:%d\n", pb->price); return 0; }
十一章、表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。同样,有些表达式的操作数在求职过程中可能需要转换为其他类型。
0x00 隐式类型转换
❓ 什么是整型提升:
① C的整型算术运算至少以缺省整型的精度来进行的;
② 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型这种转换,称为整型提升;
③ 整型提升:按照变量的数据类型的符号位来提升;
🔑 图解整型提升:
❓ 那么问题又来了,如何进行整型提升呢?
💡 整型提升是按照变量的数据类型的符号位来进行提升的;
📚 整型提升讲解(请仔细看注释的步骤):
int main() { // 我们发现 a 和 b 都是 char 类型,都没有达到一个 int 的大小 // 🔺这里就会发生整型提升 char a = 3; // 00000000000000000000000000000011 // 00000011 - a 因为是char类型,所以只能放8个比特位(截断)🔪 char b = 127; // 00000000000000000000000001111111 // 01111111 - b 同上,截断,存储的是8个比特位 🔪 char c = a + b; // 首先看a符号:char有符号,是正数,按照原来变量的符号位来提升 // 然后看b符号:char有符号,是正数,提升的时候也是补0 // 00000000000000000000000000000011 (高位补0,提升完结果还是这个) // + 00000000000000000000000001111111 // ------------------------------------- // 00000000000000000000000010000010 (这个结果要存到c里,c里只能存8个比特位) // 所以进行截断 🔪 // 10000010 - c (C里存的) /* 这时我们要打印它 */ printf("%d\n", c); // 🔺这时,c要发生整型提升: // 我们看c的符号,char有符号,是负数,高位进行整型提升,补1 // 10000010 - c // 然后进行整型提升 // 11111111111111111111111110000010 (补完1 之后的结果) // 🔺注意:这里是负数,原反补是不相同的! // 打印出来的是原码,内存里的是补码,现在开始反推: // 11111111111111111111111110000010 (补码) // 11111111111111111111111110000001 - 反码(补码-1) // 00000000000000000000000001111110 - 原码 // == -126 return 0; }
🚩 运行结果: -126
💬 整型提升的栗子1:下列代码运行的结果是什么(体会整型提升的存在)
int main() { char a = 0xb6; short b = 0xb600; int c = 0xb600000; if(a == 0xb6) printf("a"); //无 if(b == 0xb600) printf("b"); //无 if(c == 0xb600000) printf("c"); //c return 0; }
🚩 运行结果: c
❓ 为什么 a 和 b 不会被打印出来呢
🔑 解析:
① 因为表达式里的 a 是 char 类型,因为没有达到整型大小,所以需要进行整型提升;
② 提升后比较当然不会相等,所以不会打印a,short 同理,c也不会被打印;
③ 还有一种解释方式:char a 里面存不下,所以不是 0xb6 ,所以不打印;
💬 整型提升的栗子2:下列代码运行结果是什么(体会整型提升的存在)
int main() { char c = 1; printf("%u\n", sizeof(c)); // 1 printf("%u\n", sizeof(+c)); // 4 整型提升后等于计算一个整型的大小 printf("%u\n", sizeof(-c)); // 1 printf("%u\n", sizeof(!c)); // 4 gcc-4 return 0; }
🔑 解析:
① sizeof(c) ,c是char型,结果自然是1;
② sizeof(+c),+c参与运算了,就会发生整型提升,相当于计算了一个整型的大小,所以为4;
③ sizeof(-c),同上,一样的道理,所以为4;
③ sizeof( !c) ,这里值得一提的是,有些编辑器结果可能不是4,但是根据gcc为准,答案为4;
🔺 结论:
① 通过上面的例子ba,可以得到结论:到整型提升是确实存在的;
② 比 int 大的不需要整型提升,比 int 小的要进行整型提升;
0x02 算术转换
📚 定义:如果某个操作数的各个操作数属于不同的类型,
那么除非其中一个操作数的转换为另一个操作数的类型,否则操作无法进行;
📚 寻常算数转换:如果某个操作数类型在下面的这个表里,排名较低,
那么首先要转换为另外一个操作数的类型,然后才能执行运算;
🌰 举个栗子:(如果 int 类型的变量和 float 类型的变量放在一起,这时要把 int 转换成 float)
📌 注意事项:算数转换要合理,要不然会产生潜在的问题;
💬 精度丢失问题:
int main() { float f = 3.14; int num = f; // 隐式转换,会有精度丢失 printf("%d\n", num); // 3 return 0; }
🚩 3
0x03 操作符的属性
📚 复杂表达式的求值有三个影响的因素:
① 操作符的优先级;
② 操作符的结合性;
③ 是否控制求值顺序;
❓ 两个相邻的操作符先执行哪个?取决于他们的优先级,如果两者的优先级相同,
💬 代码演示:优先级决定了计算顺序
int main() { int a = 3; int b = 5; int c = a + b * 7; // 优先级决定:先乘后加 return 0; }
💬 代码演示:优先级一样,此时优先级不起作用,结合性决定顺序
int main() { int a = 3; int b = 5; int c = a + b + 7; // 先算左边,再算右边 return 0; }
📚 运算符优先级表:
| 操作符 | 描述 | 用法示例 | 结合类型 | 结合性 | 是否控制求值顺序 |
| ( ) | 聚组 | (表达式) | 与表达式相同 | N/A | 否 |
| ( ) | 函数调用 | rexp(rexp, ..., rexp) | rexp | L-R | 否 |
| [ ] | 下标引用 | rexp[rexp] | lexp | L-R | 否 |
| . | 访问结构成员 | lexp.member_name | lexp | L-R | 否 |
| -> | 访问结构指针成员 | rexp->member_name | lexp | L-R | 否 |
| ++ | 后缀自增 | lexp++ | rexp | L-R | 否 |
| -- | 后缀自减 | lexp-- | rexp | L-R | 否 |
| ! | 逻辑反 | !rexp | rexp | R-L | 否 |
| ~ | 按位取反 | ~rexp | rexp | R-L | 否 |
| + | 单目,表示正值 | +rexp | rexp | R-L | 否 |
| - | 单目,表示负值 | -rexp | rexp | R-L | 否 |
| ++ | 前缀自增 | ++lexp | rexp | R-L | 否 |
| -- | 前缀自减 | --lexp | rexp | R-L | 否 |
| * | 间接访问 | *rexp | lexp | R-L | 否 |
| & | 取地址 | &lexp | rexp | R-L | 否 |
| sizeof | 取其长度,以字节表示 | sizeof rexp szieof(类型) | rexp |
R-L | 否 |
| (类型) | 类型转换 | (类型)rexp | rexp | R-L | 否 |
| * | 乘法 | rexp*rexp | rexp | L-R | 否 |
| / | 除法 | rexp/rexp | rexp |
L-R | 否 |
| % | 整数取余 | rexp%rexp | rexp | L-R | 否 |
| + | 加法 | rexp+rexp | rexp | L-R | 否 |
| - | 减法 | rexp-rexp | rexp | L-R | 否 |
| << | 左移位 | rexp<<rexp | rexp | L-R | 否 |
| >> | 右移位 | rexp>>rexp | rexp | L-R | 否 |
| > | 大于 | rexp>rexp | rexp | L-R | 否 |
| >= | 大于等于 | rexp>=rexp | rexp | L-R | 否 |
| < | 小于 | rexp<rexp | rexp | L-R | 否 |
| <= | 小于等于 | rexp<=rexp | rexp | L-R | 否 |
| == | 等于 | rexp==rexp | rexp | L-R | 否 |
| != | 不等于 | rexp!=rexp |
rexp | L-R | 否 |
| & | 位与 | rexp&rexp | rexp | L-R | 否 |
| ^ | 位异或 | rexp^rexp | rexp | L-R | 否 |
| | | 位或 | rexp|rexp | rexp | L-R |
否 |
| && | 逻辑与 | rexp&&rexp | rexp | L-R | 是 |
| || | 逻辑或 | rexp&&rexp | rexp | L-R | 是 |
| ?: | 条件操作符 | rexp?rexp:rexp | rexp | L-R | 是 |
| = | 赋值 | lexp=rexp | rexp | N/V | 是 |
| += | 加等于 | lexp+=rexp | rexp | R-L | 否 |
| -= | 减等于 | lexp-=rexp | rexp | R-L | 否 |
| *= | 乘等于 | lexp*=rexp | rexp | R-L | 否 |
| /= | 除等于 | lexp /= rexp | rexp | R-L | 否 |
| %= | 以...取模 | lexp %= rexp | rexp | R-L | 否 |
<<= |
以...左移 | lexp <<= rexp | rexp | R-L | 否 |
| >>= | 以...右移 | lexp >>= rexp | rexp | R-L | 否 |
| &= | 以...与 | lexp &= rexp | rexp | R-L | 否 |
| ^= | 以...异或 | lexp ^= rexp | rexp | R-L | 否 |
| |= | 以...或 | lexp |= rexp | rexp | R-L | 否 |
| , | 逗号 | rexp, rexp | rexp | L-R | 是 |
❌ 问题表达式:
❌ 非法表达式:( 出自《C和指针》)
int main() { int i = 10; i = i-- - --i * ( i = -3 ) * i++ + ++i; printf("i = %d\n", i); return 0; }
🔑 解析: 堪比《茴香豆的一万种写法》,
这种代码,运行结果取决于环境,不要写出这种代码!








