4

I played around with designated initializers a bit the other day and noticed, to my surprise, that it is valid to use the same index more than once. What's more, it didn't even produce a compiler warning, error, or even informational statement when I did so, and even PC-Lint didn't seem to care (which I think surprised me the most).

I'm wondering if there's a reason for compilers not even providing an information message in this case or if there are additional compiler/lint/etc. options that may be available to catch or flag this.

Tools used: Renesas RX Standard Toolchain v1.2.0.0 (C99), gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1) (in a VM), Lint-NT 9.00i


For example, some old code I'm working on #defines a bunch of commands and then creates an array of command structures (drastically simplified here) to be cycled through to find and use that particular command:

#define CMD_RMEM 0
#define CMD_WMEM 1
#define CMD_XCRC 2
#define CMD_NULL 3

typedef struct
{
  const char  cmdID;
  const char* cmdStr;
} CMD;

const CMD commands[] = {
  {CMD_RMEM,"RMEM"},
  {CMD_WMEM,"WMEM"},
  {CMD_XCRC,"XCRC"},
  {CMD_NULL,"NULL"},
};

Then I remembered the designated initializer syntax I'd seen somewhere and thought it may provide more flexibility on the arrangement of items in the array in addition to detecting duplicate command indices, e.g.:

//(same #def's & typedef as above)
const CMD commands[] = {
  [CMD_RMEM] = {CMD_RMEM,"RMEM"},
  [CMD_NULL] = {CMD_NULL,"NULL"}, //different order in ititializer list,
    // but designation keeps it in the same array/memory position, so
    // this will still be the 'last' element
  [CMD_CMEM] = {CMD_CMEM,"CMEM"},
  [CMD_WMEM] = {CMD_WMEM,"WMEM"},
  [CMD_XCRC] = {CMD_XCRC,"XCRC"},
};

would produce the same effect as the initial code above but with flexibility in arranging the items in the array declaration (which it does) and (I was thinking/hoping) that

#define CMD_RMEM 0
#define CMD_WMEM 1
#define CMD_XCRC 1 // obvious dupe in a short list, but not so obvious
  // if part of a much longer list or if split among multiple files
#define CMD_NULL 2

// (Same designated initializer section as above)

would generate at least a warning since we're using the same designated index more than once (which it does not) (this example may easily result from adding a command without shifting the last NULL placeholder).


The GCC Designated Inits page notes a GNU extension which allows you to use ranges in the initializers, which I can see being useful with the ability to define the whole range and then override certain parts (e.g. int arr[] = {[0 ... 99] = -1, [42] = 1}, but I don't understand why it's still not at least flagged at some level...

Farther down in the same GCC page, it does address the topic of multiple fields, but doesn't explain why it behaves as it does: "If the same field is initialized multiple times, it has the value from the last initialization. If any such overridden initialization has side-effect, it is unspecified whether the side-effect happens or not. Currently, GCC discards them and issues a warning."

This IBM page shows an interesting example as well, but still doesn't answer (at least for me) why it is not at least some sort of build message...

And finally, this gcc patch page from Dec 2000 indicates duplicate checks were explicitly removed, but doesn't (from what I read briefly) explain why.

So, why is this (seemingly) glossed over? And is there any way to have the compiler (or even lint) flag this (to provide more safety)(in c/c99; don't just say 'use c++' or something :p)?

cafce25
  • 15,907
  • 4
  • 25
  • 31
johnny
  • 4,024
  • 2
  • 24
  • 38
  • Just wondering: why don't you use an `enum` instead that `#define`? – Jack May 24 '13 at 19:39
  • @Jack, it's just old code; could refactor to use enums, but the RX compiler does complain about implicit enum/int conversions, so presumably lots of stuff would have to be changed... baby steps :p – johnny May 24 '13 at 19:43
  • I wholly understand and share your interest in being _able_ to get a warning... but I do find it quite nifty and perhaps even good and useful to leverage this for initialization (see [my example](http://ideone.com/uuy57V)). – altendky Apr 16 '14 at 23:41
  • 1
    Note that GCC (5.3.0 tested, but I think it applies to earlier versions) has a specific option `-Woverride-init` to warn about overridden initializers (repeated initializers for members of a structure elements of an array); it is also activated by `-Wextra`. I don't know whether `clang` had it before `gcc` or vice versa, or why the names are different (`-Winitializer-overrides` for `clang`). Neither recognizes the other's option as equivalent to its own. If the duplication was deliberate or unavoidable, you could suppress the option with `-Wno-override-init` or `-Wno-initializer-overrides`. – Jonathan Leffler Feb 20 '16 at 19:04

2 Answers2

2

I have no explanation as to why —perhaps simply because designated initializers are still new and little-used. Compiler makers have to consider the number of programmers who will benefit from new features.

Clang warns for your construct:

$ clang  -std=c99 -Wall -c t.c
t.c:24:17: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
  [CMD_XCRC] = {CMD_XCRC,"XCRC"},
                ^~~~~~~~
t.c:4:18: note: expanded from macro 'CMD_XCRC'
#define CMD_XCRC 1 // obvious dupe in a short list, but not so obvious
                 ^
t.c:23:17: note: previous initialization is here
  [CMD_WMEM] = {CMD_WMEM,"WMEM"},
                ^~~~~~~~
t.c:2:18: note: expanded from macro 'CMD_WMEM'
#define CMD_WMEM 1
                 ^

(I had to make minor changes for it to compile but you get the idea.)

$ clang -v
Apple LLVM version 4.2 (clang-425.0.24) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.3.0
Thread model: posix
Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • ah, clang - haven't used that much (and it's been a while at that). Good to know there's at least one tool that would warn me :) – johnny May 24 '13 at 20:08
  • Marking this as the accepted answer since it suggests a tool/option that does flag this behavior (as requested). Even though JensGustedt's answer has twice as many votes (2-1, with the 1 being mine :p) and does sort of address the compiler warning issue, I still don't quite understand why it doesn't allow for even an informational message or warning at higher warning levels. – johnny Jun 03 '13 at 14:48
2

The standard foresees that there can be duplicates and imposes a strategy to resolve this, the last initializer wins. So warnings for code that explicitly is validated by the standard would be just annoying. (as is e.g the warning that some compilers give for the "zero" initializer {0})

If your question is more about the motivation, I'd guess that enumerations are the main reason for that. It is not uncommon that enumerations have repeated values, so initializing an array with these would cause major headaches.

Other motivations for that might be more involved. Have a look at

bool const usedSizes[] = { [sizeof(int)] = true, [sizeof(long)] = true, [sizeof(long long)] = true };

Here easily duplicates of initializers can occur, but the concrete values are highly platform dependent.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • I can see it being an annoyance when you actually do intend to use it that way, but I can't help thinking of how lint tends to complain a lot about some things and gets annoying sometimes, but also has options to supress certain warnings/errors). The compilers mentioned don't say _anything_ even at the highest warning/error levels (at least from what I saw). – johnny May 24 '13 at 19:57
  • I like your answer and appreciate the information about the standard's resolution strategy, but the good ship “warning for code explicitly validated by the standard” set sail ages ago when the standard decided to validate `if (a=b) …`. – Pascal Cuoq May 24 '13 at 19:58
  • As for the enumeration point, I see that being even more motivation for flagging this behavior - if you had an enum/enum-string structure with a duplicate enum index whose string then overrides the string of the other, you may want to know about that... examples could go either way, I think... – johnny May 24 '13 at 19:59
  • 1
    @PascalCuoq, I think there is a difference between "being allowed" by the standard and "explicitly giving a rule when a certain construct occurs". – Jens Gustedt May 24 '13 at 20:01