复合字面量作为参数传递的生命周期是多久?

10

使用clang编译时没有警告。

typedef struct {
  int option;
  int value;
} someType;

someType *init(someType *ptr) {
  *ptr = (someType) {
    .option = ptr->option | ANOTHEROPT,
    .value = 1
  };

  return ptr;
}

int main()
{
  someType *typePtr = init( &(someType) {
    .option = SOMEOPT
  });
  // do something else with typePtr
}
  1. 这是合法的C语言吗?

  2. 如果是:那么复合字面量的生存期是多久?


1
如果你知道你正在使用的东西的名称,你是否尝试研究一下这个功能呢?对我来说,这两篇优秀的文章排名第一和第二:The New C: Compound Literals6.25 Compound Literals,它们都可以回答你的问题。 - Shafik Yaghmour
我已经阅读了那些(以及更多),但是没有在任何地方找到这个特定的例子。 - vestol
2个回答

10

在C99及以上版本中,这是有效的C语言代码。

C99 §6.5.2.5 复合字面值

复合字面值的值是由初始化列表初始化的未命名对象的值。如果复合字面值出现在函数体外,则该对象具有静态存储期;否则,它具有与封闭块关联的自动存储期。

在您的示例中,复合字面值具有自动存储期,这意味着其生命周期仅限于其所在的块内,即main()函数。

@Shafik Yaghmour 推荐阅读:

  1. 新的C语言:复合字面值
  2. GCC手册:6.25复合字面值

4

您好,我将按照标准进行翻译,并进行一些通俗化处理。

无论何时您看到类似以下的复合字面量:

struct S *s;
s = &(struct S){1};

你可以将其替换为:
struct S *s;
struct S __HIDDEN_NAME__ = {1};
s = &__HIDDEN_NAME__;

So:

main.c

#include <assert.h>

struct S {int i;};
/* static: lives for the entire program. */
struct S *s1 = &(struct S){1};
struct S *s2;
struct S *s3;
struct S *s4;

int f(struct S *s) {
    return s->i + 1;
}

int main() {
    /* Undefined behaviour: not defined yet.
     * GCC 10 -fsanitize=undefined -ggdb3 -O0 -std=c99 gives at runtime:
     * runtime error: member access within null pointer of type 'struct S' */
#if 0
    assert(f(s2) == 1);
#endif

    /* Lives inside main, and any function called from main. */
    s2 = &(struct S){1};
    /* Fine because now instantiated. */
    assert(f(s2) == 2);

    /* Only lives in this block. */
    {
        s3 = &(struct S){1};
        /* Fine. */
        assert(f(s3) == 2);
    }
    {
        /* On GCC 10 -O0, this replaces s3 above due to UB */
        s4 = &(struct S){2};
    }
    /* Undefined Behavior: lifetime has ended in previous block.
     * On GCC 10, ubsan does not notice it, and the assert fails
     * due to the s4 overwrite.*/
#if 0
    assert(s3->i == 1);
#endif
}

完整的编译命令:

gcc -fsanitize=undefined -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c

1
如果您更新示例,请为了清晰起见将变量重命名为s1(全局)、s2(主要)和s3(子块)。最后一条评论对我来说似乎不正确,因为我认为您想引用的是s3,它确实超出了范围,但它会引用s2(而s1被遮蔽)。 - Allan Wind
很好。你添加的s4案例很有趣,而且真的很令人惊讶,你所说的“替换s3”是什么意思?如果它有相同的名称(s3),我可以理解,但为什么两个不同的变量(s3和s4)之间会有任何交互呢?为什么要使用函数f而不是直接测试值?最后一个assert没有使用f(),不确定这是有意还是疏忽。 - Allan Wind
@AllanWind,s3和s4进入堆栈,在s3释放堆栈后,堆栈下降,然后s4进入相同的位置,O0非常愚蠢和可预测。在随机位置使用f以显示子调用的生命周期(因为在堆栈上),这有点显而易见。 - Ciro Santilli OurBigBook.com

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接