setjmp是否真正保存程序状态?longjmp内存访问与参数含义问题
问题解答
先说明示例代码存在的编译错误
你给出的示例代码存在多处语法问题,无法直接编译运行:
- 结构体指针声明错误:
struct *str写法不合法,正确写法为struct str *str,缺少结构体标签str malloc参数错误:sizeof(str)计算的是指针本身的长度(32位系统为4字节,64位系统为8字节),而非目标结构体的大小,正确传参应为sizeof(struct str)- 数组成员赋值错误:
(*str).s是长度为200的char数组,不能直接用=赋值字符串常量指针,需要用strcpy拷贝字符串,或者将结构体的s成员改为char *类型 - 逻辑上存在不可达代码:
longjmp调用后会直接跳转回setjmp保存的执行点,longjmp后续的代码永远不会被执行。
free后通过longjmp跳回保存点能否访问原内存?
这是C标准明确规定的未定义行为,没有任何确定性结果。setjmp/longjmp的核心作用是保存/恢复寄存器上下文、栈帧指针,完全不会干预堆内存的分配状态:当你调用free释放内存后,这块内存的所有权就已经交还给堆管理器,堆管理器可以随时将这块内存重新分配给其他申请请求、或者将对应内存页归还给操作系统,这个状态不会因为longjmp跳转发生任何回滚。
此时访问这块已释放的内存可能出现任意结果:
- 若释放后内存未被覆写、也未被系统回收,可能暂时读出原有值,看起来和正常访问无异
- 若内存已被堆管理器标记为空闲,写入操作会破坏堆元数据,后续内存操作会直接触发崩溃
- 若内存已被重新分配给其他代码,读出的是其他逻辑写入的乱值;若对应内存页已被系统回收,会直接触发段错误。
代码中第二个printf的输出?
你标注的第二个printf位于longjmp调用之后,属于永远不会执行的不可达代码,不存在输出。
修正语法错误后代码的实际执行流程是无限循环:
- 初次执行到
setjmp时返回0,执行第一个printf输出正常的字符串和数值,随后调用free释放内存 - 调用
longjmp跳转回setjmp位置,此时setjmp返回longjmp传入的第二个参数值,再次往下执行第一个printf——此时str已经是野指针,访问已释放内存触发未定义行为,可能输出乱码、触发崩溃,也可能暂时输出原值 - 执行流会再次走到
free(str),对已经释放的指针重复free(double free),大概率直接触发堆保护机制崩溃。
longjmp第二个参数的含义
该参数是跳转后setjmp的返回值,遵循两个规则:
- 如果传入值为0,
setjmp实际返回值会被强制设为1——因为setjmp初次调用时固定返回0,用来标记“刚完成上下文保存、首次执行后续代码”的场景,强制非0返回是为了避免和初次返回的场景混淆 - 如果传入非0值,
setjmp就直接返回这个传入的值。
实际开发中传入不同的整数值(包括较大的数值),作用是做跳转来源标记:如果同一段代码里有多个不同的longjmp调用点(比如不同的错误处理分支),跳转时传入不同的非0值,setjmp返回后就可以通过判断返回值,确认是从哪个分支跳转回来的,对应执行不同的处理逻辑,数值大小本身没有特殊含义,只要是非0值都可以生效。
内容的提问来源于stack exchange,提问作者dfmaaa1
相关产品推荐
相关产品推荐

