我正在努力尋找另一個可能與 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是合法的。正確的?p1p2y.a->b - 但是和案件
.a中的成員呢?取消參考時它是左值嗎?xp - 關于p6和p7,
.a成員是否具有可變長度陣列型別?如果是,那是基于宣告還是初始化? - 因此,對、
x1.a->b和的修改行為是否定義良好?x2.a->bp1->a->bp2->a->b
謝謝!
uj5u.com熱心網友回復:
從基礎開始,我希望 x1, x2, p1 和 p2 無疑是左值,并且示例中對 ya->b 的修改是合法的。正確的?
是的。
但是 x 和 p 案例中的 .a 成員呢?取消參考時它是左值嗎?
是的。x1.a、x2.a、p1->a和p2->a指向的物件不是臨時物件。它們是復合文字[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()不是左值,但是當結構包含陣列成員時,您可能會遇到獲取此類物件地址的情況。這就是為什么存在關于生命周期和修改的語言的原因。
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 *. 結構成員不能具有可變長度陣列型別;這將是 6.7.6p3 下的可變修改型別,并且對于結構或聯合成員是禁止的;見 6.7.2.1p9 和注釋 123。
可變長度陣列類似于:
void stuff(size_t n) {
int vla[n];
}
(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.)
And, consequently, is the behavior of modifications of x1.a->b, x2.a->b, p1->a->b and p2->a->b well defined?
Yes, absolutely.
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/431175.html
