You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

N2346标准示例中将goto替换为迭代语句产生UB的原因是什么

问题核心:你混淆了自动存储对象的「生命周期边界」和「初始化求值时机」两个独立规则,标准条款之间没有矛盾,你的推导漏看了自动对象生命周期的终止规则。


1. 原始goto示例的合法逻辑

首先看6.5.2.5/15的goto示例,复合字面量(struct s){ j++ }的封闭块是整个f()的函数体块,根据N2346::6.5.2.5/5的规定,它对应的自动对象生命周期覆盖整个函数执行期:

  • 每次goto跳回again标签时,确实会按照N2346::6.8/3的规则,重新对复合字面量的初始化器求值、把新值写入同一个全局的无名对象,从头到尾只有这一个对象存在,所以p和q最终指向同一个地址,q->i == 1的访问是合法的,函数返回1符合标准要求。

2. 替换为迭代语句后触发UB的原因

如果把goto替换为迭代语句(比如for/while循环),代码结构会变成下面这样:

struct s { int i; };
int f (void)
{
    struct s *p = 0, *q;
    int j = 0;
    while (j < 2) { // 循环体是独立的内层块
        q = p, p = &((struct s){ j++ });
    }
    return p == q && q->i == 1;
}

此时复合字面量的封闭块变成了循环体的内层块,根据C标准对自动存储周期的基础规则N2346::6.2.4/6:进入块时为自动对象分配存储,退出块时释放存储,对象生命周期终止。
这时候执行流程会出现问题:

  1. 第一轮循环进入块,创建复合字面量对应的无名对象,p指向该对象
  2. 第一轮循环结束,退出循环体块,该无名对象的生命周期直接终止,p变成野指针(持有无效地址)
  3. 第二轮循环进入块,你首先把野指针p赋值给q,哪怕第二轮新创建的复合字面量对象刚好和之前的地址相同,你访问q->i也是对已经失效的对象的解引用,直接触发未定义行为。

3. 你的推导错误点

你提到的N2346::6.8/3规则完全正确,迭代场景下每轮循环确实会重新对初始化器求值、创建新的无名对象,但这个规则只保证当前轮次的对象是合法的,不影响上一轮对象生命周期结束后指针失效的事实:你持有了已经销毁的对象的指针,再解引用这个指针的行为和新对象是否创建没有任何关系,自然触发UB。

内容的提问来源于stack exchange,提问作者Some Name

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 15:00:05