Re: Missing braces around {0} initializer?!

S

Seebs

I am puzzled by a warning, I get when using {0} as initializer. This
snippet demonstrate the warning:
There was no warning with { {0,0,0} }, but I thought { 0 } was perfectly
conforming C as initializer above. Is this warning a compiler specific
thing which I can turn off somehow, or am I completely missing something
in Standard C here???

I believe the answer is neither.

Rather, it's a *warning*. A helpful warning that, if you are initializing
an array of structures, it is clearer to put braces around the structure
initializers. Which it is. Most people won't be quite sure of the semantics
of something like

struct { int a, b, c; } foo[] = { 1, 2, 3, 4 };

and thus gcc warns you that you should probably have the right number of
levels of braces.

It's not saying your code violates the standard, just that your code has
characteristics which are very strongly suggestive that you have made a
typo.

-s
 
K

Keith Thompson

Seebs said:
I am puzzled by a warning, I get when using {0} as initializer. This
snippet demonstrate the warning:
There was no warning with { {0,0,0} }, but I thought { 0 } was perfectly
conforming C as initializer above. Is this warning a compiler specific
thing which I can turn off somehow, or am I completely missing something
in Standard C here???

I believe the answer is neither.

Rather, it's a *warning*. A helpful warning that, if you are initializing
an array of structures, it is clearer to put braces around the structure
initializers. Which it is. Most people won't be quite sure of the semantics
of something like

struct { int a, b, c; } foo[] = { 1, 2, 3, 4 };

and thus gcc warns you that you should probably have the right number of
levels of braces.

It's not saying your code violates the standard, just that your code has
characteristics which are very strongly suggestive that you have made a
typo.

Which are very strongly suggestive *to gcc*.

The initializer { 0 } sets all members of the initialized object to
zero. This is, or should be, a common C idiom. gcc just doesn't
recognize it.

gcc probably has an option to turn off the warning, but that option
will probably turn off some useful warnings as well. For example,
I definitly *want* a warning for
struct { int a, b, c; } foo[] = { 1, 2, 3, 4 };
but I don't want one for:
struct { int a, b, c; } foo[] = { 0 };
I don't think gcc distinguishes between those two cases.
 
A

Andrew Poelstra

The initializer { 0 } sets all members of the initialized object to
zero. This is, or should be, a common C idiom. gcc just doesn't
recognize it.

gcc probably has an option to turn off the warning, but that option
will probably turn off some useful warnings as well. For example,
I definitly *want* a warning for
struct { int a, b, c; } foo[] = { 1, 2, 3, 4 };
but I don't want one for:
struct { int a, b, c; } foo[] = { 0 };
I don't think gcc distinguishes between those two cases.

Even for

struct test {
int a;
int b;
int c;
}

int main(void) {
struct test t = { 0 };
return 0;
}

gcc warns me. And it's a particularly irritating warning because
it individually lists each struct member with a message that I am
"missing initialization".

So for some structs I get six or seven warning messages for every
single variable I initialize that way, so I then have to type out
struct test t = { 0, 0, 0 } like a goof or just not initialize it
at all (which gcc is perfectly fine with).
 
A

Andrew Poelstra

The initializer { 0 } sets all members of the initialized object to
zero. This is, or should be, a common C idiom. gcc just doesn't
recognize it.

gcc probably has an option to turn off the warning, but that option
will probably turn off some useful warnings as well. For example,
I definitly *want* a warning for
struct { int a, b, c; } foo[] = { 1, 2, 3, 4 };
but I don't want one for:
struct { int a, b, c; } foo[] = { 0 };
I don't think gcc distinguishes between those two cases.

Even for

struct test {
int a;
int b;
int c;
}

Missing a semicolon here.
int main(void) {
struct test t = { 0 };
return 0;
}

gcc warns me. And it's a particularly irritating warning because
it individually lists each struct member with a message that I am
"missing initialization".

For that specific test program, it only gave me two lines of warnings
(plus an "unused variable" message). The first was "missing initializer"
and the second was "near initialization of something".

So perhaps I am thinking of g++ warning of every member.
 
S

Seebs

The initializer { 0 } sets all members of the initialized object to
zero. This is, or should be, a common C idiom. gcc just doesn't
recognize it.

Hmm. I wouldn't have done that; I'd have done { { 0 } } to show that I was
aware that I was initializing an aggregate of aggregates.

I do agree that it might make sense to special-case the case where there's
exactly one initializer, and it's zero.

-s
 
B

Ben Pfaff

Andrew Poelstra said:
Even for

struct test {
int a;
int b;
int c;
}

int main(void) {
struct test t = { 0 };
return 0;
}

gcc warns me. And it's a particularly irritating warning because
it individually lists each struct member with a message that I am
"missing initialization".

It's annoying.

In practice I often end up doing this:

struct test {
int a;
int b;
int c;
};

#define TEST_INITIALIZER { 0, 0, 0 }

int main(void) {
struct test t = TEST_INITIALIZER;
return 0;
}
 
K

Kaz Kylheku

It's annoying.

GCC is becoming annoying. The project has been taken over by twits.

Here is a gem: they decided that it's okay to generate deliberately broken
code when the function can detect at compile time that a function pointer
is being ``misused'' (cast to a different function type, and used to make
a call).

So I upgrade the compiler, rebuild an embedded Linux distro, boot it, and blam!
OpenSSH fails on key generation with a strange error, aborting on some kind
of illegal instruction.

After spending the time to trace the problem, I ended up patching this
goddamned idiocy out of the compiler.

Can you believe the reasoning? Let's break the code /deliberately/ just because
it invokes undefined behavior, but since we can't prove that it's reached,
let's translate the program successfully anyway. Let's give the distro
maintainer a clean build, so he can boot the system and go hunting for the
problem in the sea of shiny new executables.
 
S

Seebs

Here is a gem: they decided that it's okay to generate deliberately broken
code when the function can detect at compile time that a function pointer
is being ``misused'' (cast to a different function type, and used to make
a call).

Specifically, when it's cast to a function type which does not match the
function *being called*.

And it should give you copious warnings when it triggers that, and it's
fixed as of OpenSSH 0.9.8f or so.

I seem to recall being told by someone closer to the code that the reason
is that otherwise the PPC compiler tended to blow up spectacularly trying
to optimize mismatched calls.

BTW, this sounds *eerily* familiar. When I encountered it, it involved
someone using a heavily-hacked OpenSSH 0.9.8 library, rather than the 0.9.8g
we shipped with that version of the product, because it had some specific
other bit of magic going on that was needed. I told the support people to
point the customer at the specific patches (I think that was 0.9.8f) to
openssh which fixed the calls to use the correct types.

But honestly, overall, I like that one. I am quite happy to have the compiler
throw out fierce warnings about something that is known to have a real chance
of blowing up in exciting ways, and to die cleanly rather than executing
garbage.

-s
 
K

Keith Thompson

Seebs said:
Hmm. I wouldn't have done that; I'd have done { { 0 } } to show that I was
aware that I was initializing an aggregate of aggregates.

Ok. Would you write { { { 0 } } } for an aggregate of aggregates of
aggregates? And so forth.
I do agree that it might make sense to special-case the case where there's
exactly one initializer, and it's zero.

Right. The point is that { 0 } means, regardless of the depth of the
object being initialized, that all members and elements are
initialized to zero (whatever "zero" means for the particular type).
The object can even be a scalar. And there's no other way to say
that.

On the other hand, { 0 } also means initializing a single-element
array or a single-member struct (or a union).

The rule that allows us to use { 0 } as a generic zero initializer is
C99 6.7.8p21:

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.

Because the syntax requires at least one initializer between the
braces, you have to initialize the first element explicitly and leave
the rest, if any, to be initialized implicitly.

I'd like to see the syntax expanded to allow empty braces:

some_type obj = { };

with the same meaning as:

some_type obj = { 0 };

With the "{ }" syntax, you're unambiguously leaving all the members to
be initialized implicitly (and there'd be nothing for gcc to complain
about).
 
S

Seebs

Ok. Would you write { { { 0 } } } for an aggregate of aggregates of
aggregates? And so forth.

By default, yes. I think it's clearer.
Because the syntax requires at least one initializer between the
braces, you have to initialize the first element explicitly and leave
the rest, if any, to be initialized implicitly.
Yup.

With the "{ }" syntax, you're unambiguously leaving all the members to
be initialized implicitly (and there'd be nothing for gcc to complain
about).

Yes, that would be prettier.

-s
 
B

Ben Bacarisse

Keith Thompson said:
Seebs <[email protected]> writes:

Right. The point is that { 0 } means, regardless of the depth of the
object being initialized, that all members and elements are
initialized to zero (whatever "zero" means for the particular type).
The object can even be a scalar. And there's no other way to say
that.

On the other hand, { 0 } also means initializing a single-element
array or a single-member struct (or a union).

.... or single scalar.
The rule that allows us to use { 0 } as a generic zero initializer is
C99 6.7.8p21:

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.

[just for completeness...] together with p11 in the same section:

The initializer for a scalar shall be a single expression,
optionally enclosed in braces. [....]

I'd like to see the syntax expanded to allow empty braces:

some_type obj = { };

with the same meaning as:

some_type obj = { 0 };

Nice idea.

<snip>
 
B

Ben Bacarisse

Tor Rustad said:
After investigating the gcc man page, I finally (after zillion scrolls):

-Wmissing-braces
Warn if an aggregate or union initializer is not fully
bracketed. In the following example, the initializer for a is not
fully bracketed, but that for b is fully
bracketed.

int a[2][2] = { 0, 1, 2, 3 };
int b[2][2] = { { 0, 1 }, { 2, 3 } };

This warning is enabled by -Wall.



sooo... it appears we need to turn off -Wall, and that's not something
I will do.

You need only turn off:

-Wno-missing-braces -Wno-missing-field-initializers

and {0} becomes a silent universal initializer. You will still be
warned about excess initializers but, sadly, this:

int primes[5] = { 2, 3, 5, 7 };

will be silent too. I can live with that.
 
P

Phil Carmody

Keith Thompson said:
Seebs said:
I am puzzled by a warning, I get when using {0} as initializer. This
snippet demonstrate the warning:
There was no warning with { {0,0,0} }, but I thought { 0 } was perfectly
conforming C as initializer above. Is this warning a compiler specific
thing which I can turn off somehow, or am I completely missing something
in Standard C here???

I believe the answer is neither.

Rather, it's a *warning*. A helpful warning that, if you are initializing
an array of structures, it is clearer to put braces around the structure
initializers. Which it is. Most people won't be quite sure of the semantics
of something like

struct { int a, b, c; } foo[] = { 1, 2, 3, 4 };

and thus gcc warns you that you should probably have the right number of
levels of braces.

It's not saying your code violates the standard, just that your code has
characteristics which are very strongly suggestive that you have made a
typo.

Which are very strongly suggestive *to gcc*.

The initializer { 0 } sets all members of the initialized object to
zero. This is, or should be, a common C idiom. gcc just doesn't
recognize it.

Please provide proof that it doesn't recognise it. Please be more
detailed than the above - precisely which bit of the construct do
you think it is unable to cope with? From my perspective, it looks
as if it understands the construct and the legality, and typical use,
of same, perfectly.

Phil
 
P

Phil Carmody

Kaz Kylheku said:
Can you believe the reasoning? Let's break the code /deliberately/ just because
it invokes undefined behavior

If it's UB, it's already broken, I don't think you can blame
the compiler for shoving that in your face so abruptly. Perhaps
you should thank it, instead?

Phil
 
K

Keith Thompson

Phil Carmody said:
Keith Thompson said:
Seebs said:
I am puzzled by a warning, I get when using {0} as initializer. This
snippet demonstrate the warning:

There was no warning with { {0,0,0} }, but I thought { 0 } was perfectly
conforming C as initializer above. Is this warning a compiler specific
thing which I can turn off somehow, or am I completely missing something
in Standard C here???

I believe the answer is neither.

Rather, it's a *warning*. A helpful warning that, if you are initializing
an array of structures, it is clearer to put braces around the structure
initializers. Which it is. Most people won't be quite sure of the semantics
of something like

struct { int a, b, c; } foo[] = { 1, 2, 3, 4 };

and thus gcc warns you that you should probably have the right number of
levels of braces.

It's not saying your code violates the standard, just that your code has
characteristics which are very strongly suggestive that you have made a
typo.

Which are very strongly suggestive *to gcc*.

The initializer { 0 } sets all members of the initialized object to
zero. This is, or should be, a common C idiom. gcc just doesn't
recognize it.

Please provide proof that it doesn't recognise it. Please be more
detailed than the above - precisely which bit of the construct do
you think it is unable to cope with? From my perspective, it looks
as if it understands the construct and the legality, and typical use,
of same, perfectly.

Hmm. I really thought I was sufficiently clear.

gcc, like any conforming C compiler, certainly recognizes the
initializer { 0 } and correctly handles its syntax and semantics.
What I meant is that it doesn't recognize it *as an idiom* for
"recursively initialize this object and all its sub-members and
sub-elements to zero".

Consider the following translation unit:

struct s { int x; int y; int z; };
struct s s1 = { 0 };
struct s s2 = { 0, 0 };

With "-Wmissing-field-initializers" or equivalent, gcc warns about
both initializers. With "-Wno-missing-field-initializers", gcc
doesn't warn about either of them.

What I would like is a way to make gcc, or whatever compiler I happen
to be using, to warn about the initialization of s2 (because it's
missing an initializer for z), but not about the initialization
of s1 (because it's using the "{ 0 }" idiom, and I *deliberately*
omitted explicit initializers for most of the members).

One possible drawback is that if I write "{ 0 }", I might really
have meant that the object has exactly one member or element rather
than that I don't want to specify how many it has.

What I'd like even better is a change to the language so that "{ }"
is equivalent to what "{ 0 }" currently means, but I can expect a
compiler not to warn me about it.

I have another idea, but it would break tons of existing code, so it's
unlikely ever to make it into the language. By default, require
explicit initializers for all subobjects. Allow "..." to indicate
that the current subobject and everything following it is implicitly
zero. Thus:

struct s s1 = { 0 }; /* constraint violation */
struct s s2 = { 42, ... }; /* x = 42, y = 0, z = 0 */
struct s s3 = { ... }; /* initialize all members to 0 */
struct s s4 = { 1, 2, 3, ... }; /* probably ok */

There would be a lot of details to work out; since it would break
existing code, I won't bother.
 
A

Andrew Poelstra

If it's UB, it's already broken, I don't think you can blame
the compiler for shoving that in your face so abruptly. Perhaps
you should thank it, instead?

Phil

I think his point was that it /didn't/ give him an abrupt
warning. It went out of its way to interpret a nonsensical
construct as nonsense rather than issuing a warning.

So it caused a runtime crash.

At least, I think that's what happened. I wasn't quite
clear on his original post either.
 
B

Ben Bacarisse

William Ahern said:
What version of GCC are you using? I can't replicate that warning with any
of 3.3.5 (OpenBSD), 4.0.1 (Apple), 4.2.1 (Apple), 4.2.4 (Ubuntu), 4.3.3
(GCC), 4.3.4 (Ubuntu), or 4.4.1 (Ubuntu). I've tried -Wall with every
combination of -ansi -pedantic xor -std=c99, and -O2 xor no
optimization.

It's turned on with -Wextra.

<snip>
 
S

Seebs

I think his point was that it /didn't/ give him an abrupt
warning. It went out of its way to interpret a nonsensical
construct as nonsense rather than issuing a warning.

I am pretty sure that ALWAYS generates a warning. What it doesn't do
is abort compilation.

-s
 
P

Phil Carmody

Keith Thompson said:
Phil Carmody said:
Keith Thompson said:
I am puzzled by a warning, I get when using {0} as initializer. This
snippet demonstrate the warning:

There was no warning with { {0,0,0} }, but I thought { 0 } was perfectly
conforming C as initializer above. Is this warning a compiler specific
thing which I can turn off somehow, or am I completely missing something
in Standard C here???

I believe the answer is neither.

Rather, it's a *warning*. A helpful warning that, if you are initializing
an array of structures, it is clearer to put braces around the structure
initializers. Which it is. Most people won't be quite sure of the semantics
of something like

struct { int a, b, c; } foo[] = { 1, 2, 3, 4 };

and thus gcc warns you that you should probably have the right number of
levels of braces.

It's not saying your code violates the standard, just that your code has
characteristics which are very strongly suggestive that you have made a
typo.

Which are very strongly suggestive *to gcc*.

The initializer { 0 } sets all members of the initialized object to
zero. This is, or should be, a common C idiom. gcc just doesn't
recognize it.

Please provide proof that it doesn't recognise it. Please be more
detailed than the above - precisely which bit of the construct do
you think it is unable to cope with? From my perspective, it looks
as if it understands the construct and the legality, and typical use,
of same, perfectly.

Hmm. I really thought I was sufficiently clear.

gcc, like any conforming C compiler, certainly recognizes the
initializer { 0 } and correctly handles its syntax and semantics.
What I meant is that it doesn't recognize it *as an idiom* for
"recursively initialize this object and all its sub-members and
sub-elements to zero".

Hmm. I really thought I was sufficiently clear. The above is no
proof that it's not recognised as an idiom for the above. Just
because it's recognised as an idiom doesn't mean the compiler can't
throw up warnings about it. Calling something an idiom doesn't
magically make everything that looks like it safe and not worth
warning about. Common mistakes can be idiomatic of a language too.
Consider the following translation unit:

struct s { int x; int y; int z; };
struct s s1 = { 0 };
struct s s2 = { 0, 0 };

With "-Wmissing-field-initializers" or equivalent, gcc warns about
both initializers. With "-Wno-missing-field-initializers", gcc
doesn't warn about either of them.

What I would like is a way to make gcc, or whatever compiler I happen
to be using, to warn about the initialization of s2 (because it's
missing an initializer for z), but not about the initialization
of s1 (because it's using the "{ 0 }" idiom, and I *deliberately*
omitted explicit initializers for most of the members).

One possible drawback is that if I write "{ 0 }", I might really
have meant that the object has exactly one member or element rather
than that I don't want to specify how many it has.

Exactly. Treating elision to denote intent is dangerous. Sufficiently
dangerous for gcc to consider it worth a warning with most common
invocations.
What I'd like even better is a change to the language so that "{ }"
is equivalent to what "{ 0 }" currently means, but I can expect a
compiler not to warn me about it.

I approve of that, certainly.
I have another idea, but it would break tons of existing code, so it's
unlikely ever to make it into the language. By default, require
explicit initializers for all subobjects. Allow "..." to indicate
that the current subobject and everything following it is implicitly
zero. Thus:

struct s s1 = { 0 }; /* constraint violation */
struct s s2 = { 42, ... }; /* x = 42, y = 0, z = 0 */
struct s s3 = { ... }; /* initialize all members to 0 */
struct s s4 = { 1, 2, 3, ... }; /* probably ok */

There would be a lot of details to work out; since it would break
existing code, I won't bother.

I approve of an explicit '...' to represent an explicit request
(to initialise all further subobjects to 0) too. Only the orthogonal
addition of the requirement for other initialisations to be fully
explicit causes breakage.

Phil
 
B

Ben Bacarisse

William Ahern said:
Ah, thank you.

This C99 named initialization construct works w/o any warning:

struct test t = { .a = 0 };

I want to say that the standard explicitly says that every other member is
implicitly zero initialized in this case, but that's just from my poor
memory.

Yes they are. 6.7.8 p19:

The initialization shall occur in initializer list order, each
initializer provided for a particular subobject overriding any
previously listed initializer for the same subobject; all subobjects
that are not initialized explicitly shall be initialized implicitly
the same as objects that have static storage duration.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,992
Messages
2,570,220
Members
46,805
Latest member
ClydeHeld1

Latest Threads

Top