首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >只管去面(20) c语言奇淫技巧

只管去面(20) c语言奇淫技巧

作者头像
早起的鸟儿有虫吃
发布2026-03-04 20:33:37
发布2026-03-04 20:33:37
820
举报

本文不是让你成为c/c++专家, 而是让你成为c/c+面试专家

Q1:gdb程序调试

背景

程序调试:添加打印log日志方式 确实一个很好方式 但是 一个大型项目,修改一行代码,编译1个小时,开发自测阶段 这个不是浪费你宝贵时间呀?因此必须了解gdb调试最基本技巧

任务:使用gdb 方式直接运行一个服务 带参数服务

最少知识

方法 1:启动 GDB 时直接传递参数(推荐)

使用 gdb --args 前缀,将程序路径所有参数一起传入,确保参数被正确识别。

代码语言:javascript
复制
gdb --args ./test -N 4 -c x

方法 2:先启动 GDB,再设置参数

先进入 GDB 调试环境,再通过 set args 命令设置参数,最后用 run 运行。

步骤:

1

启动 GDB 并加载程序:

代码语言:javascript
复制
gdb ./test

2

在 GDB 内部设置参数:

代码语言:javascript
复制
(gdb) set args -N 4 -c x

3

设置断点

代码语言:javascript
复制
(gdb) run

Q2 : git 如何获取远程分支代码

git fetch origin

git fetch是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。

git pull 则是将远程主机的最新内容拉下来后直接合并, 即:git pull = git fetch + git merge,这样可能会产生冲突,需要手动解决。

git merge origin branch_wangchuanyi

git-merge - Join two or more development histories together

代码语言:javascript
复制
    A---B---C topic
         /
    D---E---F---G master

         A---B---C topic
         /         \
    D---E---F---G---H master

创建本地分支

git checkout -b branch_wangchuanyi origin/money/branch_wangchuanyi

git push origin branch_wangchuanyi:money/branch_wangchuanyi

how-to-push-a-local-branch-to-a-remote-repository-in-git

git add -u

git clean -dfx

git clean -dfx -d:删除未跟踪的目录 -f:强制删除(force) -x:删除所有未跟踪的文件,包括被.gitignore忽略的文件

Q3 系统升级过程中 ,RPC接口调用过程中 发现类大小不一样导致 类字段解析错误?IDL + 序列化 怎么解决的

问题发生的背景

同一个RPC消息, 消息类型没有发生变化,消息结构没有发生变化,为什么?最后sizeof 计算类大小不一样呢?

最少知识 sizeof(class)
代码语言:javascript
复制
取消所有填充字节,结构体大小 = 所有成员大小之和(最紧凑)
代码语言:javascript
复制
// 强制按1字节对齐(无填充字节) #pragma pack(1)  struct Test  { 	 char a; // 1字节 	 int b; // 4字节(紧跟a,无填充)  };  #pragma pack() // 恢复默认对齐 // 大小:1+4=5字节(默认对齐下是8字节:1+3填充+4)
终极方案:序列化解耦(放弃依赖 sizeof)

高端开源 RPC 框架(gRPC、Protobuf)彻底不用「结构体内存镜像传输」, 而是通过 IDL 文件(如.proto)定义消息,编译后生成序列化 / 反序列化代码 —— 此时sizeof无关紧要,传输的是 “编码后的字节流”,完全规避对齐差异。

疑问:IDL + 序列化 和直接序列化区别是什么?

一、先搞懂:传统「内存镜像传输」为什么会出问题?

你之前遇到的「结构体 sizeof 不一致、对齐差异」,本质是因为 RPC 消息传输用了「直接拷贝结构体在内存中的原样」(也就是「内存镜像」),这个过程就像:

把你房间里的家具(结构体成员)原样搬到另一个房子(目标平台),家具之间的间隙(编译器插入的填充字节)、摆放位置(对齐规则)完全依赖房子的户型(编译器/平台)—— 户型变了(比如 MSVC 换成 GCC),间隙大小就变,家具占的总空间(sizeof)也变,搬过去就会乱套。

举个具体例子

你定义了一个 RPC 消息结构体:

代码语言:javascript
复制
struct UserMsg {    char name;  // 1字节    int age;    // 4字节};

在 GCC 下,编译器会在 name 后插 3 字节填充(对齐要求),sizeof(UserMsg)=8

在 MSVC 下,若开了 pack(4),可能只插 0 字节填充,sizeof(UserMsg)=5

如果直接把这个结构体的「内存镜像」(8 字节/5 字节)通过网络传给对方,对方按自己的 sizeof 解析,就会把填充字节当成有效数据,导致解析错误(比如 age 读成乱码)。

二、「序列化解耦」的核心:放弃搬“房间原样”,只搬“家具零件+组装清单”

gRPC/Protobuf 这类框架的思路是:不直接传内存里的结构体,而是先把结构体的「数据值」按「统一规则」拆成纯字节流(序列化),接收方再按同一规则还原(反序列化)

这个过程就像:

把房间里的家具(结构体成员)拆成标准化零件(只保留有效数据,去掉所有填充字节),按「组装清单」(IDL 定义的规则)打包成快递(字节流);接收方收到快递后,按同一清单把零件组装成家具—— 不管接收方的房子(编译器/平台)户型如何,只要清单一致,组装出的家具(数据)就完全一样,和“间隙(填充)”“户型(对齐)”无关。

三、Protobuf 实战:用 IDL 定义规则,序列化传输

我们用 Protobuf 举一个最简化的例子,你就能直观理解:

步骤1:写 IDL 文件(统一的“组装清单”)

创建 user_msg.proto 文件,只定义「数据是什么、是什么类型」,完全不涉及内存布局:

代码语言:javascript
复制
// 版本声明syntax = "proto3";// 定义消息(对应你的结构体)message UserMsg {  string name = 1;  // 字段1:字符串类型(对应char name)  int32 age = 2;    // 字段2:32位整数(对应int age)}

这个文件就是「跨平台/编译器的统一规则」—— 不管是 MSVC、GCC 还是 Clang,不管是 32 位还是 64 位,所有人都按这个清单来处理数据。

步骤2:编译 IDL 文件(生成序列化/反序列化代码)

用 Protobuf 编译器 protoc.proto 文件编译成你需要的语言代码(C++/Java/Go 等):

代码语言:javascript
复制
# 编译成C++代码protoc --cpp_out=. user_msg.proto

编译后会生成 user_msg.pb.huser_msg.pb.cc,里面包含:

对应 UserMsg 的类(替代你手写的结构体);

自动生成的 SerializeToString()(序列化)、ParseFromString()(反序列化)函数。

步骤3:序列化传输(只传有效数据,无填充)

发送方代码(不管什么编译器,逻辑都一样):

代码语言:javascript
复制
// 1. 创建消息对象,赋值(只关心数据值,不关心内存布局)UserMsg msg;msg.set_name("Tom");  // 设值:字符串msg.set_age(20);      // 设值:整数// 2. 序列化:把数据转成纯字节流(无任何填充字节)std::string byte_stream;msg.SerializeToString(&byte_stream);// 3. 传输字节流(RPC框架负责把byte_stream发出去)rpc_client.Send(byte_stream);

这个 byte_stream只有「Tom」和「20」的有效数据,没有任何编译器插入的填充字节,大小完全由数据本身决定,和 sizeof、对齐规则无关。

步骤4:反序列化还原(按同一规则组装)

接收方代码(不管什么编译器,逻辑都一样):

代码语言:javascript
复制
// 1. 接收字节流std::string received_byte_stream = rpc_server.Receive();// 2. 反序列化:按.proto规则还原数据UserMsg msg;msg.ParseFromString(received_byte_stream);// 3. 读取数据(结果和发送方完全一致)std::cout << msg.name() << ", " << msg.age() << std::endl;// 输出:Tom, 20

四、为什么序列化能彻底规避对齐/sizeof问题?

核心原因就3点,也是开源 RPC 框架选择这种方案的本质:

1

只传输「数据值」,不传输「内存布局」:序列化后的字节流只包含有效数据(比如字符、数字),完全剔除了编译器插入的填充字节,自然不存在「填充多少、对齐规则」的差异;

2

IDL 保证「规则统一」.proto 文件是跨编译器/平台的“统一契约”,不管发送方用 MSVC、接收方用 GCC,都按同一个规则解析数据,和 #pragma pack、sizeof 无关;

3

编译生成的代码「适配底层」:Protobuf 自动生成的序列化/反序列化代码,已经帮你处理了所有编译器/平台的细节差异(比如大小端、类型长度),你不用关心底层。

总结

传统方式(内存镜像)

序列化解耦(Protobuf/gRPC)

传输「内存里的结构体原样」,包含填充字节

传输「纯有效数据的字节流」,无填充

依赖编译器对齐规则、sizeof

只依赖IDL定义的统一规则

不同编译器/平台易出解析错误

跨平台/编译器100%兼容

Q4 IDL + 序列化直接序列化 的根本区别是什么?

有没有一份跨语言、跨平台的“公共契约”

一、直观比喻

直接序列化: 你用母语写了一份便签(内存对象),然后用自己发明的符号加密成一段文字(字节流),对方只有也是说母语、懂你加密规则的人才能读懂。 → 只能在同一语言/同一运行时环境内使用

IDL + 序列化: 你和对方先约定用“世界语”写合同(IDL 文件),然后把合同内容翻译成各国语言(生成各语言的代码),通信时双方都按合同拆装箱(序列化/反序列化)。 → 跨语言、跨平台、长期演进无压力


二、核心区别清单

维度

直接序列化

IDL + 序列化

跨语言能力

❌ 几乎不行(如 Java 的 Serializable 只能 Java 反序列化)

✅ 天生支持(Protobuf/Thrift 等可生成 Java/C++/Go/Python…)

跨平台/编译器

❌ 依赖内存布局,对齐/大小端差异易出错

✅ 二进制格式固定,与平台/编译器完全无关

版本兼容性

❌ 字段增删几乎必挂(新旧版本不兼容)

✅ 通过字段编号/optional 设计,向后向前兼容

契约文档

❌ 无显式契约,反序列化靠硬编码

✅ .proto 文件即是契约,可读、可校验、可生成文档

性能

⚠️ 依赖反射,通常较慢(如 Java 反射、Python pickle)

✅ 代码生成,零反射/少反射,常带 Varint 等优化

安全风险

⚠️ 反序列化可能执行任意代码(如 Java 反序列化漏洞)

✅ 只解析已知字段,天然防御大部分反序列化攻击

数据体积

⚠️ 常包含类型元数据、类结构信息,臃肿

✅ 仅存编号+值,紧凑(尤其整数用 Varint)

使用场景

本地缓存、同语言进程间通信

微服务 RPC、异构系统对接、长期存储数据


三、典型例子对比

直接序列化

代码语言:javascript
复制
// Java 原生序列化
UserMsg msg = new UserMsg("Tom", 20);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(msg);
byte[] data = baos.toByteArray();   // ❌ 只有 Java 能读

包含类名、版本号、字段描述等大量元信息。

换个语言(Go/C++)直接无法解析。

IDL + 序列化(Protobuf)

代码语言:javascript
复制
// C++ 序列化
UserMsg msg;
msg.set_name("Tom");
msg.set_age(20);
std::string data = msg.SerializeAsString(); // ✅ 任何语言都能解析

// Python 反序列化
import user_msg_pb2
msg = user_msg_pb2.UserMsg()
msg.ParseFromString(data)   // 拿到 "Tom", 20

数据格式纯粹,只有字段编号+值。

IDL 描述明确,谁都能生成代码。

四、为什么需要 IDL?

IDL,Interface description language,即接口描述语言。

IDL是一种很有用的工具,它提供了对接口的描述,约定了接口协议。‘使得通讯双方通讯时,无需再发送 scheme,有效提高了通讯数据的荷载比。

首先,依据 IDL 生成对应的客户端和接口模块,这个本质是编译。

直接序列化 本质上把“数据的形状”(Schema)隐藏在了代码和二进制流里,别人没法独立还原。

IDL + 序列化 把 Schema 显式写出来,成为团队/跨团队、跨语言、跨时间的“共同约定”——这才是分布式系统里最宝贵的资产。

Q5 代码规范 4空格对齐?

在window平台 source insight 显示是对齐的为到了 ,liunx平台 4空格变成8空格 set list

source insight 4.0对齐提示设置

第一手资料

[1]how-to-pass-command-line-arguments-to-gdb-in-a-linux-environment

https://visualgdb.com/gdbreference/commands/set_args set args [Arguments] show args Arguments Specifies the default command-line arguments that will be passed to the program.

https://www.geeksforgeeks.org/linux-unix/how-to-pass-command-line-arguments-to-gdb-in-a-linux-environment/

祝:

不要独自一个人看手机,

我们常常陷入这样的场景: 独自一人时,在餐厅、地铁、卧室、沙发或书桌前, 当你躺在那里,趴在哪里,做在哪里时候,身体固定狭小空间,无法互动 ,不自觉地掏出手机。 身体被困在狭小的物理空间里,无法动弹, 只能目光便只能被那方寸屏幕牢牢吸引, 你行为被 多巴胺诱惑,简单舒服即使反馈奖励 ,被平台设计各种陷阱控制

除非拥有极强的意志力,根本不选择痛苦迟到的奖励

与其对抗本能,甚至平台 不如改变环境。 请选择去户外,去操场,视眼开阔 看手机。 请主动为你的手机使用选择更健康的场景

核心行动准则1:为特定场景设立无手机时间

进入公司开始工作时

下班回到家中时

在餐厅用餐或社交时

乘坐地铁通勤时

行动建议:

在上述场景开始时,立刻将手机放入书包或固定在某个位置(如抽屉)。

给自己设定一个专注时限,例如至少接下来的3小时内不主动查看

这能有效打破“无聊就刷手机”的循环,把注意力还给当下的人和事。

核心行动准则2:换个开阔的地方看手机

早晨起床后

下班之后

周末时光 行动建议:

可以选择去图书馆、咖啡馆、商场中庭或景点休息区,公司园区,马路边

在这些具有公共生活感的场所使用手机,

周围的环境流动能天然地分散你对屏幕的过度专注,避免陷入无休止的刷屏。

一句话描述:

普通人最简单方式,重启自己操作系统

固定21点入睡:1 R90睡眠方案之所以能这样的世界顶尖运动员所青睐,每天晚上的睡眠规律你可以

固定6点起床:2 成不了作家 你可以打开笔记本写一行文字,3 做不出产品产品你打开软件写一行代码,4 无法演讲信服的话,你自己说一句话。5 成不运动健身达人 你走到运动走一步

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-02-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端开发成长指南 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Q1:gdb程序调试
    • 背景
    • 最少知识
      • 方法 1:启动 GDB 时直接传递参数(推荐)
    • 方法 2:先启动 GDB,再设置参数
      • 步骤:
  • Q2 : git 如何获取远程分支代码
  • Q3 系统升级过程中 ,RPC接口调用过程中 发现类大小不一样导致 类字段解析错误?IDL + 序列化 怎么解决的
    • 问题发生的背景
      • 最少知识 sizeof(class)
      • 终极方案:序列化解耦(放弃依赖 sizeof)
      • 步骤1:写 IDL 文件(统一的“组装清单”)
      • 步骤2:编译 IDL 文件(生成序列化/反序列化代码)
      • 步骤3:序列化传输(只传有效数据,无填充)
      • 步骤4:反序列化还原(按同一规则组装)
    • 四、为什么序列化能彻底规避对齐/sizeof问题?
    • 总结
  • Q4 IDL + 序列化 和 直接序列化 的根本区别是什么?
    • 一、直观比喻
    • 二、核心区别清单
    • 三、典型例子对比
    • 直接序列化
    • IDL + 序列化(Protobuf)
      • 四、为什么需要 IDL?
  • Q5 代码规范 4空格对齐?
  • 第一手资料
    • [1]how-to-pass-command-line-arguments-to-gdb-in-a-linux-environment
  • 祝:
    • 核心行动准则1:为特定场景设立无手机时间
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档