【问题标题】:Lifetime of referenced compound array literals引用的复合数组文字的生命周期
【发布时间】:2013-02-19 10:28:22
【问题描述】:

我最近才知道我实际上可以在 C 中使用对复合文字数组的引用,我觉得这很有用,但我不太明白它是如何工作的。

例如,假设我使用该功能来避免必须为调用某个我不关心返回名称长度的套接字接口函数声明一个变量,如下所示:

int sockfamily(int fd)
{
    struct sockaddr_storage ss;

    getpeername(fd, (struct sockaddr *)&ss, (socklen_t [1]){sizeof(ss)});
    return(ss.ss_family);
} 

显然,sizeof(ss) 需要实际存储在堆栈中,以便将指向它的指针传递给 getpeername,因此必须为此目的分配和保留堆栈上的空间,但什么是这个分配的生命周期?我可以相信它会保持分配状态多久?

查看 GCC 的汇编输出,我观察到如果我将调用 getpeername 放在一个循环中,分配不会在循环的多次迭代中继续存在,但是还有什么其他条件可能会导致它不复存在了?

【问题讨论】:

标签: c compound-literals


【解决方案1】:

在函数中定义的复合文字具有与包含它的块关联的自动生命周期(即与在同一级别上声明的变量具有相同的生命周期)。这在标准第 6.5.2.5p5 段中有所规定。

int f() {
    for (int i = 0; i < 10; ++i) {
        int *j = (int []){i};  // storage duration of loop body
    }
} 

这实质上意味着复合文字等价于在同一范围内声明和初始化的变量:

int f() {
    for (int i = 0; i < 10; ++i) {
        int __unnamed[] = {i};
        int *j = __unnamed;
    }
} 

如果在任何地方传递复合文字,它们的指针可能会持续到它们的生命周期,请注意:

int f() {
    int *p;
    if (1) {
        p = (int []){0, 1, 2};
        assert(p[0] == 0);
    }
    // *p is undefined
}

【讨论】:

  • 感谢参考标准!这很有帮助。
  • 鉴于void blah(void) { struct foo *f; ... foo = somefunc(); if (foo==NULL) f=(struct foo*){data}; ...} 之类的内容,是否有任何干净的方法可以让if 控制多个语句?
  • @supercat 如果我理解正确,您可以尝试类似 struct foo *fallback[] = {data}; if (f==NULL) f=fallback; 的内容 - 但您不需要复合文字。
  • @ecatmur:这就是我的观点。我认为在不引入语义困难的情况下,没有办法让复合文字表现为左值,except 在可以(并且已经)制作const static 的情况下。我想知道如果更改标准以使复合文字将是const static lvalues 如果它们的所有成员都是常量,并且可以被视为rvalues [允许编译器具有后一种情况,那么会破坏多少真实世界的代码产生临时左值,但不推荐使用这种用途]?
  • @ecatmur:对于类型不包含 VLA,但值是非常量的场景,我可以看到让复合文字成为左值(出于生命周期的目的)的一些用处提升到函数范围,但让它们成为生命周期较短的左值在我看来是最糟糕的解决方案。能够在宏中定义除字符串以外的 const static 项将表示无法以其他方式获得的语义,并且对于编译器来说应该不难(处理基本上等同于字符串文字的处理)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-29
  • 2019-12-18
  • 1970-01-01
  • 2016-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多