部分初始化 C 结构体

29

这个链接说,“当自动数组或结构体有部分初始化器时,其余部分将被初始化为0”。我决定尝试一下我所读到的,并编写了以下代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)
{
    //int arr[3] = {2};  // line no. 7

    struct s {
        int si;
        int sj;
    };

    struct s myStruct;
    myStruct.si = 9;
    printf("%d\n", myStruct.sj);
}

当我注释掉第7行并取消注释时,我不明白为什么会打印出4096(我相信这是一些“垃圾”值),而我得到0。我不认为arr声明与main()的激活记录(或者更确切地说是myStruct)有关,它应该是这样的(假设我们已经取消注释了第7行):

---------------
|  Saved PC   |
---------------
|  arr[2]     |
---------------
|  arr[1]     |
---------------
|  arr[0]     |
---------------
|  si         |
---------------
|  sj         |
---------------

有人能否解释一下我在这里缺失了什么?


1
相关:http://stackoverflow.com/q/28545491/694576 - alk
4个回答

56

当您执行以下操作时:

struct s myStruct;
myStruct.si = 9;

你没有对myStruct进行初始化。你声明它时没有使用初始化器,然后运行语句来设置一个字段。
因为变量未初始化,其内容是未定义的,读取它是未定义行为。这意味着看似不相关的更改可能会修改这种行为。在你的例子中,添加一个额外的变量happened导致myStruct.sj为0,但不能保证这样做。
初始化变量,必须在定义时给它一个值。
struct s myStuct = { 9 };

做完这些后,您将看到myStruct.sj的内容设置为0。根据C标准第6.7.8节(针对此情况的特定突出显示),这是有保证的:

10如果具有自动存储期的对象没有明确初始化,则其值不确定。如果具有静态存储期的对象没有明确初始化,则:

—如果它具有指针类型,则初始化为null指针;

—如果它具有算术类型,则初始化为(正或无符号)零;

—如果它是一个聚合体,则每个成员都按照这些规则(递归地)初始化;

—如果它是联合体,则第一个命名成员按照这些规则(递归地)初始化。

...

21 如果封装在大括号内的初始化器少于聚合体的元素或成员,或者用于初始化已知大小数组的字符串文本字符数少于数组中的元素数量,则剩余部分的聚合体将隐式初始化,与具有静态存储期对象相同。


“声明时不要初始化”是一个不错的说法。顺便提一下,目前还没有任何答案解释“当我取消注释第7行时,为什么会得到0”的问题。 - sjsam
编译器选项可以设置为将所有内存初始化为零,某些操作系统可能会这样做,但正如@dbush所指出的那样,myStruct.sj 可能是任何值。如果 myStruct.sj 是一个指针,则未初始化的值可能会引起各种麻烦,我曾经遇到过这种情况。 - Michael Shopsin
1
关于“当我取消注释第7行时, 我得到0”的问题:正如dbush所指出的(“看似无关的更改……添加额外的变量恰好使myStruct.sj为0”),按定义来说,它是未定义的,在 _合理的实践中_它是任何留在那一部分内存中的值。如果您改变arr[3]定义的大小(从而改变sisj在内存映射中的位置),则可能会得到整个范围的值。 - TripeHound
2
@Spotlight 这就是原始的OP设置。他设置了 si,认为它是一个初始化,希望 sj 是0。 - dbush
@dbush 哦,好的。再看一遍,我没有看到sj和si之间的区别。谢谢! - Spotlight
显示剩余4条评论

15

在你的情况下,

 myStruct.si = 9;

这是一个 赋值语句,而不是 初始化。在这种情况下,结构体变量(以及对应的变量)未初始化。因此,您最终会读取一个未初始化的变量 sj 的值,这将导致未定义行为

您可以尝试

struct s myStruct = {9};

看到隐式初始化的效果。


11

那不是一个初始化器——你的结构体未被初始化,然后你只分配了 sisj 仍然未被初始化。

这本书指的是这种代码:

struct s myStruct = {9};

...其中sj保证为0。


2
这基本上是其他人已经给出的很好答案的一个或多或少完整的示例。
#include<stdio.h>

struct{
  int a;
  int b;
}obj1={.a=0}; //Partial initialization

typedef struct struct_B{
  int a;
  int b;
}struct_B;

int main(void)
{
  printf("obj1.b : %d\n",obj1.b);
  struct_B obj2={.b=1,.a=0,0}; // b's first value is overridden here as 0 immediately follows a
  printf("obj2.b : %d\n",obj2.b);
  struct_B obj3={0}; //Partial initialization, here the '0' value is meant for a as it comes first in the declaration
  printf("obj3.b : %d\n",obj3.b);
  struct_B obj4={.a=0}; //Partial initialization
  printf("obj4.b : %d\n",obj4.b);
  return 0;
}

输出:

obj1.b : 0
obj2.b : 0
obj3.b : 0
obj4.b : 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接