28

This link states that "When an automatic array or structure has a partial initializer, the remainder is initialized to 0". I decided to try out what I read and wrote the following piece of code:

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

I don't understand why 4096 (which I believe is some "garbage" value) is printed when I comment out line no. 7 and I get 0 when I uncomment line no. 7. I don't think the arr declaration has got something to do with main()'s activation record (or rather myStruct) which should look like (provided we have line no. 7 uncommented):

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

Can somebody please explain what I am missing here?

alk
  • 69,737
  • 10
  • 105
  • 255
babon
  • 3,615
  • 2
  • 20
  • 20

4 Answers4

55

When you do this:

struct s myStruct;
myStruct.si = 9;

You are not initializing myStruct. You declare it without an initializer, then run a statement to set one field.

Because the variable is uninitialized, its contents are undefined, and reading it is undefined behavior. This means that seemingly unrelated changes can modify this behavior. In your example adding an extra variable happened to cause myStruct.sj to be 0, but there's no guarantee that this will be the case.

To initialize a variable, you have to give it a value at the time it is defined:

struct s myStuct = { 9 };

One you do this, then you'll see the contents of myStruct.sj set to 0. This is guaranteed as per section 6.7.8 of the C standard (with highlighting specific to this case):

10 If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static storage duration is not initialized explicitly, then:

—if it has pointer type, it is initialized to a null pointer;

if it has arithmetic type, it is initialized to (positive or unsigned) zero;

if it is an aggregate, every member is initialized (recursively) according to these rules;

—if it is a union, the first named member is initialized (recursively) according to these rules.

...

21 If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

Community
  • 1
  • 1
dbush
  • 205,898
  • 23
  • 218
  • 273
  • `declare it without an initializer` is a nice way to put it.. As a side note none of the answers yet have explained `I get 0 when I uncomment line no. 7` – sjsam May 31 '16 at 14:36
  • Compiler options can be set to initialize all memory to zero and some operating systems may do so, but `myStruct.sj` could be anything as @dbush points out. If `myStruct.sj` is a pointer than the uninitialized value could cause all kinds of trouble, this has happened to me. – Michael Shopsin May 31 '16 at 15:16
  • @MichaelShopsin : Without compliler options set, could you give an example for `some operating systems that do so`. I believe both Linux and Windows will not do so.. – sjsam May 31 '16 at 15:27
  • 1
    In relation to "_I get 0 when I uncomment line no. 7_": As dbush notes ("_seemingly unrelated changes ... adding an extra variable happened to cause myStruct.sj to be 0_"), by definition, it is undefined, in _plausible practice_ it is whatever happens to have been left in that bit of memory. If you were to vary the size of your `arr[3]` definition (thus varying how far down memory the `si` and `sj` in your memory map are), you _may_ get a whole range of values. – TripeHound May 31 '16 at 16:16
  • @sjsam I know some memory chips that tend to do so. Alternating 1s and 0s is another common pattern. None of this is something you can rely on unless you write code to do it. Undefined behaviour in C and C++ means anything can happen. It is that way because defining it would be more work for the compiler/processor and if you want it done you can ask by programming it to do so. – TafT May 31 '16 at 16:33
  • With `myStruct.si = 9;` did you mean sj, or am I overlooking something? – Spotlight May 31 '16 at 18:52
  • 2
    @Spotlight That's what OP originally had. He set `si` and, thinking it was an initialization, expected `sj` to be 0. – dbush May 31 '16 at 18:53
  • @dbush Ah, okay. Looking at it again I didn't see the difference between sj and si. Thanks! – Spotlight May 31 '16 at 18:55
  • Cross-referenced this handy answer here: https://stackoverflow.com/questions/59900970/how-to-init-struct-pointer-variables-with-null/59930302#59930302 – Edd Inglis Jan 27 '20 at 11:33
15

In your case,

 myStruct.si = 9;

is an assignment statement, not initialization. In this case, the structure variable (and the corresponding variables) are uninitialized. Thus, you're ending up reading the value of an uninitialized variable sj, which leads to undefined behavior.

You can try

struct s myStruct = {9};

to see the implicit initialization in action.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
11

That's not an initializer -- your structure is uninitialized, then you assign only si. sj remains uninitialized.

The book is referring to this kind of code:

struct s myStruct = {9};

... where sj is guaranteed to be 0.

Quentin
  • 62,093
  • 7
  • 131
  • 191
2

This is basically a - more-or-less- complete example for the great answers others already gave.

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

Output:

obj1.b : 0
obj2.b : 0
obj3.b : 0
obj4.b : 0
sjsam
  • 21,411
  • 5
  • 55
  • 102