6

Say I've got this type:

struct Bitmap
{
    int w, h, *b;
};

I am initializing it like this:

int w = 7, h = 4;
struct Bitmap bmp = {w, h, calloc(bmp.w * bmp.h, sizeof(*bmp.b))};

Is it guaranteed that the compiler will initialize the struct sequentially? Can I be sure that by the time the bmp.b field is being initialized the bmp.w and bmp.h fields are already initialized?

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Why is the order important? – chux - Reinstate Monica Mar 30 '16 at 16:02
  • BTW, should make certain the product is calculated using `size_t` math: `bmp.w * bmp.h` --> `(size_t) bmp.w * bmp.h`. – chux - Reinstate Monica Mar 30 '16 at 16:03
  • If the `bmp.b` field is being initialized first, then `bmp.w` and `bmp.h` fields contain garbage at the time of `bmp.b` initialization, and thus thing like `calloc(-542 * 3546, sizeof(*bmp.b))` can occur. –  Mar 30 '16 at 16:03
  • Is there a reason you can't just use `calloc(w * h, sizeof(*bmp.b))`? – R_Kapp Mar 30 '16 at 16:04
  • Yes I see, use `{w, h, calloc((size_t) w * h, sizeof(*bmp.b))}` – chux - Reinstate Monica Mar 30 '16 at 16:05
  • In this particular case, it should because the compiler would optimize, as the values (7,4) are hardcoded. However, if they are dynamic, then this may depend on the compiler that shall be used. – Hugo Mar 30 '16 at 16:05
  • @R_Kapp in real code, I'm using `w + 1` and `h + 1`, which together would result in ugly `calloc((w + 1) * (h + 1), sizeof(*bmp.b))`. And what if the 'w' and 'h' were even more heavily modified? –  Mar 30 '16 at 16:06
  • Interestingly, even though the answer to each of your questions is affirmative, the code is still broken. Shows how hard it can be to even ask the right question! – Kerrek SB Mar 30 '16 at 16:22
  • @KerrekSB What do you mean broken? It works just fine if you include the `stdlib` header. And I'm using the C99 standard, not the ancient C89. –  Mar 30 '16 at 16:23
  • @Xis88: See R Sahu's answer? – Kerrek SB Mar 30 '16 at 16:25
  • @KerrekSB The affirmative answers have been deleted/edited by their authors. –  Mar 30 '16 at 16:26
  • @Xis88: I think you misunderstand: The answer to all *your* questions is "yes", as R Sahu explains: Yes, the struct is initialized sequentially, and yes, `bmp.b` is initialized only after `bmp.h` and `bmp.w`. But the important point you're missing is that the evaluation of the initial value for `bmp.b` causes undefined behaviour. – Kerrek SB Mar 30 '16 at 16:49
  • @KerrekSB Do you mean `sizeof(*bmp.b)`? It's not UB: http://stackoverflow.com/questions/19785518/is-dereferencing-null-pointer-valid-in-sizeof-operation –  Mar 30 '16 at 17:07
  • @Xis88: No, I don't mean that. I mean `calloc(bmp.w * bmp.h, sizeof(*bmp.b))`, which requires two lvalue conversions of possibly uninitialized values. Just as R Sahu describes in his excellent answer. – Kerrek SB Mar 30 '16 at 17:08
  • @KerrekSB Oh, got it. Thank you for clearing that up. –  Mar 30 '16 at 17:10

2 Answers2

7

Using

Bitmap bmp = {w, h, calloc(bmp.w * bmp.h, sizeof(*bmp.b))};

is undefined behavior. There are two items in the C99 standard that address this.

6.7.8. Initialization

...

19 The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject.

...

23 The order in which any side effects occur among the initialization list expressions is unspecified.133)

and footnote 133 says:

In particular, the evaluation order need not be the same as the order of subobject initialization

Together, what they mean is that bmp.w will be initialized before bmp.h. However, it is possible that calloc(bmp.w * bmp.h, sizeof(*bmp.b)) is evaluated before bmp.w and bmp.h are initialized. Hence the undefined behavior.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
2

No, it's not safe.

Quoted from N1570 6.7.9 Initialization, emphasis mine:

23 The evaluations of the initialization list expressions are indeterminately sequenced with respect to one another and thus the order in which any side effects occur is unspecified.

In your code, calloc(bmp.w * bmp.h, sizeof(*bmp.b)) is not guaranteed to be evaluated before bmp.w and bmp.h are initialised. This means you have to write

int w = 7, h = 4, *b = calloc(w * h, sizeof *b);
struct Bitmap bmp = {w, h, b};

or

struct Bitmap bmp = {w, h, calloc(w * h, sizeof *b)};
nalzok
  • 14,965
  • 21
  • 72
  • 139