ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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 标准。由于 1990、1999 和 2011 标准是由 ISO 发布的,因此最好避免使用“ANSI C”一词,如果有任何混淆的可能性,请参考标准的日期。

N
Neuron

在 (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”,现在很难理解为什么这个答案和评论提到“(ANSI)C99”。
J
Jonathan Leffler

您可以使用 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 };

那么,指定的初始化器是用于同时声明和定义的时候,而复合字面量是用于定义已经声明的变量的时候?
@Geremia 您必须在这两种情况下首先定义结构,但是使用指定的初始化程序,您可以使代码更具可读性,并且在结构发生更改的情况下对错误更具弹性。
这有点棘手。很多次(有些成功)我尝试使用指定的初始化程序。但是,现在才变得清楚(感谢您和@philant 的示例)如果在创建对象时未使用初始化程序,则必须存在强制转换。但是,我现在还了解到,如果在创建对象以外的时间(如您的示例中)使用初始化器,则它被称为 复合文字,而不是严格的 指定初始化器ideone.com/Ze3rnZ
M
Mr. Polywhirl

我看到您已经收到了有关 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 的唯一区别是您不能指定名称?
q
qrdl

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

在 C99 中会做得很好


N
Neuron

你几乎得到它...

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 年代以来的其他规则,例如带有管理信息(如修订号)的静态代码标题。每个版本的更改,即使源没有更改......
P
PF4Public

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

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

它在 ISO/IEC 9899:1999 标准的 paragraph 76.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: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 不变。


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

作为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 将包含“垃圾值”,仅当声明是本地的!

如果声明是 global 或 static(如本例),所有未初始化的 variable.members 将自动初始化为:

0 表示整数和浮点数

'\0' 表示 char(当然这和 0 一样,char 是整数类型)

指针为 NULL。


有趣的是本地/全局,这也适用于 struct tm 时间值。我有一个本地的,在一种情况下 DST 开启,在另一种情况下没有,不是因为我做了什么。我没有使用 DST(tm_isdst 字段),这是来自 strptime,但是当我转换为 time_t 时,有些时间休息了一个小时。
C
Community

添加到所有这些好的答案中,总结了如何在 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 };

指南

指定初始化器-c | geeksforgeeks.org

使用指定初始化器

教程点.com |指定初始化程序-in-c


d
dev

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

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

N
Neuron

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


没有关于结构数组初始化的问题!我的意思是,您的答案包含开销。
我想我的意思是声明结构中已经存在值,而不是使用 = 稍后设置每个值。
无论是一个结构还是它们的数组,该原则都适用。使用 = 并不是真正的初始化,我不认为。
D
David Guyon
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}

一旦我听说“初始化不同于分配”。所以这不是赋值而不是初始化吗?
P
Palec

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

结构:

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

初始化:

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

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


N
Neuron

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


t
tdube

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

H
Hartmut Schorrig

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

C99 标准第 6.7.8 章初始化解释了指示符的可能性,但在我看来,对于复杂的结构并不是很清楚。 C99 standard as pdf

在我看来,这可能会更好

对所有静态数据使用 = {0};- 初始化。机器代码的工作量更少。使用宏进行初始化,例如 typedef MyStruct_t{ int x, int a, int b; } 我的结构;定义 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);

d
div0man

我一直在寻找一种初始化我的结构的好方法,我必须使用下面的(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