• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2026-08-17 22:58 Aet 隐藏边栏 |   抢沙发  1 
文章评分 1 次,平均分 5.0

所有权、错误处理与 goto cleanup

概述

  1. C 没有析构函数和异常
  2. 资源管理依靠接口约定与控制流完成

四种所有权关系

  1. 拥有 owned
    1. 当前代码拥有这块内存,必须最终调用 free(),或者明确转交给其他对象

  1. 借用 borrowed
    1. process() 通常只在调用期间借用 data,不能释放它,也不能在调用结束后继续保存指针,除非接口另有说明
    2. const 经常用于借用接口,但 const 本身并不表达生命周期

  1. 转移 transferred
    1. 如果 task_set_payload() 成功接管所有权,原调用者必须停止访问和释放该资源

  1. 共享 shared
    1. 多个对象共同引用同一资源时,需要额外机制:
    2. 明确谁最后释放
    3. 引用计数
    4. 父对象统一拥有
    5. 生命周期严格覆盖全部借用者
    6. 线程间同步

接口必须说明失败时的所有权

  1. 这个接口存在歧义
  2. 如果返回 false
    1. 队列是否已经释放 task
    2. 调用者是否仍拥有 task
    3. 任务是否可能已经部分入队

资源变量先初始化为无效值

  1. 这样所有失败路径都可以进入统一清理逻辑
  2. 注意

为什么Cgoto cleanup 是合理的

  1. 假设依次获得三种资源:
    1. 打开输入文件
    2. 打开输出文件
    3. 分配缓冲区
  2. 直接写多个提前返回
    1. 资源越多,每个失败分支重复的清理代码越多,也越容易漏掉

  1. 统一清理模式

  1. 这里的 goto
    1. 只向前跳
    2. 只有一个清理出口
    3. 不构造任意循环
    4. 不负责业务分支

文件复制示例

  1. 释放为什么是反序?

  1. 资源可能依赖较早获得的资源,因此反序释放是最稳定的通用规则,与 C++ 析构栈展开的顺序类似

清理函数应接受部分初始化状态

  1. 网络连接对象

  1. 销毁函数

  1. 创建时先建立安全不变量

  1. 这里 connection_destroy() 能处理:
    1. connection == NULL
    2. 缓冲区尚未分配
    3. Socket 尚未创建
    4. 全部初始化成功

输出参数必须先进入确定状态

  1. 创建函数常见接口

  1. 推荐规则

立即保存errno

  1. POSIX 系统调用失败时

  1. 为什么不能清理后再读取 errno
    1. 清理函数可能调用其他系统函数并覆盖 errno

  1. 还要注意
    1. 成功的函数通常不保证把 errno 清零
    2. 必须先检查函数返回值,只有确认失败后才读取 errno

  1. Windows 对应地应在失败后立即保存

异步任务的所有权交接

  1. 一个可靠的任务模型可以规定

  1. 约定:
    1. 入队成功:队列接管整个任务及 context
    2. 入队失败:调用者仍然拥有它们
    3. 执行完成:工作线程调用 destroy_context
    4. 任务取消:取消路径也必须调用 destroy_context
    5. 队列销毁:剩余任务全部执行或全部清理
  2. 提交端
    1. 如果提交成功后调用者仍然释放 task,就会造成 use-after-free
    2. 如果提交失败而双方都认为对方负责释放,就会造成泄漏

goto cleanup 的边界

  1. 适合:
    1. 一个函数中按顺序获取多个资源
    2. 多个失败点需要相同清理
    3. 只向函数末尾跳转
    4. 每个资源都有明确无效状态
  2. 不适合:
    1. 在业务逻辑中任意跳转
    2. 在多个标签之间来回跳
    3. 代替正常循环和函数拆分
    4. 跳入包含变长数组等特殊对象的作用域

未定义行为、严格别名、整数提升与 volatile

四类行为

类型 含义 示例
明确定义 标准规定唯一语义 无符号整数按模运算回绕
实现定义 编译器必须选择并记录一种行为 普通 char 是否有符号
未指定 多种结果都合法,不必记录选择 独立函数参数的求值顺序
未定义行为 标准不再约束程序行为 越界、悬空指针、除零、数据竞争
  1. 常见未定义行为

  1. “未定义”不表示只会得到一个随机值,还可能:
    1. 代码被优化器删除
    2. 条件判断被恒定折叠
    3. 相邻对象被破坏
    4. DebugRelease 表现不同
    5. 程序在修改前的代码处表现异常

有符号与无符号溢出不同

  1. 无符号整数按照模 2^N 回绕

  1. 如果 uint8_t 存在,它恰好是 8 位无符号整数,因此赋值转换结果按模 256 处理
  2. 有符号溢出则是未定义行为

  1. C23 提供 <stdckdint.h> 的检查算术功能

小整数通常先提升为 int

  1. 表达式中的 charunsigned charshortuint8_tuint16_t 等小整数类型,通常先进行整数提升

  1. 计算过程通常是:
    1. first 提升为 int,值为 250
    2. second 提升为 int,值为 10
    3. 使用 int 计算得到 260
    4. 转换回 uint8_t
    5. 最终得到 4

有符号数与无符号数比较

  1. 网络代码中的典型错误

普通 char 的符号由实现决定

  1. 不同平台中,byte 可能是-1,也可能是255
    1. 取决于普通 char 是有符号还是无符号
  2. 所以:
    1. 文本字符使用 char
    2. 原始字节使用 unsigned charuint8_t
    3. 不要用 char 保存协议长度或二进制标志

位移运算的陷阱

  1. 错误
    1. 左操作数 1 的类型是 int
    2. 在典型 32int 平台上,结果无法由 int 表示,会产生未定义行为

  1. 位移量也必须小于左操作数类型的位数

严格别名规则

  1. 编译器通常可以假设
    1. 指向互不兼容类型的指针,不会指向同一个对象

  1. 安全复制对象表示
    1. memcpy 通常会被编译器优化成普通寄存器操作,不必为了性能使用非法指针转换

什么是有效类型?

  1. 具有声明的普通对象,其有效类型通常就是声明类型:
    1. 应该通过 float 类型或允许的字符类型访问它

  1. malloc() 返回的存储没有声明类型

  1. 一般原则:
    1. 通过与对象兼容的类型访问
    2. 可以通过 char*signed char*unsigned char* 检查对象表示
    3. 不要把同一地址随意转换成不相关结构体或标量指针后解引用
    4. 类型转换只改变指针表达式,不会自动改变现有对象的真实类型

接收缓冲区不能直接转换成协议结构体

  1. 危险写法

  1. 可能同时违反多个条件:
    1. receive_buffer 可能还没有完整头部
    2. 地址可能没有满足 DataHeader 对齐
    3. 字节对象不一定能直接作为结构体对象访问
    4. 结构体存在填充
    5. 网络字节序与主机字节序不同
    6. 原始位模式可能不是字段类型的合法表示
    7. 缓冲区扩容后 header 变成悬空指针

未对齐访问

  1. buffer + 1 很可能不满足 uint32_t 的对齐要求
  2. 某些 CPU 能处理未对齐读取,另一些可能异常
    1. 即便硬件支持,C 语言层面仍可能是未定义行为
  3. 安全方法:
    1. 逐字节解码
    2. 或者 memcpy 到正确对齐的本地对象,再处理字节序

安全协议头解码示例

  1. 协议格式

volatile 到底保证什么?

  1. volatile 表示对该对象的访问具有可观察性,编译器不能像普通无副作用对象那样随意删除或合并这些访问
  2. 常见用途:
    1. 内存映射硬件寄存器
    2. 特定底层环境中的设备状态
    3. 信号处理器与普通代码间的 volatile sig_atomic_t
  3. 但不要简单理解成
    1. 每次一定直接访问物理内存或绕过 CPU 缓存
  4. volatile 不保证
    1. 原子性
    2. 线程安全
    3. happens-before
    4. CPU 内存屏障
    5. 缓存一致性时序
    6. 多个操作组成事务

  1. 硬件只读状态寄存器可能写成
    1. 程序不能通过该指针写入
    2. 每次读取仍然是 volatile 访问
    3. 外部硬件可能改变值

增量解析器

声明:本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享