c-对复合文字的赋值如何有用



我读到了关于复合文字的文章,发现它们是l-value。所以我怀疑你可以分配它们,所以我做了一个实验,我注意到用gcc -Wall -Wextra -pedantic:编译时没有警告

struct foo {
int x;
int y;
};
int main(void) {
(struct foo){0,0} = (struct foo){1,1};
}

这让我很困惑,因为我真的看不出这在任何情况下都有用。你什么时候想把赋值给一个复合文字?

还是这是一种常见的未定义行为?因为它看起来很像修改字符串文字。这编译时没有警告,参数与上面相同:

struct foo *ptr = &(struct foo){0,0};
*ptr = (struct foo){1, 1};
char *str = "Hello";
*str = 'X'; // Undefined behavior

根据C标准6.4.5.7 ,修改字符串是未定义的行为

嗯,它可能对没有用处,但它是限制最少的。

由于非数组类型的非常量限定复合文字是可修改的左值,并且可修改的左值可以在中分配给所有可以对可修改左值执行的操作,因此可以分配给复合文字。

相反的情况是,对于不能对作为复合文字的lvalues执行的操作,您将有一些额外的情况。


我发现了一个用例,工作,赋值会导致左值,而不是:

既然你刚才说你不喜欢宏,那么我不确定这是否能说服你,但这使得编写一个扩展到如下内容的宏成为可能
foo(&((struct baz){0} = bar()))

这里bar返回一个struct baz作为值,而foo需要一个指向这样的struct的指针作为参数。如果没有这个特性,您就无法进行这样的内联值传递

复合文字的目标是什么
目标是在不提供名称的情况下在堆栈上创建一个完全可用的。

是否"堆栈上的完全可用对象";需要成为左值
是。复合文字的一个典型用途是获取它们的地址并将其传递给某个函数。目标是,每当我有一个接受Foo* fooPtr的函数时,我都可以将参数提供为复合文字&(Foo){...}。由于我正在获取地址,并且需要将非常量指针传递给函数,因此复合文字必须是左值。

请注意,该函数可以通过简单的*fooPtr = (Foo){...};分配给对象。像这样的代码对于构造函数或重置对象的函数来说是非常典型的。函数不知道fooPtr指向的是复合文字、命名变量还是堆上的内存块。在所有这些情况下,转让都是合法的。

你看,你可以给一个复合文字赋值的事实只是复合文字是lvalue的副作用。复合文字只有在它们真的是lvalue的情况下,才真正有用作为内联的、动态的对象创建构造。

我找不到任何好的用途,但我确实发现了字符串文字的一些显著差异。关于复合文字的章节只出现了一个单词";未定义的";正如人们所看到的,它不适用于一般的分配或其他修改:

16请注意,如果使用迭代语句而不是显式goto和标记语句,则未命名对象的生存期将仅为循环的主体,并且在下一次进入时,p周围的值将不确定,这将导致未定义的行为。

因此;是未定义的行为吗;似乎是:";不,不是">

此外,另一个区别是寿命。虽然字符串文字总是有静态存储,但复合文字却没有:

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

这意味着这完全可以:

char *get_string() {
return "Foobar"; // Ok! Static storage
}

但这不是:

struct my_struct {
int x;
int y;
};
struct my_struct *get_my_struct() {
return &(struct my_struct){0,0}; // Not ok! Automatic storage
}

我还做了一个实验。我不知道如何100%地解释它,因为它既可以被解释为静态存储,也可以被理解为未定义的行为。这个输出的";42〃:

int main(void) {
struct my_struct *p = get_my_struct();
struct my_struct *t = get_my_struct();
p->x = 42;
printf("%dn", t->x);
}

然而,当我打开-O2-O3时;0";相反,所以我很确定这是未定义的行为。优化级别不同的结果是UB非常常见的症状。不过请注意,在我打开优化之前没有发出任何警告。当我这样做的时候,我得到了这个:

warning: ‘<Uf3f0>.x’ is used uninitialized in this function [-Wuninitialized]
16 |     printf("%dn", t->x);
|     ^~~~~~~~~~~~~~~~~~~~

另一件值得注意的事情是:

7字符串文字和具有const限定类型的复合文字不需要指定不同的对象

这意味着两个没有const的复合文字应该指定不同的对象,但对于字符串文字来说这不是真的。

IMO没有理由禁止它,即使它没有用处。

IMO与非常相似

void foo(void)
{
int a = a;
a = a;
}

即使a也被认为是由编译器初始化的:)

https://godbolt.org/z/Msv6q9

最新更新