知识点:C++ 运算符
Q1:C++ 运算符优先级怎么记?
回答模板:
C++ 运算符优先级从高到低可以这样记:
最高 :
()、[]、->、.(括号和成员访问)高 :
!、~、++、--(单目运算符)中 :
*、/、%(乘除)→+、-(加减)中低 :
<、>、<=、>=(关系比较)→==、!=(等于比较)低 :
&&(逻辑与)→||(逻辑或)最低 :
?:(条件运算符)→=(赋值运算符)记忆口诀:"单目 > 算术 > 关系 > 逻辑 > 赋值 "
Q2:
!和~的区别是什么?回答模板:
!是逻辑非,~是按位取反。
运算符 类型 作用 示例 !逻辑非 非0→0,0→1 !5 = 0,!0 = 1~按位取反 每一位0→1,1→0 ~5 = -6详细解释:
cpp// ! 逻辑非(针对整个值) int a = 5; !a == 0; // 5 非0,所以为 0 int b = 0; !b == 1; // 0 取反为 1 bool flag = true; !flag == false; // ~ 按位取反(针对每一位) // 5 = 0000 0101 // ~5 = 1111 1010 = -6 int c = 5; int d = ~c; // d = -6Q3:什么是"单目运算符"?为什么优先级高?
回答模板:
单目运算符是只操作一个操作数 的运算符,如
!、~、++、--、-(负号)。它们优先级最高,是因为它们通常直接作用于一个变量,需要先计算才能参与后续运算。
示例:
cppint a = 5; int b = 10; int c = -a + b; // 先计算 -a(单目),再计算加法 // 相当于:int c = (-a) + b;Q4:
++i和i++的区别?回答模板:
写法 含义 返回值 常见用法 ++i先自增,后使用 自增后的值 推荐(效率略高) i++先使用,后自增 自增前的值 需要原值时使用
cppint i = 5; int a = ++i; // i=6, a=6(先加后用) int b = i++; // b=6, i=7(先用后加)Q5:什么是"右结合"运算符?
回答模板:
大多数运算符是左结合 的(从左到右计算),但单目运算符、条件运算符、赋值运算符是右结合的(从右到左计算)。
cpp// 右结合示例 int a = b = c = 5; // 从右到左执行:c=5, b=c, a=b // 等价于:int a = (b = (c = 5)); // 条件运算符也是右结合 int result = a > b ? a : b > c ? b : c; // 等价于:int result = a > b ? a : (b > c ? b : c);Q6:写代码时如何避免优先级错误?
回答模板:
我遵循一个原则:不确定优先级时,用括号明确表达意图。
cpp// ❌ 不清晰(需要查优先级) if (a > b && c > d || e > f) // ✅ 清晰(括号明确优先级) if ((a > b && c > d) || e > f)6."三目运算符是 C++ 唯一的三目运算符,格式是
条件 ? 表达式1 : 表达式2,条件为真返回表达式1,为假返回表达式2。它适合简单的条件赋值,比如int max = (a > b) ? a : b。复杂逻辑建议用 if-else,可读性更好。"7.源码、补码、反码
"计算机存储整型数值时,最左边的一位是符号位,0 代表正数,1 代表负数。
原码 就是二进制本身,比如 10 的原码是
0000 1010,-10 的原码是1000 1010。反码 :正数的反码和原码一样。负数的反码是符号位不变,其他位 0 变 1、1 变 0。比如 -10 原码
1000 1010,反码就是1111 0101。补码 :正数的补码和原码一样。负数的补码是反码加 1。比如 -10 反码
1111 0101,补码就是1111 0110。计算机实际存储用的是补码,因为补码可以把减法统一成加法来运算,简化了硬件设计。"
"原码就是二进制本身,反码正数不变、负数按位取反,补码是反码加 1。计算机存储和运算都用补码。"
为什么要用补码?
"如果用原码,正数和负数的加法需要单独处理,硬件更复杂。补码的好处是可以把减法变成加法,比如
5 - 3可以变成5 + (-3),统一用加法器处理,简化了硬件电路设计。"举例
"比如
10 + (-10):
原码:
0000 1010+1000 1010=1001 0100(-20)❌ 错误!补码:
0000 1010+1111 0110=1 0000 0000,溢出舍去高位,结果是0✅ 正确!这就是为什么计算机用补码的原因。"
8.短路求值
一句话定义
逻辑运算中,如果左侧表达式已经能确定结果,右侧表达式就不会执行。
两个核心规则
运算符 规则 说明 &&(逻辑与)左假则右不执行 因为 false && anything = false` `(逻辑或) 有符号转无符号,运算是补码运算,输出看格式。
cppint a = -7; // 有符号 unsigned int b = 3; // 无符号 // a + b 时,a 被隐式转换为无符号数 // 按补码进行运算 // 打印结果取决于格式原码、反码、补码总结表
数值 原码 反码 补码 7 0000 0111 0000 0111 0000 0111 -7 1000 0111 1111 1000 1111 1001 3 0000 0011 0000 0011 0000 0011 -4 1000 0100 1111 1011 1111 1100 8."
%是取模运算符,用来计算整数除法的余数。两边必须是整数类型 ,如果一边是浮点数,编译会报错。如果是浮点数取余,可以用fmod()函数。"9."三目运算符格式是
条件 ? 表达式1 : 表达式2,条件为真返回表达式1,为假返回表达式2。注意两个表达式类型要一致,并且%运算符两边必须是整数。"1.continue 关键字定义
continue跳过本次循环剩余的代码,立即进入下一次循环判断。注意:
continue不会终止整个循环,只结束当前这一轮!核心理解
cpp┌─────────────────────────────────────────────────────────────────────┐ │ for (i = 0; i < 5; i++) { │ │ if (i == 2) { │ │ continue; ← 跳过本次循环剩下的代码 │ │ } │ │ printf("%d", i); ← i=2 时这行不执行 │ │ } │ │ 输出:0 1 3 4 │ └─────────────────────────────────────────────────────────────────────┘continue vs break
对比项 continue break 作用 跳过本次循环 终止整个循环 循环是否继续 继续下一次 彻底结束 使用场景 跳过某些条件 提前结束 break 关键字
定义
break 用于立即退出当前所在的循环或 switch 语句,跳到后面的代码执行。
break 的两种使用场景
场景 作用 循环中 跳出本层循环(for、while、do-while) switch-case 中 跳出当前 case,防止继续执行下一个 case 3.return
定义
return 结束当前函数执行,返回调用处,并可带回一个返回值。
核心作用
作用 说明 结束函数 函数执行到 return 立即停止 返回调用处 跳回调用该函数的地方 返回值 可以返回一个值给调用者(void 函数无返回值) "return 有两个作用:结束当前函数 和返回一个值 。函数执行到 return 立即停止,后面的代码不再执行。void 函数也可以使用
return;提前结束。我经常用 return 来处理错误情况,比如参数校验失败时提前返回。"4.goto
定义
goto 无条件跳转到程序中的指定标签位置。
为什么不推荐使用 goto?
原因 说明 破坏结构化编程 使代码逻辑混乱 可读性差 跳来跳去难以理解 维护困难 修改代码时容易出错 有更好的替代 用循环、函数、break 替代 5.volatile
定义
volatile 告诉编译器:这个变量随时可能被外部改变,不要优化,每次使用都必须从内存重新读取。
为什么需要 volatile?
cpp// ============================================================ // 没有 volatile:编译器优化 // ============================================================ int a = 20; int b, c; b = a; // 从内存读取 a 到寄存器,再存到 b c = a; // 编译器优化:直接从寄存器取 a 的值(可能已过时) // 如果 a 在两次读取之间被外部(中断、其他线程)修改了 // c 拿到的就是旧值! // ============================================================ // 有 volatile:强制从内存读取 // ============================================================ volatile int a = 20; int b, c; b = a; // 从内存读取 a c = a; // 还是从内存读取 a(保证拿到最新值)常见使用场景
场景 说明 多线程共享变量 多个线程同时读写,需要最新值 中断服务程序 中断可能修改变量值 硬件寄存器 AD 转换、GPIO 寄存器随时变化 信号处理函数 信号可能修改全局变量 "volatile 告诉编译器不要对该变量进行优化,每次使用都必须从内存中读取最新值,而不是从寄存器取缓存值。
因为编译器默认会优化,如果变量在两次读取之间没有赋值操作,编译器可能会直接从寄存器取值。但如果这个变量会被中断、其他线程或硬件改变,就会读到旧值。
常见使用场景:多线程共享变量、中断程序、硬件寄存器。"
一、struct 结构体
1. C 语言 vs C++ 结构体区别
对比项 C 语言 C++ 函数成员 ❌ 不允许 ✅ 允许 继承 ❌ 不支持 ✅ 支持 访问权限 默认 public,不可修改 可设置 private/public/protected 数据成员初始化 ❌ 不可以 ✅ 可以在声明时初始化 空结构体大小 0 1 使用方式 必须加 struct关键字或typedef可直接使用类名 2. 代码示例
cpp// ============================================================ // C 语言使用方式 // ============================================================ // 方式一:必须加 struct 关键字 struct Student { int age; int num; int sex; }; struct Student s1; // 必须写 struct // 方式二:使用 typedef typedef struct Student { int age; int num; int sex; } Student; Student s2; // 可以直接使用 // ============================================================ // C++ 使用方式(直接使用类名) // ============================================================ struct Student { int age; int num; int sex; }; Student s; // 直接使用,不需要 struct 关键字
- 结构体大小计算(字节对齐)
cpp// ============================================================ // 字节对齐规则 // ============================================================ struct A { char a; // 1 字节 int b; // 4 字节 char c; // 1 字节 }; // 实际大小:12 字节(不是 6) // 原因:对齐到最大成员(4)的倍数 struct B { int a; // 4 字节 char b; // 1 字节 char c; // 1 字节 }; // 实际大小:8 字节 struct C { char a; // 1 字节 char b; // 1 字节 int c; // 4 字节 }; // 实际大小:8 字节 // ============================================================ // 面试高频题:求结构体大小 // ============================================================ struct Test { char c; // 1 double d; // 8 int i; // 4 }; // 大小:24(对齐到 8 的倍数)4. struct vs class(C++)
对比项 struct class 默认访问权限 public private 默认继承权限 public private 定义模板 ❌ 不能 ✅ 可以 使用场景 纯数据结构 对象封装
cpp// ============================================================ // struct 默认 public // ============================================================ struct Person { int age; // 默认 public }; // ============================================================ // class 默认 private // ============================================================ class Person { int age; // 默认 private,外部不能访问 };二、union 联合体
1. union 与 struct 区别
对比项 union struct 内存分配 所有成员共享一块内存 所有成员内存叠加 大小 最大成员的大小 所有成员大小之和(+对齐) 修改影响 修改一个成员影响其他成员 成员独立,互不影响 同时使用 同一时间只能用一个成员 可以同时使用所有成员
cpp// ============================================================ // struct 内存布局 // ============================================================ struct StructTest { int a; // 4 字节 char b; // 1 字节 short c; // 2 字节 }; // 大小:8 字节(各自独立存储) // ============================================================ // union 内存布局 // ============================================================ union UnionTest { int a; // 4 字节 char b; // 1 字节(与 a 共享内存) short c; // 2 字节(与 a 共享内存) }; // 大小:4 字节(取最大成员) union u { int a; char b[4]; }; // 大小:4 字节,a 和 b 共享同一块内存2. union 判断大小端
cpp// ============================================================ // 大小端判断 // ============================================================ #include <stdio.h> union MyUnion { short t; // 2 字节 char b[2]; // 2 字节,与 t 共享内存 }; int main() { union MyUnion test; test.t = 0x0102; // 0x0102 = 258 // 大端:高字节(01)存低地址,低字节(02)存高地址 // 小端:低字节(02)存低地址,高字节(01)存高地址 if (test.b[0] == 0x01 && test.b[1] == 0x02) { printf("大端字节序\n"); printf("b[0]=%x, b[1]=%x\n", test.b[0], test.b[1]); } else if (test.b[0] == 0x02 && test.b[1] == 0x01) { printf("小端字节序\n"); printf("b[0]=%x, b[1]=%x\n", test.b[0], test.b[1]); } return 0; } // 大部分 PC 是小端,输出:小端字节序 \n b[0]=2, b[1]=13. 大小端图示
cpp大端字节序(网络字节序): ┌────────┬────────┐ │ 0x01 │ 0x02 │ │ 高字节 │ 低字节 │ └────────┴────────┘ 低地址 → 高地址 小端字节序(PC 常见): ┌────────┬────────┐ │ 0x02 │ 0x01 │ │ 低字节 │ 高字节 │ └────────┴────────┘ 低地址 → 高地址Q1:union 和 struct 的区别?
对比项 union struct 内存分配 所有成员共享一块内存 成员内存独立分配 大小 最大成员大小 所有成员大小之和 修改影响 一个成员会覆盖其他成员 互不影响 同时使用 只能用一个成员 可同时用所有成员 Q2:为什么 struct 需要字节对齐?
硬件访问对齐的数据效率更高,编译器会自动对齐以提高访问速度。
Q3:什么时候用 union?
当需要节省内存或需要同一块内存存放不同类型数据时,比如判断大小端、协议解析、类型转换等场景。
5.计算占用空间大小问题:
对于不同位的操作系统,个别数据类型数据大小不一样,
Long 和 unsigned long 在 32 位中是 4 个字节
在 64 位中是 8 个字节
计算的时候需要考虑字节对齐问题:
所占空间必须是成员变量中字节最大的整数倍
每个变量类型的偏移量必须是该变量类型的整数倍
对于联合体,由于所有变量都是共用一块内存,还需注意数组占用最大内
存
例如:
cppTypedef union {double I;int k[5];char c;} DATE;在联合体中成员变量最大为 double 为 8 个字节,所以最终大小必须是 8 的整数倍;又因为联合体是共占内存空间,即 int*5=20 字节,所以最终为 24 个字节。
cppTypedef struct data {int cat;DATE cow; double dog;}too;求 sizeof(too);
解:在结构体里面联合体为 24,联合体的最大类型为 8 字节,所以联合体 的起始位置必须满足偏移量为 8 的倍数,计算如下:
cppCat:1-4, DATE cow 8+24 Double dog 32+8=40结构体大小计算与字节对齐详解
一、基础概念
1. 不同操作系统的数据类型大小
数据类型 32位系统 64位系统 char1 字节 1 字节 short2 字节 2 字节 int4 字节 4 字节 long4 字节 8 字节 ⚠️ unsigned long4 字节 8 字节 ⚠️ float4 字节 4 字节 double8 字节 8 字节 指针4 字节 8 字节 注意:
long和unsigned long在 64 位系统下是 8 字节!2. 字节对齐规则
规则 说明 规则1 结构体总大小必须是最大成员大小的整数倍 规则2 每个成员的偏移量必须是该成员类型大小的整数倍 规则3 联合体大小 = 最大成员的大小(向上对齐到最大对齐值)
二、结构体大小计算示例
示例1:基础对齐
cppstruct Example1 { char a; // 1 字节,偏移量 0 int b; // 4 字节,偏移量必须为 4 的倍数 → 从 4 开始 char c; // 1 字节,偏移量 8 }; // 计算: // a: 0-1(1字节) // b: 4-8(4字节)← 中间空出 1-3(3字节填充) // c: 8-9(1字节) // 总大小:9,但必须对齐到最大成员(4)的倍数 → 12 // 实际占用:12 字节示例2:重排顺序优化
cpp// ❌ 浪费空间:12 字节 struct Bad { char a; // 1 字节 int b; // 4 字节(从 4 开始) char c; // 1 字节 }; // 大小:12 // ✅ 优化空间:8 字节 struct Good { int b; // 4 字节,偏移量 0 char a; // 1 字节,偏移量 4 char c; // 1 字节,偏移量 5 }; // 大小:8(对齐到最大成员 4 的倍数)三、联合体大小计算
cpp// ============================================================ // 联合体大小计算 // ============================================================ typedef union { double I; // 8 字节 int k[5]; // 4*5 = 20 字节 char c; // 1 字节 } DATE; // 计算过程: // 1. 最大成员:int k[5] = 20 字节 // 2. 最大对齐值:double = 8 字节 // 3. 总大小必须是 8 的整数倍 // 4. 20 向上对齐到 8 的倍数 → 24 // 所以:sizeof(DATE) = 24图示
cppunion DATE 内存布局(24 字节): ┌───────────────────────────────────────────────────────────────┐ │ double I 区域(8 字节) │ │ int k[5] 区域(20 字节) │ │ char c 区域(1 字节) │ └───────────────────────────────────────────────────────────────┘ 所有成员共享同一块内存,大小取最大成员(20),对齐到 8 → 24四、嵌套结构体计算
cpp// ============================================================ // 联合体 + 结构体嵌套 // ============================================================ typedef union { double I; // 8 字节 int k[5]; // 20 字节 char c; // 1 字节 } DATE; // 联合体大小:24(最大对齐 8) typedef struct data { int cat; // 4 字节 DATE cow; // 24 字节(联合体) double dog; // 8 字节 } too;联合体的计算公式:
cpp#include <stdio.h> // ============================================================ // 联合体定义 // ============================================================ union date { char a; // 1 字节 double b[3]; // 8 * 3 = 24 字节 char c; // 1 字节 }; typedef union date DATE; // ============================================================ // 计算过程 // ============================================================ // 1. 最大成员:double b[3] = 24 字节 // 2. 最大对齐值:double = 8 字节 // 3. 24 已经是 8 的倍数 → sizeof(DATE) = 24 // ============================================================ int main() { printf("sizeof(DATE) = %ld\n", sizeof(DATE)); // 24 return 0; }联合体(Union)大小计算
计算公式
联合体大小 = 最大成员大小(向上对齐到最大对齐值的整数倍)
规则说明
规则 说明 规则1 联合体大小至少能容纳最大的成员 规则2 联合体大小必须是其内部最大对齐值的整数倍 规则3 所有成员共享同一块内存,同一时间只能使用一个成员 结构体(Struct)大小计算
计算公式
结构体大小 = 所有成员大小之和 + 填充字节(对齐到最大对齐值的整数倍)
规则说明
规则 说明 规则1 结构体总大小必须是最大成员对齐值的整数倍 规则2 每个成员的起始偏移量 必须是其自身类型大小的整数倍 规则3 如果成员是联合体,联合体的起始偏移量必须满足联合体内部最大对齐值 规则4 结构体的最大对齐值是所有成员中最大的对齐值 示例代码
cpp#include <stdio.h> // ============================================================ // 结构体定义(包含联合体成员) // ============================================================ struct test { char a; // 1 字节,偏移量 0 DATE d; // 24 字节,偏移量 8(需要 8 的倍数) char b; // 1 字节,偏移量 32 char c; // 1 字节,偏移量 33 }; typedef struct test TEST; // ============================================================ // 计算过程 // ============================================================ // 1. char a → 偏移量 0,占用 1 字节 // 2. DATE d → 起始偏移量必须是 8 的倍数 // 当前偏移量 1 → 填充 7 字节 → 偏移量 8 // 占用 24 字节 → 偏移量 8-31 // 3. char b → 偏移量 32,占用 1 字节 // 4. char c → 偏移量 33,占用 1 字节 // 5. 当前总大小:34 字节 // 6. 最大对齐值:8(double) // 7. 对齐到 8 的倍数 → 34 → 40 // sizeof(TEST) = 40 // ============================================================ int main() { printf("sizeof(TEST) = %ld\n", sizeof(TEST)); // 40 return 0; }结构体内存布局
cpp┌─────────────────────────────────────────────────────────────────────┐ │ 偏移量 │ 0 │ 1-7 │ 8-31 │ 32 │ 33 │ 34-39 │ ├──────────┼─────┼───────┼────────┼──────┼──────┼─────────────────┤ │ 成员 │ a │ 填充 │ DATE │ b │ c │ 填充 │ │ 大小 │ 1 │ 7 │ 24 │ 1 │ 1 │ 6 │ └──────────┴─────┴───────┴────────┴──────┴──────┴─────────────────┘三、完整代码
cpp#include <stdio.h> // ============================================================ // 联合体定义 // ============================================================ union date { char a; // 1 字节 double b[3]; // 24 字节(8*3) char c; // 1 字节 }; typedef union date DATE; // sizeof(DATE) = 24 // ============================================================ // 结构体定义(包含联合体成员) // ============================================================ struct test { char a; // 1 字节,偏移量 0 DATE d; // 24 字节,偏移量 8(需要 8 的倍数) char b; // 1 字节,偏移量 32 char c; // 1 字节,偏移量 33 }; typedef struct test TEST; // sizeof(TEST) = 40 // ============================================================ // 主函数验证 // ============================================================ int main() { printf("sizeof(DATE) = %ld\n", sizeof(DATE)); // 24 printf("sizeof(TEST) = %ld\n", sizeof(TEST)); // 40 // 验证各成员偏移量 TEST t; printf("\n成员偏移量验证:\n"); printf("&t.a = %p\n", (void*)&t.a); printf("&t.d = %p\n", (void*)&t.d); printf("&t.b = %p\n", (void*)&t.b); printf("&t.c = %p\n", (void*)&t.c); // 计算偏移量 printf("\n偏移量(字节):\n"); printf("a 的偏移量:%ld\n", (char*)&t.a - (char*)&t); // 0 printf("d 的偏移量:%ld\n", (char*)&t.d - (char*)&t); // 8 printf("b 的偏移量:%ld\n", (char*)&t.b - (char*)&t); // 32 printf("c 的偏移量:%ld\n", (char*)&t.c - (char*)&t); // 33 return 0; }
嵌入式面经笔记
丰锋ff2026-08-23 13:04
相关推荐
CodeStats5 分钟前
【Java进程通信】Java进程通信系统完全指南:从ProcessBuilder底层原理到多语言实战绿智校园7 分钟前
物联网平台选型指南:为什么底层协议自研比云端SDK接入更可靠?T01156187 分钟前
艺培场馆课时预约小程序用户端 & 后台联动踩坑上篇(预约、课时、缓存、消息、学员端自身问题)长谷深风1119 分钟前
AI Agent 踩坑:盲目重试会把小故障炸成系统灾难金立基包装胶水11 分钟前
纸袋封口热封胶不粘是什么原因?chnyi6_ya21 分钟前
论文阅读笔记 | From Priors to Perception: Grounding Video-LLMs in Physical Reality自己的九又四分之三站台32 分钟前
C# 接入 RasterLite2:从原生 DLL 加载到 Coverage、Section、Tile 验证飞翔的火箭弹33 分钟前
企业网络资产管理用什么系统工具evans在进步34 分钟前
Spring Boot 核心机制详解:自动装配、事件监听、异步任务与请求映射码云骑士38 分钟前
116-Python调用GPT-4V分析图片-Base64编码-多图-JSON结构化输出















