25

In my current project, which uses the MISRA 2004 standard, we use three GCC compilers, versions 3.2.3, 4.4.2 and 5.4.0.

We run build checks with the pedantic switch and c89 standard and a load of other restrictions. One of the restrictions is that all data must be initialised at declaration.

I have a problem in that on GCC 3.2.3, the universal zero initialiser {0} only compiles for arrays of the basic unitary types. If I have an array of structs, then I get a missing braces warning and the warning only goes away if I change {0} for {{0}}.

struct my_type my_thing[NUMBER_OF_THINGS] = {0};

becomes

struct my_type my_thing[NUMBER_OF_THINGS] = {{0}};

This does not work, however, for arrays of structs which have struct members. Then the problem is with the 4.4.2 compiler, which gives missing initialiser errors, so I had to do this:

struct my_struct_with_structs_inside my_other_thing[NUMBER_OF_THINGS] = {{0, 0, {0}, 0}};

This satisfies the compilers, but it trips our MISRA checker, because MISRA demands either the universal single {0} initialiser or the full, whole-array initialiser:

struct my_struct_with_structs_inside my_other_thing[NUMBER_OF_THINGS] = {{0, 0, {0}, 0},
                                                                         {0, 0, {0}, 0},
                                                                         {0, 0, {0}, 0},
                                                                         {0, 0, {0}, 0},
                                                                         {0, 0, {0}, 0}};

This is impractical for us because we have all sorts of restrictions and NUMBER_OF_THINGS may be changeable and be automatically generated from outside the source code at build time.

Nub and Crux

I would like to be able to say to my boss that the so-called universal initialiser {0} is sufficient for initialising any array. I have found threads on the GCC mailing lists and Bugzilla, going back many years, which consider the compiler warnings I've mentioned to be bugs, and stating that {0} is part of the standard. However, none of them mention which standard, and I have been unable to find {0} in the ISO C89 or C99 drafts, or K&R v2. Is the {0} a standard? Is there any way to guarantee that an array of structs with struct members is initialised to all zeros (or NULL)?

The trouble is that, while I can magic away the violations of the MISRA code, I am unsure that doing this:

struct my_struct_with_structs_inside my_other_thing[NUMBER_OF_THINGS] = {{0, 0, {0}, 0}};

...is sufficient to guarantee that the array will be completely zeroed.

Can anyone please offer wisdom from the root of this problem?

Lundin
  • 195,001
  • 40
  • 254
  • 396
Walkingbeard
  • 590
  • 5
  • 15
  • 1
    Does your code have to compile under GCC 3.2.3 or GCC 4.4.2? You say `{0}` doesn't compile on GCC 3.2.3. If your code has to compile under GCC 3.2.3, you're not going to be able to use things that don't compile on GCC 3.2.3, no matter how standard they are. – user2357112 Aug 11 '20 at 21:41

3 Answers3

24

The {0} initializer to initialize all objects in an aggregate (meaning array or struct) is 100% standard in any version of C. The syntax allows you to omit the braces for sub-aggregates.

I won't go into all the dirty details here. In case you are interested in the formal normative text, this is explained in a number of intricate rules that you can read about in for example C11 6.7.9 from §17 and forward.


Regarding MISRA-C:2004, this rule was a bit cumbersome and there is a MISRA-C:2004 TC1 regarding it. Your static analyser might not properly implement TC1 9.2 which stated that {0} on the top level is compliant.

Still, the TC1 didn't quite sort all things out. I asked the committee about this back in 2008 here:

https://www.misra.org.uk/forum/viewtopic.php?f=65&t=750

The reply I got there is a formal committee response and may be used as reference in your documentation. The committee agreed that the rule needed further improvement and based on this the rule was fixed in MISRA-C:2012 where {0} may be used anywhere to initialize subobjects of aggregate type.

I would recommend to use MISRA-C:2012 if possible.

Lundin
  • 195,001
  • 40
  • 254
  • 396
7

Yes. {0} is valid universal initializer in C89, too:

If there are fewer initializers in a list than there are members of an aggregate, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

It's true in all newer versions of the standard as well.

The warning you get from gcc is an old bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80454 which was fixed around gcc 4.9.

For your situation, you could consider:

  • upgrade to a newer gcc (3.2.3 is too old!)
  • Use -Wno-missing-braces option to silence the warning since this is a known bug
  • Use memset to initialize your struct types
P.P
  • 117,907
  • 20
  • 175
  • 238
  • 2
    Warning for the unlikely event that code is ran on some exotic system: `memset` won't initialize struct with pointer members correctly, if system uses non-zero bit pattern NULL pointers. – user694733 Aug 11 '20 at 10:49
  • I have a feeling that MISRA will frown on memsetting a struct – M.M Aug 11 '20 at 11:24
  • On the "exotic" systems: http://c-faq.com/null/machexamp.html But it's highly unlikely to an issue any more. – P.P Aug 11 '20 at 11:27
  • @M.M Possible. The preferred approach here, of course, should be to get `{0}` working (as that's both valid/correct & easy to read/understand). – P.P Aug 11 '20 at 11:37
  • 8
    People working under MISRA rules are working on safety-critical software. “Highly unlikely” is not an acceptable criterion for correctness and gets people killed. – Eric Postpischil Aug 11 '20 at 12:52
  • 3
    @EricPostpischil No one said it's an acceptable criterion. It's suggested as *one* of the potential options for *this* situation of OP's. I also linked with relevant info and it's up to OP and others to decide whether it's OK for them. Besides, answers are not only for OP alone - others who are writing non-MISRA environment may also have same/similar problem. But feel free to press criminal charges against me. – P.P Aug 11 '20 at 13:08
  • 3
    @user694733 Or floating point members for that matter. There is no guarantee that an all-zeros bit pattern represents a zero floating point value. – Ian Abbott Aug 11 '20 at 14:31
  • @M.M No, MISRA-C doesn't mention memset at all - it's regarded as fine to use. MISRA-C does however say that `NULL` is the only allowed form of null pointer constants, so one can potentially argue and say that memset to zero would violate that rule. – Lundin Aug 12 '20 at 08:09
  • @Lundin Would `{0}` violate that rule if the first member is a pointer? Does MISRA-C allow `{NULL}` as a "not quite universal" initializer in such cases? – Ian Abbott Aug 12 '20 at 09:04
  • @IanAbbott Yeah it would, and using `NULL` to initialize non-pointer objects might violate both standard C and MISRA-C. That's not the MISRA violation that the OP got, however. – Lundin Aug 12 '20 at 09:07
  • @IanAbbott: If the rule was changed to specifically allow `{0}` as a universal array initializer, that should settle the issue regardless of whether a literal zero would be an acceptable way of writing a pointer value otherwise. Semantically, I would regard the `0` as a "phony constant", in that it looks syntactically like a constant, but is really just part of an idiomatic expression for a universal initializer. – supercat Oct 08 '20 at 22:09
3

The "universal initializer" {0} should work. For C89, the relevant paragraphs of section 3.5.7 are:

Otherwise, the initializer for an object that has aggregate type shall be a brace-enclosed list of initializers for the members of the aggregate, written in increasing subscript or member order; and the initializer for an object that has union type shall be a brace-enclosed initializer for the first member of the union.

If the aggregate contains members that are aggregates or unions, or if the first member of a union is an aggregate or union, the rules apply recursively to the subaggregates or contained unions. If the initializer of a subaggregate or contained union begins with a left brace, the initializers enclosed by that brace and its matching right brace initialize the members of the subaggregate or the first member of the contained union. Otherwise, only enough initializers from the list are taken to account for the members of the first subaggregate or the first member of the contained union; any remaining initializers are left to initialize the next member of the aggregate of which the current subaggregate or contained union is a part.

If there are fewer initializers in a list than there are members of an aggregate, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

Ian Abbott
  • 15,083
  • 19
  • 33
  • 2
    And for completion, _"If an object that has static storage duration is not initialized explicitly, it is initialized implicitly as if every member that has arithmetic type were assigned 0 and every member that has pointer type were assigned a null pointer constant."_ – pipe Aug 11 '20 at 23:04