我很难就另一个可能涉及临时寿命规则的C11 /ISO/IEC9899:2018的例子找到一个明确的答案,我在这里再次引用它是为了便于搜索:
具有结构或联合类型的非值表达式,其中结构或联合包含数组类型的成员(递归地包括所有包含的结构和联合的成员)引用具有自动存储持续时间和临时生存期的对象。36)当计算表达式时,其生存期开始,其初始值为表达式的值。其生存期在包含完整表达式的计算结束时结束。任何修改具有临时生存期的对象的尝试都会导致未定义的行为。具有临时生存期的对象的行为就像为了有效类型的目的使用其值的类型声明它一样。这样的对象不需要有唯一的地址。
首先,我不确定这条规则是否适用。例子是:
#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);
}
我的问题:
x1
、x2
、p1
和p2
无疑是lvalue,并且在示例中修改y.a->b
是合法的。对,是这样?.a
成员在x
和p
的情况下呢?当取消引用时,这是一个值吗?.a
成员是否具有可变长度数组类型?如果是,是基于声明还是初始化?x1.a->b
、x2.a->b
、p1->a->b
和p2->a->b
的修改行为是否得到了明确的定义?谢谢!
发布于 2022-02-22 17:50:07
首先,我希望x1、x2、p1和p2无疑是lvalue,并且示例中y.a->b的修改是合法的。对,是这样?
是。
但是x和p情况下的.a成员呢?当取消引用时,这是一个值吗?
是。x1.a
、x2.a
、p1->a
和p2->a
指向的对象不是临时对象。它们是http://port70.net/%7Ensz/c/c11/n1570.html#6.5.2.5 6.5.2.5。它们是可写的值,它们的生存期直到退出封闭块,就像具有auto
存储持续时间的普通局部变量一样。它们在你进入它们的每一个地方都是活着的。
您从6.2.4p8引用的段落与代码无关,代码不包含任何具有临时生存期的对象。临时值类似于返回类型为struct A
的函数返回的值。例如:
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,但是当结构包含数组成员时,您可以进入这样一个对象的地址获取的情况。这就是为什么关于生命和修改的语言是存在的。
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。
可变长度数组如下所示:
void stuff(size_t n) {
int vla[n];
}
(灵活数组成员6.7.2.1p18的概念与此略有关联,它实际上是结构末尾的数组成员,其大小取决于动态分配的额外空间的数量。但这是另外一个问题。)
因此,是否明确了x1.a->b,x2.a->b,p1->a->b和p2->a->b的修饰行为?
是的,完全正确。
https://stackoverflow.com/questions/71225472
复制相似问题