8

I'm using gcc 4.9.1/Mingw and compiling the code with:

gcc test.c -otest.exe -std=c11 -pedantic-errors -Wall -Wextra

This code gives a diagnostic:

int main (void)
{
  char a[5] = {'h','e','l','l','o','\0'};
}

error: excess elements in array initializer char a[5]

However, this code does not yield a warning:

int main (void)
{
  char b[5] = "hello";
}

I thought the two forms were 100% equivalent. Is there any reason or subtlety in the C standard etc why the latter should not give a warning?

Or is this a compiler bug? I know that the C standard allows excess initializers, unlike C++, so formally I don't believe gcc is required to give a diagnostic. But I would expect the compiler to give warnings consistently.

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 1
    Related to: [No compiler error when fixed size char array is initialized without enough room for null terminator](http://stackoverflow.com/q/20694796/1708801) – Shafik Yaghmour Jul 08 '15 at 15:12
  • The question is similar to the supposed duplicate, but I'm curious about the specific case of inconsistent warnings, which none of the answers to the supposed duplicate is addressing. I believe I also received some better answers here. Re-opening. (Feel free to close the other one as a duplicate to this one, I won't meddle with it since I'm partial :) ) – Lundin Jul 08 '15 at 15:44
  • 1
    "*I know that the C standard allows excess initializers, unlike C++*" -- What do you mean by this? As the answer to the duplicate question says, C explicitly permits a string literal of the exact length of the object; C++ does not. C does not permit excess initializers in general; `int arr[2] = { 1, 2, 3, 4 };` is illegal in both languages. – Keith Thompson Jul 08 '15 at 15:44
  • 2
    The warnings are not inconsistent. C has a specific special-case rule for string literals; it has no such rule for braced initializers. In `char b[5] = "hello";` the initializer does not provide a value for `b[5]`. – Keith Thompson Jul 08 '15 at 15:46
  • @KeithThompson I believe that is indeed why gcc behaves differently for the two cases. Ouah has cited the relevant parts covering both cases. – Lundin Jul 08 '15 at 15:46

3 Answers3

15

While:

 char a[5] = {'h','e','l','l','o','\0'};

is invalid.

(C11, 6.7.9p2) "No initializer shall attempt to provide a value for an object not contained within the entity being initialized."

This:

char b[5] = "hello";

is explicitly allowed by C (emphasis mine):

(C11, 6.7.9p14) "An array of character type may be initialized by a character string literal or UTF−8 string literal, optionally enclosed in braces. Successive bytes of the string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array."

But

 char b[5] = "hello!";

is invalid.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • It would seem to me then that §1 and §14 contradict each other, because a string literal is still an initializer. A bug in the C standard? – Lundin Jul 08 '15 at 15:18
  • 2
    @Lundin I don't take it as a bug but as a special provision when the initializer is a string literal. – ouah Jul 08 '15 at 15:27
9

It's a weird quirk in the C Standard. Back in the day, people occasionally used fixed-length, non-null-terminated strings. (One example was the 14-character filenames in V7 Unix.) So to let those old programs to continue to compile, it's legal to initialize an explicitly-sized char array with a string constant that ends up scraping off the '\0', as you've just observed.

I agree it's surprising that the {'h','e','l','l','o','\0'} initializer warned while the "hello" one did not. But these are two very different forms, and it turns out that the rules for them are different. When you give your array a size and you use the {} form, there must be room for all your initializers, period. But when you give a size and use the "" form, there's a special exception for that case and that case only.

(It's also not legal in C++ for either form.)

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • 1
    Albeit the code is valid, issuing a warning for `char b[5] = "hello";` would help most programmers avoid silly bugs. To avoid the warning, an explicit `char b[5] = { 'h', 'e', 'l', 'l', 'o' };` can always be used. – chqrlie Sep 07 '21 at 07:58
3

In

char b[5] = "hello";  

\0 is not appended to the string because array b is of size 5. This is valid. Compiler think of it as

char b[5] = {'h','e','l','l','o'};

Here b is an array of chars. But, it can't be used in a place where a string literal is to be supposed. For example, You can't use b in printf with %s specifier or str family function.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • Indeed. But why doesn't it warn consistently between the two cases? – Lundin Jul 08 '15 at 15:14
  • @Lundin; Because compiler knows that if the initializer is string literal then `\0` may or may not be appended depends on the size of array/buffer. – haccks Jul 08 '15 at 15:20
  • 1
    *not appended to the string because array a* You meant array `b`, right? But this is not enough to explain why `char b[5] = "hello";` is valid while `char b[5] = "hello!";` is not. – ouah Jul 08 '15 at 15:34