【问题标题】:如何根据 C 编程语言标准初始化结构
【发布时间】:2010-09-24 18:04:12
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 你真的应该接受一个更好的答案,我看到你不得不使用一些糟糕的编码指南,但你仍然不应该向其他人建议这是正确的做法..跨度>
  • @KarolyHorvath 好吧,大多数好的答案都是针对 C99 的。也许我的问题与stackoverflow.com/questions/6624975/… 重复?
  • 如果这是您的初衷,那么可能是的,但是 1) 投票会非常具有误导性。 2)从顶部搜索命中,这是唯一一个显示 C99 方式的......最好重新使用这个页面进行 C99 演示......(显然人们开始链接这个页面来展示如何做)
  • 有趣的是,即使是最初发布的,已接受(并且高度赞成)的答案实际上并没有回答问题。指定的初始化器不解决 OP 的问题,即从初始化中拆分声明。对于 1999 年之前的 C,唯一真正的解决方案是分配给每个成员;对于 C99 及更高版本,CesarB 的答案中的复合文字是解决方案。 (当然,有或没有指示符的实际初始化程序会更好,但显然 OP 背负着非常糟糕的编码标准。)
  • 严格来说,“ANSI C”一词现在指的是 ANSI 采用的 2011 ISO 标准。但在实践中,术语“ANSI C”通常指的是(正式过时的)1989 标准。例如,“gcc -ansi”仍然执行 1989 标准。由于是 ISO 发布了 1990、1999 和 2011 标准,因此最好避免使用“ANSI C”一词,如果有任何混淆的可能性,请参考标准的日期。

标签: c struct initialization


【解决方案1】:

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

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

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

【讨论】:

  • 太棒了,谢谢。你甚至可以嵌套 em: static oxeRay2f ray = { .unitDir = { .x = 1.0f, .y = 0.0f } };
  • 这根本不能回答问题。 OP 希望将初始化与声明分开。
  • C99 != ANSI C - 所以这在 C89 和 C90 中都不起作用。
  • C99 ANSI C,见this
  • @CutbertoOcampo 我明白现在发生了什么。最初的问题是在 ANSI C 中寻求解决方案,显然他只想要 C89 的答案。 2016 年,该问题被编辑并删除了“ANSI C”,现在很难理解为什么这个答案和 cmets 提到“(ANSI) C99”。
【解决方案2】:

您可以使用compound literal 来完成。根据该页面,它适用于 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 };

【讨论】:

  • 那么,指定的初始化器是在你同时声明和定义的时候用的,而复合字面量是在你定义一个已经声明的变量时用的?
  • 这有点棘手。很多次(有些成功)我尝试使用指定的初始化程序。但是,现在才变得清楚(感谢您和@philant 的示例)如果在创建对象时未使用初始化程序,则必须存在强制转换。但是,我现在还了解到,如果在创建对象以外的时间(如您的示例中)使用初始化器,则它被称为 复合文字,而不是严格的 指定初始化器ideone.com/Ze3rnZ
【解决方案3】:

我看到您已经收到了有关 ANSI C 99 的答案,所以我会抛出有关 ANSI C 89 的问题。 ANSI C 89 允许您以这种方式初始化结构:

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

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

要记住的重要一点是,在你初始化结构中的一个对象/变量的那一刻,它的所有其他变量都将被初始化为默认值。

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

祝你好运!

【讨论】:

  • 初始化时可以使用变量吗?
  • @Cyan 适用于具有自动存储期限的对象。请参阅open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 中的 6.7.9 13)。对于全局对象几乎仅限于文字。你甚至不能使用其他全局对象,即使它们是 const。
  • 那么与 C 99 的唯一区别就是不能指定名称?
【解决方案4】:

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

在 C99 中会很好

【讨论】:

    【解决方案5】:

    你已经差不多了......

    MY_TYPE a = { true, 15, 0.123 };
    

    search on 'struct initialize c'shows me this

    【讨论】:

    • 我认为这适用于标准 C,但不适用于 ANSI C(99?)。此外,我受制于不允许我同时进行声明和初始化的编码规则,因此我必须将其拆分并初始化每个子元素。
    • 初始化只能在声明时发生。这就是“初始化”的意思。否则,您允许未定义的值作为变量/结构的初始值,然后再分配一个值。
    • 嗯...你有故意不好的编码规则?或许你应该把写它们的人介绍给“资源获取就是初始化”(en.wikipedia.org/wiki/RAII
    • 相信我,在这一点上,我真的,真的,无法改变这个规则。编写代码感觉很糟糕。还有很多 70 年代以来的其他规则,例如带有管理信息(如修订号)的静态代码标题。每个版本的更改,即使源没有更改...
    【解决方案6】:

    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 实现中,省略的成员被初始化为零或类似零的类型合适的值。如 GNU GCC 文档的 6.27 Designated Initializers 部分所述:

    省略的字段成员与具有静态存储持续时间的对象一样被隐式初始化。

    根据官方博客文章C++ Conformance Roadmap,Microsoft Visual C++ 编译器应该从 2013 版开始支持指定的初始化程序。 MSDN Visual Studio 文档中Initializers 文章的Initializing unions and structs 段落表明,未命名的成员初始化为类似于 GNU GCC 的类似零的适当值。

    ISO/IEC 9899:2011 标准(通常称为 C11)已取代 ISO/IEC 9899:1999,在 6.7.9 Initialization 部分保留指定的初始化程序。它还保持paragraph 9不变。

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

    【讨论】:

    • 我相信“未命名成员”不是指“省略字段”,而是指“匿名成员”,例如struct thing { union { char ch; int i; }; }; 中的工会。该标准后来说:“如果大括号括起来的列表中的初始化程序少于聚合的元素或成员,或者用于初始化已知大小数组的字符串文字中的字符少于数组中的元素,聚合的其余部分应隐式初始化,与具有静态存储持续时间的对象相同。"
    【解决方案7】:

    作为Ron Nuni said:

    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(tm_isdst 字段),这是来自 strptime,但是当我转换为 time_t 时,有些休息了一个小时。
    【解决方案8】:

    在所有这些好的答案中添加一个关于如何在 C 中初始化结构(联合和数组)的总结,特别关注设计的初始化器。

    标准初始化

    struct point 
    {
        double x;
        double y;
        double z;
    }
    
    p = {1.2, 1.3}; 
    

    指定初始化器

    指定初始化器自 ISO C99 开始出现,它是在初始化 structunionarray 时在 C 中初始化的一种不同且更动态的方式。

    与标准初始化最大的不同是不必按固定顺序声明元素,也可以省略元素。

    来自GNU Guide

    标准 C90 要求初始值设定项的元素以固定顺序出现,与被初始化的数组或结构中元素的顺序相同。

    在 ISO C99 中,您可以按随机顺序指定元素,指定它们适用的数组索引或结构字段名称,GNU C 也允许将其作为 C90 模式的扩展


    示例

    1。数组索引

    标准初始化

    int a[6] = { 0, 0, 15, 0, 29, 0 };
    

    指定初始化

    int a[6] = {[4] = 29, [2] = 15 }; // or
    int a[6] = {[4]29 , [2]15 }; // or
    int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 };
    

    2。结构或联合:

    标准初始化

    struct point { int x, y; };
    

    指定初始化

    struct point p = { .y = 2, .x = 3 }; or
    struct point p = { y: 2, x: 3 };
    

    3。将命名元素与连续元素的普通 C 初始化结合起来:

    标准初始化

    int a[6] = { 0, v1, v2, 0, v4, 0 };
    

    指定初始化

    int a[6] = { [1] = v1, v2, [4] = v4 };
    

    4。其他:

    标记数组初始值设定项的元素

    int whitespace[256] = { [' '] = 1, ['\t'] = 1, ['\h'] = 1,
                            ['\f'] = 1, ['\n'] = 1, ['\r'] = 1 };
    

    在“=”之前写入一系列“.fieldname”和“[index]”指示符以指定要初始化的嵌套子对象

    struct point ptarray[10] = { [2].y = yv2, [2].x = xv2, [0].x = xv0 };
    

    指南

    【讨论】:

      【解决方案9】:

      这可以通过不同的方式完成:

      MY_TYPE a = { true, 1, 0.1 };
      
      MY_TYPE a = { .stuff = 0.1, .flag = true, .value = 1 }; //designated initializer, not available in c++
      
      MY_TYPE a;
      a = (MY_TYPE) { true,  1, 0.1 };
      
      MY_TYPE m (true, 1, 0.1); //works in C++, not available in C
      

      另外,您可以在声明结构时定义成员。

      #include <stdio.h>
      
      struct MY_TYPE
      {
          int a;
          int b;
      }m = {5,6};
      
      int main()
      {
          printf("%d  %d\n",m.a,m.b);    
          return 0;
      }
      

      【讨论】:

        【解决方案10】:

        我不喜欢这些答案中的任何一个,所以我自己做了。我不知道这是否是 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 个项目,所以我希望它们放在一个单独的文件中,既可以避免代码混乱,也更容易替换。

        【讨论】:

        • 结构数组的初始化毫无疑问!我的意思是,您的答案包含开销。
        • 我想我的意思是声明结构中已有值,而不是使用 = 稍后设置每个值。
        • 无论是一个结构体还是它们的数组,该原则都适用。使用 = 并不是真正的初始化,我不认为。
        【解决方案11】:
        void function(void) {
          MY_TYPE a;
          a.flag = true;
          a.value = 15;
          a.stuff = 0.123;
        }
        

        【讨论】:

        • 我听说过“初始化不同于分配”。所以这不是赋值而不是初始化吗?
        【解决方案12】:

        我找到了另一种初始化结构的方法。

        结构:

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

        初始化:

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

        根据 GCC 的文档,此语法为 obsolete since GCC 2.5

        【讨论】:

          【解决方案13】:

          如果MS还没有更新到C99,MY_TYPE a = { true,15,0.123 };

          【讨论】:

            【解决方案14】:

            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;
            }
            

            【讨论】:

              【解决方案15】:

              我已经阅读了Microsoft Visual Studio 2015 Documentation for Initializing Aggregate Types,但是那里解释了使用{...} 进行初始化的所有形式,但是没有提到使用点进行初始化,命名为''designator''。它也不起作用。

              C99 标准第 6.7.8 章初始化解释了指示符的可能性,但在我看来,对于复杂的结构并不是很清楚。 C99 standard as 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 中的面向对象。使用 thiz 而不是 this 也可以用 C++ 编译它!

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

              【讨论】:

                【解决方案16】:

                我一直在寻找一种初始化我的结构的好方法,我必须使用下面的 (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},你会看到只有第一个数组元素被如此初始化。
                • 是的,我刚做完更多测试回来,想编辑帖子 :) 顺便说一句,int a[10] = {1}; 也会发生同样的行为,只有第一个元素是 ==1
                猜你喜欢
                • 2010-12-14
                • 1970-01-01
                • 1970-01-01
                • 2020-01-18
                • 1970-01-01
                • 1970-01-01
                • 2022-01-11
                • 2012-03-13
                • 2013-12-25
                相关资源
                最近更新 更多