【问题标题】:What is the lifetime of compound literals passed as arguments?作为参数传递的复合文字的生命周期是多少?
【发布时间】:2014-02-19 13:53:28
【问题描述】:

使用 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 和 2:The New C: Compound Literals6.25 Compound Literals,它们都回答了您的问题。
  • 我已经阅读了那些(以及更多),但在任何地方都没有找到这个具体的例子。

标签: c struct compound-literals


【解决方案1】:

是C99或以上的有效C。

C99 §6.5.2.5 复合文字

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

在您的示例中,复合文字具有自动存储功能,这意味着它的生命周期在其块内,即它所在的 main() 函数。

@Shafik Yaghmour 推荐阅读:

  1. The New C: Compound Literals
  2. GCC Manual: 6.25 Compound Literals

【讨论】:

    【解决方案2】:

    于浩已经用标准回答了,现在有些庸俗了。

    每当你看到这样的复合文字时:

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

    您可以将其替换为:

    struct S *s;
    struct S __HIDDEN_NAME__ = {1};
    s = &__HIDDEN_NAME__;
    

    所以:

    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
    

    【讨论】:

    • 如果您更新了示例,为清楚起见,请将变量重命名为 s1(全局)、s2(主)和 s3(子块)。最后一条评论对我来说似乎不正确,因为我认为您的意思是指确实超出范围的 s3,但它会引用 s2(并且 s1 被遮蔽)。
    • 美丽。您添加的 s4 案例很有趣,而且非常令人惊讶,“替换 s3”是什么意思?如果它具有相同的名称(s3),我可以看到它,但为什么两个不同的变量(s3 和 s4)之间会有任何交互?为什么要使用函数 f 而不是直接测试值?最后一个断言没有使用 f(),不确定这是故意还是疏忽。
    • @AllanWind s3 和 s4 进入堆栈,在 s3 释放堆栈后,s4 进入同一位置,O0 真的很愚蠢且可预测。 f 用于随机位置以显示子调用的生命周期保持不变(很明显,因为在堆栈上)。
    猜你喜欢
    • 2021-06-28
    • 2019-10-09
    • 1970-01-01
    • 2016-09-29
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多