有关 C11 临时生命周期规则和未定义行为的更多问题
我正在努力寻找可能与 相关的另一个示例的明确答案来自 C11 / ISO/IEC 9899: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
毫无疑问是左值,并且示例中ya->b
的修改是合法的。正确的? - 但是
x
和p
情况下的.a
成员又如何呢?取消引用时它是左值吗? - 关于 p6 和 p7,
.a
成员是否具有可变长度数组类型?如果是,是基于声明还是初始化? - 因此,修改
x1.a->b
、x2.a->b
、p1->a-> 的行为;b
和p2->a->b
定义良好吗?
谢谢你!
I am struggling to find a definitive answer on yet another example possibly concerning the temporary lifetime rule from C11 / ISO/IEC 9899:2018, which I am quoting here again to facilitate searches:
A non-lvalue expression with structure or union type, where the structure or union contains a
member with array type (including, recursively, members of all contained structures and unions)
refers to an object with automatic storage duration and temporary lifetime. 36) Its lifetime begins
when the expression is evaluated and its initial value is the value of the expression. Its lifetime ends
when the evaluation of the containing full expression ends. Any attempt to modify an object with
temporary lifetime results in undefined behavior. An object with temporary lifetime behaves as if it
were declared with the type of its value for the purposes of effective type. Such an object need not
have a unique address.
To begin with, I am not sure if the rule even applies. The example is:
#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);
}
My questions:
- To start with the basics, I hope that
x1
,x2
,p1
andp2
indisputably are lvalues and that the modification ofy.a->b
in the example is legal. Correct? - But what about the
.a
member in thex
andp
cases? Is it an lvalue when dereferenced? - With respect to p6 and p7, does the
.a
member have a variable length array type? If yes, is that based on the declaration or the initialization? - And, consequently, is the behavior of modifications of
x1.a->b
,x2.a->b
,p1->a->b
andp2->a->b
well defined?
Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
是的。
是的。
x1.a
、x2.a
、p1->a
和p2->a
的对象> 指向的不是临时的。它们是复合文字 [6.5.2.5]。它们是可写的左值,它们的生命周期直到从封闭块退出为止,就像具有auto
存储持续时间的普通局部变量一样。它们在您访问它们的每个点上都是活跃的。您从 6.2.4p8 引用的段落与您的代码无关,该代码不包含任何具有临时生命周期的对象。临时值类似于返回类型为 struct A 的函数返回的值。例如:
blah()返回的对象不是左值,但是当结构体包含数组成员时,您可能会遇到获取此类对象地址的情况。这就是为什么存在有关生命周期和修改的语言。
不。
.a
成员仅具有一个指针类型,struct A *
。结构成员不能具有可变长度数组类型;这将是 6.7.6p3 下的可变修改类型,并且对于结构或联合成员是禁止的;请参阅 6.7.2.1p9 和注释 123。可变长度数组类似于:(
灵活数组成员有一个稍微相关的概念,6.7.2.1p18,它实际上是位于结构的末尾,其大小由动态分配的附加空间量决定,但这是一个单独的问题。)
是的,绝对是。
Yes.
Yes. The objects which
x1.a
,x2.a
,p1->a
, andp2->a
point to are not temporaries. They are compound literals [6.5.2.5]. They are lvalues that are writable, and their lifetime is until exit from the enclosing block, just like an ordinary local variable withauto
storage duration. They are alive at every point where you access them.The passage you quoted from 6.2.4p8 is irrelevant to your code, which does not contain any objects with temporary lifetime. A temporary would be something like the value returned by a function with return type
struct A
. For instance:The object returned by
blah()
is not an lvalue, but you can get into a situation of taking the address of such an object when the struct contains an array member. This is why the language about lifetime and modification is there.No. The
.a
member simply has a pointer type,struct A *
. A struct member cannot have a variable length array type; that would be a variably modified type under 6.7.6p3, and that is forbidden for a struct or union member; see 6.7.2.1p9 and note 123.A variable length array is something like:
(There is the slightly related concept of a flexible array member, 6.7.2.1p18, which is effectively an array member at the end of a struct, whose size is determined by the amount of additional space you dynamically allocate. But that's a separate question.)
Yes, absolutely.