8

我正在阅读复合文字,我看到它们是左值。所以我怀疑你可以分配它们,所以我做了一个实验,我注意到编译时没有警告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 ,修改字符串是未定义的行为

4

4 回答 4

5

好吧,它可能没有,但它是限制最少的一个。

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

相反的是,对于作为复合文字的左值,将有一些额外的情况。


我发现了一个可以使用的用例,它可以分配左值,但它不会:

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

这里bar返回 astruct baz作为一个值,并且foo需要一个指向诸如struct参数之类的指针。如果没有这个特性,你就不能做这样的内联值传递。

于 2020-10-08T10:40:00.640 回答
2

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

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

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

您会看到,您可以分配给复合文字的事实只是复合文字是左值的副作用。只有当复合字面量是真正的左值时,复合字面量才能真正用作内联、即时的对象创建构造。

于 2020-10-08T13:30:45.820 回答
0

我找不到任何好的用途,但我确实发现了字符串文字的一些显着差异。关于复合文字的章节只出现了一次“未定义”一词,并且可以看出,它通常不适用于赋值或其他修改:

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("%d\n", t->x);
}

但是,当我打开-O2-O3打印“0”时,我很确定这是未定义的行为。取决于优化级别的不同结果是 UB 的一个非常常见的症​​状。请注意,在我打开优化之前没有发出警告。当我这样做时,我得到了这个:

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

还有一点值得注意的是:

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

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

于 2020-10-08T11:11:52.437 回答
-1

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

IMO 非常类似于

void foo(void)
{
    int a = a;

    a = a;
}

Evena被编译器认为是初始化的:)

https://godbolt.org/z/Msv6q9

于 2020-10-08T11:01:33.700 回答