首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >关于C11临时生存期规则和未定义行为的更多问题

关于C11临时生存期规则和未定义行为的更多问题
EN

Stack Overflow用户
提问于 2022-02-22 17:10:50
回答 1查看 64关注 0票数 0

我很难就另一个可能涉及临时寿命规则的C11 /ISO/IEC9899:2018的例子找到一个明确的答案,我在这里再次引用它是为了便于搜索:

具有结构或联合类型的非值表达式,其中结构或联合包含数组类型的成员(递归地包括所有包含的结构和联合的成员)引用具有自动存储持续时间和临时生存期的对象。36)当计算表达式时,其生存期开始,其初始值为表达式的值。其生存期在包含完整表达式的计算结束时结束。任何修改具有临时生存期的对象的尝试都会导致未定义的行为。具有临时生存期的对象的行为就像为了有效类型的目的使用其值的类型声明它一样。这样的对象不需要有唯一的地址。

首先,我不确定这条规则是否适用。例子是:

代码语言:javascript
运行
复制
#include <stdio.h>
#include <assert.h>

struct A {
    int b;
};

struct X {
    int x;
    struct A *a;
};

static void
foo(const char *n, struct X *x)
{
    assert(x != NULL);
    assert(x->a != NULL);
    printf("%s = {{ .x = %d, .a[0] = { .b = %d }}}\n", n, x->x, x->a->b);
}

#define FOO(x) foo(#x, x)

void
main(void) {
    struct X x1 = { .x = 11, .a = &(struct A){ .b = 21 }};
    struct X x2 = { .x = 12, .a = (struct A [2]){{ .b = 22 }, { .b = 23 }}};
    FOO(&x1);
    FOO(&x2);

    /* UB? */
    x1.a->b = 31;
    FOO(&x1);
    x2.a[0].b = 32;
    FOO(&x2);

    /* --- */

    struct X *p1 = &(struct X){ .x = 31, .a = &(struct A){ .b = 41 }};
    struct X *p2 = &(struct X){ .x = 32, .a = (struct A [2]){{ .b = 42 }, { .b = 43 }}};

    FOO(p1);
    FOO(p2);

    /* UB? */
    p1->a->b = 51;
    FOO(p1);
    p2->a[0].b = 52;
    FOO(p2);

    /* --- */

    struct A a[2] = {{ .b = 2 }, { .b = 3 }};
    struct X y = { .x = 1, .a = a};

    FOO(&y);

    /* should be legal */
    y.a->b = 4;
    FOO(&y);
}

我的问题:

  • 首先,我希望x1x2p1p2无疑是lvalue,并且在示例中修改y.a->b是合法的。对,是这样?
  • 但是.a成员在xp的情况下呢?当取消引用时,这是一个值吗?
  • 对于p6p7.a成员是否具有可变长度数组类型?如果是,是基于声明还是初始化?
  • 因此,x1.a->bx2.a->bp1->a->bp2->a->b的修改行为是否得到了明确的定义?

谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-22 17:50:07

首先,我希望x1、x2、p1和p2无疑是lvalue,并且示例中y.a->b的修改是合法的。对,是这样?

是。

但是x和p情况下的.a成员呢?当取消引用时,这是一个值吗?

是。x1.ax2.ap1->ap2->a指向的对象不是临时对象。它们是http://port70.net/%7Ensz/c/c11/n1570.html#6.5.2.5 6.5.2.5。它们是可写的值,它们的生存期直到退出封闭块,就像具有auto存储持续时间的普通局部变量一样。它们在你进入它们的每一个地方都是活着的。

您从6.2.4p8引用的段落与代码无关,代码不包含任何具有临时生存期的对象。临时值类似于返回类型为struct A的函数返回的值。例如:

代码语言:javascript
运行
复制
struct A blah(void) {
    struct A ret = { 47, NULL };
    return ret;
}

void other(void) {
    printf("%d\n", blah().x); // valid, prints 47
    blah().x = 15;            // error, blah().x not an lvalue
    int *ptr = &(blah().x);   // error, blah().x not an lvalue
}

blah()返回的对象不是lvalue,但是当结构包含数组成员时,您可以进入这样一个对象的地址获取的情况。这就是为什么关于生命和修改的语言是存在的。

代码语言:javascript
运行
复制
struct B {
    int arr[5];
};

struct B foobar(void) {
    struct B ret = { { 0,1,2,3,4 } };
    return ret;
}

void other(void) {
    printf("%d\n", foobar().arr[3]); // valid, prints 3;
    foobar().arr[2] = 7;             // compiles but is UB, temporary may not be modified
    int *ptr = foobar().arr + 2;     // valid but useless
    // lifetime of object returned by `foobar()` ends
    printf("%d\n", *ptr);            // UB, lifetime has ended
}

对于p6和p7,.a成员是否具有可变长度数组类型?如果是,是基于声明还是初始化?

不是的。.a成员只是有一个指针类型,struct A *。struct成员不能具有可变长度数组类型;这将是6.7.6p3下的可变修改类型,是禁止结构成员或联合成员使用的类型;请参见6.7.2.1p9和注123。

可变长度数组如下所示:

代码语言:javascript
运行
复制
void stuff(size_t n) {
    int vla[n];
}

(灵活数组成员6.7.2.1p18的概念与此略有关联,它实际上是结构末尾的数组成员,其大小取决于动态分配的额外空间的数量。但这是另外一个问题。)

因此,是否明确了x1.a->b,x2.a->b,p1->a->b和p2->a->b的修饰行为?

是的,完全正确。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71225472

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档