【问题标题】:C: Initializing struct variables after declarationC:声明后初始化结构变量
【发布时间】:2013-08-20 00:25:02
【问题描述】:

我最近遇到了这个问题,但不明白为什么该语言会允许 b = c;低于并且失败 b = {3, 4}。允许后者有问题吗?

struct T {
    int x;
    int y;
};

int main()
{
    T a = {1, 2};
    T b;

    b = {3, 4}; // why does this fail ?

    T c = {3, 4};
    b = c; // this works

    return 0;
}

【问题讨论】:

  • 我知道这是骗子:stackoverflow.com/q/16614045/694576
  • @alk,这不完全是一个骗局。另一个链接谈论将结构分配给结构,而这个只是关于将初始化程序分配给结构。
  • b = {3, 4}; 不是初始化,而是赋值。前者是T b = {3, 4};。是的,这两个是不同的野兽...... :-) 初始化发生在定义期间。分配发生在所有其他情况下。
  • 所以来回答你的问题标题:因为这是根据定义! ;-)
  • 标题与内容略有不同,因此对其进行了更改以匹配详细描述。它现在看起来还像一个 dup 吗?

标签: c++ c


【解决方案1】:

它失败是因为{3, 4},尽管它是一个有效的初始化器,但它不是一个表达式(至少它不在 C 语言中;有关 C++ 的更多信息,请参见下文)。

C 中的每个表达式都有一个类型,可以通过检查表达式本身来确定。 {3, 4} 可能是 struct T 或 int[2](数组类型)或无数其他类型中的任何一个。

C99 添加了一个名为 compound literals 的新功能,它使用与初始化程序类似的语法,但允许您指定类型,创建表达式:

b = (struct T){3, 4};

请注意,(struct T) 不是 强制转换运算符;它是复合文字语法的一部分。

有关复合文字的更多信息,请参阅draft C11 standard 的第 6.5.2.5 节。

复合文字是由 1999 ISO C 标准 (C99) 引入的。如果您的编译器不支持 C99 或更高版本 (*cough*Microsoft*cough*),那么您将无法使用它们。

如果您使用的是 C++(别忘了,它是一种不同的语言),它不支持复合文字,但可能有替代方案。正如 Potatoswatter 在评论中指出的那样:

b = T{3, 4};

在 C++11 中有效(但在 C++ 语言的早期版本中无效)。这在 C++ 标准的第 5.2.3 节 [expr.type.conf] 中有介绍。

就此而言,这是:

b = {3, 4};

也是有效的 C++11 语法。这种形式可以在许多指定的上下文中使用,包括赋值的右侧。这在 C++ 标准的第 8.5.4 节 [dcl.init.list] 中有介绍。

C++ 标准的最新草案是 N3485。

(g++ 支持 C++ 中的 C99 风格的复合文字作为扩展。)

如果你被 C99 之前的编译器卡住了,你总是可以编写自己的初始化函数,例如:

struct T init_T(int x, int y) {
    struct T result;
    result.x = x;
    result.y = y;
    return result;
}

/* ... */

struct T obj;
/* ... */
obj = init_T(3, 4);

这是令人讨厌的额外工作量(这就是 C99 添加复合文字的原因),但它确实完成了工作。另一方面,在大多数情况下,使用初始化可能会更好:

struct T obj;
/* ... */
{
    struct T tmp = { 3, 4 };
    obj = tmp;
}

哪个更好可能取决于您的程序的结构。

【讨论】:

  • 在 C++ 中,带括号的 type-id a la cast 表示法是非法的,但 T{ 3, 4 } 可以解决问题。
  • 有趣。为什么编译器对初始化和赋值的处理方式不同?在初始化时,它似乎将 RHS 解释为一个结构,而在分配中它不确定?
  • @user1952500:正如我所说,这是因为{3, 4} 是一个有效的初始化程序,它从正在初始化的对象中获取其类型,但它不是一个有效的表达式。
  • 啊,我没注意术语。感谢您的回答。
  • 那不是有效的 c++11 语法。您必须删除类型周围的括号。这更加严重,因为问题中使用的语法 is 有效 C++11。
【解决方案2】:

因为您没有使用正确的 C99 或 C11“复合文字”表示法:

b = (struct T){ 3, 4 };

请参阅 ISO/IEC 9899:2011 中的 §6.5.2 后缀运算符和 §6.5.2.5 复合文字,了解更多信息(以及其他地方)。

( type-name ) { initializer-list }
( type-name ) { initializer-list , }

【讨论】:

  • @JackCColeman:正如第一句话所说:这是一个 C99“复合文字”。 Microsoft 仍在实施 C89(即 24 年前)。
  • @JackCColeman:正如 MSalters 所说,Microsoft C 编译器是 C89 编译器,因此得知它不支持 C99 的此功能也就不足为奇了。确实,引用该标准(请注意,旧标准,甚至不是当前标准)的原因之一是让人们意识到 MSVC 的问题(它是 C89 编译器,而不是 C99 或 C11编译器)知道它不会与它一起工作。每次写需要C99或更高版本的答案时,我都不会选择谴责MS;我没有足够的时间,它不会取得任何有用的结果。
【解决方案3】:

因为这是语言的定义...我认为除了“编写编译器来实现这一点变得更加困难”之外,我认为没有任何特别的原因。

如果您有 C++ 11 编译器,您可以使用 b = T{3, 4}。

【讨论】:

  • C++ 不支持复合文字。当我尝试将其与 g++ -std=c++11 -pedantic 一起使用时,我收到“警告:ISO C++ 禁止复合文字”。
  • 这就是我开始写的,但认为我错了,所以当我看到其他答案时改变了它......相信你的直觉......
猜你喜欢
  • 2020-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-17
  • 1970-01-01
相关资源
最近更新 更多