如何根据C编程语言标准初始化结构


465

我想初始化一个struct元素,拆分为声明和初始化。这就是我所拥有的:

typedef struct MY_TYPE {
  bool flag;
  short int value;
  double stuff;
} MY_TYPE;

void function(void) {
  MY_TYPE a;
  ...
  a = { true, 15, 0.123 }
}

这是MY_TYPE按照C编程语言标准(C89,C90,C99,C11等)声明和初始化局部变量的方法吗?还是有什么更好的方法,或者至少是可行的方法?

更新我最终得到了一个静态初始化元素,在其中根据需要设置了每个子元素。


3
你真的应该接受一个更好的答案,我看你不得不使用一些不好的编码规则,但你还是不应该推荐给其他人认为这是做正确的方式..
卡罗利·霍瓦斯

@KarolyHorvath很好,大多数好的答案都特定于C99。也许我的问题是stackoverflow.com/questions/6624975/…的副本?
畏缩

如果那是您的初衷,则可能是,但是然后1)投票将极具误导性。2)从顶部搜索命中起,这是唯一显示C99方式的网站...最好将该页面重新用于C99演示...(显然,人们开始链接此页面以显示如何做到)
Karoly Horvath 2012年

10
有趣的是,即使是最初发布的,被接受(且被强烈反对)的答案实际上也无法回答问题。指定的初始化程序无法解决OP的问题,即将声明与初始化分开。对于1999年前的C,唯一真正的解决方案是分配给每个成员。对于C99和更高版本,可以使用CesarB的答案中的复合文字来解决。(当然,一个实际的初始化程序,带有或不带有指定符,甚至会更好,但显然OP的编码标准很差。)
Keith Thompson

31
严格来说,“ ANSI C”一词是指ANSI已采用的2011 ISO标准。但是实际上,术语“ ANSI C”通常是指(正式过时的)1989年标准。例如,“ gcc -ansi”仍强制执行1989年标准。由于是发布1990、1999和2011年标准的ISO,因此最好避免使用“ ANSI C”一词,并在可能出现混淆的情况下引用该标准的日期。
Keith Thompson

Answers:


726

在(ANSI)C99中,可以使用指定的初始化程序来初始化结构:

MY_TYPE a = { .flag = true, .value = 123, .stuff = 0.456 };

编辑:其他成员初始化为零:“省略的字段成员隐式初始化为与具有静态存储持续时间的对象相同。” (https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html


87
很好,谢谢。您甚至可以嵌套它们:static oxeRay2f ray = {.unitDir = {.x = 1.0f,.y = 0.0f}};
Orion elenzil 2011年

4
这根本无法回答问题。OP希望将初始化与声明分开。
osvein

3
C99!= ANSI C-因此在C89和C90中均不起作用。
蒂姆·维斯曼(TimWißmann)'18年

3
C99 ANSI C,看到
Cutberto奥坎波

3
@CutbertoOcampo我知道现在发生了什么。最初的问题是在ANSI C中寻求解决方案,显然他只想为C89提供答案。在2016年,对问题进行了编辑,并删除了“ ANSI C”,这使得现在很难理解为什么此答案和注释提到“(ANSI)C99”。
圣艾蒂安

198

您可以使用复合文字来实现。根据该页面,它可以在C99(也计为ANSI C)中工作。

MY_TYPE a;

a = (MY_TYPE) { .flag = true, .value = 123, .stuff = 0.456 };
...
a = (MY_TYPE) { .value = 234, .stuff = 1.234, .flag = false };

初始化程序中的指定是可选的;您还可以写:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };

因此,指定的初始值设定项是用于同时声明和定义的,而复合文字是用于定义已声明的变量的?
Geremia

@Geremia在这两种情况下都必须先定义结构,但是使用指定的初始化程序,可以在结构发生更改的情况下使代码更具可读性,并且对错误更具有弹性。
hoijui

2
这有点棘手。很多次(有些成功)我尝试使用指定的初始化程序。但是,现在才变得很清楚(由于您和@philant的示例),如果在对象创建时未使用初始化程序,则必须存在强制转换。但是,我现在还了解到,如果在对象创建之外的其他时间使用初始化程序(如您的示例中所示),则将其称为复合文字,而不是严格指定的初始化程序ideone.com/Ze3rnZ
sherrellbc

93

我看到您已经收到有关ANSI C 99的答案,因此,我将介绍ANSI C89。ANSI C 89允许您通过以下方式初始化结构:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = { 5, 2.2, "George" };
    return 0;
}

要记住的重要一点是,即使在结构中初始化一个对象/变量时,其所有其他变量也会被初始化为默认值。

如果不初始化结构中的值,则所有变量都将包含“垃圾值”。

祝好运!


3
是否可以在初始化中使用变量?
青色

2
@Cyan用于具有自动存储持续时间的对象。参见open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf中的 6.7.9 13)。对于全局对象,几乎仅限于文字。您甚至不能使用其他全局对象,即使它们是const。
PSkocik

因此,与C 99的唯一区别是您无法指定名称?
Akaisteph7

42

a = (MYTYPE){ true, 15, 0.123 };

可以在C99中很好用


1
老实说,我认为答案是最有用的答案,但无论如何。
user12211554

21

你差不多了...

MY_TYPE a = { true,15,0.123 };

快速搜索'struct initialize c' 向我展示了这个


1
我认为这对于标准C是正确的,但对于ANSI C(99?)则不是。另外,我受制于不允许我立即进行声明和初始化的编码规则,因此我必须将其拆分并初始化每个子元素。
畏缩

8
初始化只能在声明时进行。这就是“初始化”的含义。否则,您允许将未定义的值用作变量/结构的初始值,然后再分配一个值。
Kieveli

8
嗯...您的编码规则故意是不好的?也许你应该引进谁他们写信给“资源获取就是初始化”(这个家伙en.wikipedia.org/wiki/RAII
詹姆斯·柯伦

相信我,我现在真的,真的不能更改此规则。编写代码感觉很恐怖。在70年代,还有许多其他规则,例如带有管理信息(例如修订号)的静态代码标头。对于每一个版本的变化,即使源没有改变...
畏缩,

19

C编程语言标准ISO / IEC 9899:1999(通常称为C99)允许使用指定的初始化程序来初始化结构或联合的成员,如下所示:

MY_TYPE a = { .stuff = 0.456, .flag = true, .value = 123 };

在ISO / IEC 9899:1999标准的paragraph 7章节中将其定义6.7.8 Initialization为:

如果指定人具有表格
。标识符,
则当前对象(在下面定义)应具有结构或联合类型,并且标识符应是该类型的成员的名称。

请注意,paragraph 9同一部分中指出:

除非另有明确说明,否则出于本条的目的,结构和联合类型的对象的未命名成员不参与初始化。结构对象的未命名成员即使在初始化后也具有不确定的值。

但是,在GNU GCC实现中,省略的成员初始化为零或类似零的类型合适的值。如第6.27节“ GNU GCC 指定的初始化程序”所述:

忽略字段成员的隐式初始化与具有静态存储持续时间的对象相同。

根据官方博客文章《C ++一致性路线图》, Microsoft Visual C ++编译器应从2013版开始支持指定的初始化程序。MSDN Visual Studio文档中Initializing unions and structsInitializers段落建议,未命名的成员类似于GNU GCC一样初始化为类似零的适当值。

已取代ISO / IEC 9899:1999的ISO / IEC 9899:2011标准(通常称为C11)保留了第节中指定的初始化程序6.7.9 Initialization。它也保持paragraph 9不变。

已取代ISO / IEC 9899:2011的新ISO / IEC 9899:2018标准(通常称为C18)保留了第节中指定的初始化程序6.7.9 Initialization。它也保持paragraph 9不变。


2
最初,我建议将此作为对已接受答案的编辑,但被拒绝了。因此,我将其发布为答案。
PF4Public,2016年

我相信“未命名成员”并不意味着“遗漏字段”,而是“匿名成员”,例如中的struct thing { union { char ch; int i; }; };。该标准后来说:“如果括号括起来的列表中的初始化程序少于集合中的元素或成员,或者用于初始化已知大小的数组的字符串文字中的字符少于该数组中的元素,聚合的其余部分应与具有静态存储持续时间的对象隐式初始化。”
再现

14

正如罗恩·努尼(Ron Nuni)所说:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = {5, 2.2, "George"};
    return 0;
}

要记住的重要事项:在结构中甚至初始化一个对象/变量时,其所有其他变量都将被初始化为默认值。

如果您不初始化结构体中的值(即仅声明该变量),variable.members仅在声明为本地的情况下,所有内容都将包含“垃圾值”

如果声明是全局的或静态的(如本例所示),则所有未初始化的variable.members将自动初始化为:

  • 0 用于整数和浮点数
  • '\0'for char(当然,这与和一样0,并且char是整数类型)
  • NULL 用于指针。

对本地/全局感兴趣,这也适用于struct tm时间值。我有一个本地计算机,在一种情况下启用DST,在另一种情况下没有启用DST,这并不是由于我所做的任何事情。我没有使用DST(tm_isdst字段),这是从strptime开始的,但是当我转换为time_t时,会节省一个小时的时间。
艾伦·科里

3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}

23
一旦我听到“初始化不同于赋值”。所以这不是分配而是初始化?
HayriUğurKoltuk 2012年

2

如果MS尚未更新为C99,则MY_TYPE a = {true,15,0.123};


2

我发现了另一种初始化结构的方法。

结构:

typedef struct test {
    int num;
    char* str;
} test;

初始化:

test tt = {
    num: 42,
    str: "nice"
};

根据GCC的文档,此语法自GCC 2.5起过时


2

我不喜欢这些答案,所以我自己做了。我不知道这是否是ANSI C,它只是默认模式下的GCC 4.2.1。我永远都不记得括号,所以我从我的数据子集开始,并与编译器错误消息作斗争,直到它关闭为止。可读性是我的首要任务。

    // in a header:
    typedef unsigned char uchar;

    struct fields {
      uchar num;
      uchar lbl[35];
    };

    // in an actual c file (I have 2 in this case)
    struct fields labels[] = {
      {0,"Package"},
      {1,"Version"},
      {2,"Apport"},
      {3,"Architecture"},
      {4,"Bugs"},
      {5,"Description-md5"},
      {6,"Essential"},
      {7,"Filename"},
      {8,"Ghc-Package"},
      {9,"Gstreamer-Version"},
      {10,"Homepage"},
      {11,"Installed-Size"},
      {12,"MD5sum"},
      {13,"Maintainer"},
      {14,"Modaliases"},
      {15,"Multi-Arch"},
      {16,"Npp-Description"},
      {17,"Npp-File"},
      {18,"Npp-Name"},
      {19,"Origin"}
    };

数据可能以制表符分隔的文件开始,您可以搜索该数据并替换成其他内容。是的,这是Debian的东西。因此,一对外部{}(表示数组),然后对内部的每个结构另外一对。之间用逗号分隔。严格将内容放在标头中不是必须的,但是我的结构中已有大约50个项目,因此我希望将它们放在一个单独的文件中,既可以避免代码混乱,又可以更轻松地替换。


3
毫无疑问有关结构数组的初始化!我的意思是,您的答案包含开销。
Victor Signaevskyi '17

我想我的意思是声明结构中已经有值,而不是使用=以后再设置每个值。
艾伦·科里

无论是一个结构还是它们的数组,该原则都适用。我不认为使用=并不是真正的初始化。
艾伦·科里

0

C中的结构可以这样声明和初始化:

typedef struct book
{
    char title[10];
    char author[10];
    float price;
} book;

int main() {
    book b1={"DS", "Ajay", 250.0};

    printf("%s \t %s \t %f", b1.title, b1.author, b1.price);

    return 0;
}

0

我已经阅读了有关初始化聚合类型Microsoft Visual Studio 2015文档,所有形式的初始化{...}此处介绍了,但此处未提及使用点进行初始化(名为“指示符”)的情况。它也不起作用。

C99标准章节6.7.8初始化说明了指定符的可能性,但是在我看来,对于复杂的结构并不清楚。在C99标准为PDF

在我看来,

  1. = {0};对所有静态数据使用-initialization。机器代码的工作量较小。
  2. 例如,使用宏进行初始化

    typedef MyStruct_t{ int x, int a, int b; } MyStruct; define INIT_MyStruct(A,B) { 0, A, B}

可以修改宏,其参数列表可以独立于更改的结构内容。如果应初始化较少的元素,则是适当的。对于嵌套结构也很合适。3.一个简单的形式是:在子例程中初始化:

void init_MyStruct(MyStruct* thiz, int a, int b) {
  thiz->a = a; thiz->b = b; }

该例程看起来像C中的ObjectOriented。使用thiz,也不this要用C ++进行编译!

MyStruct data = {0}; //all is zero!
init_MyStruct(&data, 3, 456);

0

我一直在寻找初始化我的结构的好方法,并且必须使用下面的代码(C99)。这使我可以用与普通类型相同的方式初始化单个结构或结构数组。

typedef struct {
    char *str;
    size_t len;
    jsmntok_t *tok;
    int tsz;
} jsmn_ts;

#define jsmn_ts_default (jsmn_ts){NULL, 0, NULL, 0}

可以在代码中将其用作:

jsmn_ts mydata = jsmn_ts_default; /* initialization of a single struct */

jsmn_ts myarray[10] = {jsmn_ts_default, jsmn_ts_default}; /* initialization of
                                                    first 2 structs in the array */

但这不会将结构数组初始化为默认值。它将数组中的第一个结构初始化为中给定的值jsmn_ts_default,但其余结构被初始化为好像数组具有静态存储持续时间,这意味着成员被初始化为NULL0。但是,如果更改为#define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0},则会看到只有第一个数组元素如此初始化。
前nihilo

是的,我只是从更多测试中回来,想要编辑该帖子:)顺便说一句,相同的行为int a[10] = {1};也会发生,只有第一个元素是==1
div0man
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.