ridiculous warning or not?

K

kerravon

Is this warning considered ridiculous?

I'll stop using that syntax if the compiler is
more correct than me.


% cat foo.c

char f[];



int main(void) { return (0); }



% cc foo.c

"foo.c", line 1: warning: null dimension: f




% cc -V

cc: Sun C 5.9 SunOS_sparc Patch 124867-01 2007/07/12

usage: cc [ options] files. Use 'cc -flags' for details
 
E

Eric Sosman

Is this warning considered ridiculous?

Not by me.
I'll stop using that syntax if the compiler is
more correct than me.

% cat foo.c

char f[];

This line attempts to define an array. What is an array?
A collection of a fixed number of elements, all of the same type.
What kind of elements does this array contain? `char', it seems:
That's fine. How many? Um, er, ah, well, ...

Perhaps you intended `extern char f[];', which would be a
different matter entirely: Not an attempt to define an array,
but a declaration that an array is defined somewhere else --
and that the size will be found at that "somewhere else."
int main(void) { return (0); }



% cc foo.c

"foo.c", line 1: warning: null dimension: f

Another compiler says

foo.c:1: warning: array 'f' assumed to have one element
 
A

Andrew Cooper

Is this warning considered ridiculous?
No


I'll stop using that syntax if the compiler is
more correct than me.


% cat foo.c

char f[];

I hope you are not using 'char f[];' where you mean to use 'char * f;',
as they are very different things.

~Andrew
int main(void) { return (0); }



% cc foo.c

"foo.c", line 1: warning: null dimension: f




% cc -V

cc: Sun C 5.9 SunOS_sparc Patch 124867-01 2007/07/12

usage: cc [ options] files. Use 'cc -flags' for details
 
B

Ben Bacarisse

kerravon said:
Is this warning considered ridiculous?

I'll stop using that syntax if the compiler is
more correct than me.


% cat foo.c

char f[];

int main(void) { return (0); }

% cc foo.c

"foo.c", line 1: warning: null dimension: f

The warning is true, but possibly misleading. What you've written could
certainly be called a "null dimension" but the effect should be as if
you had written

char f[1];

The C standard gives meaning to this bizarre syntax for what I can only
imagine are historical reasons. There's no sensible reason to write
such code.

This makes it probable that others, who have suggested you did not mean
what you wrote, are right. One way or the other, you should stop using
this syntax because it either does not mean what you want, or (vanishing
unlikely) it's a confusing way to write what you do want (why would you
want am external one-element array?).
 
K

Keith Thompson

Ben Bacarisse said:
char f[]; [...]
"foo.c", line 1: warning: null dimension: f

The warning is true, but possibly misleading. What you've written could
certainly be called a "null dimension" but the effect should be as if
you had written

char f[1];

The C standard gives meaning to this bizarre syntax for what I can only
imagine are historical reasons. There's no sensible reason to write
such code.

This makes it probable that others, who have suggested you did not mean
what you wrote, are right. One way or the other, you should stop using
this syntax because it either does not mean what you want, or (vanishing
unlikely) it's a confusing way to write what you do want (why would you
want am external one-element array?).

As far as I can tell, the rule that `char f[];` is treated as `char
f[1];` isn't even stated explicitly.

N1570 6.9.2p2, External definitions, says:

A declaration of an identifier for an object that has file scope
without an initializer, and without a storage-class specifier or
with the storage-class specifier static, constitutes a *tentative
definition*. If a translation unit contains one or more tentative
definitions for an identifier, and the translation unit contains
no external definition for that identifier, then the behavior
is exactly as if the translation unit contains a file scope
declaration of that identifier, with the composite type as of
the end of the translation unit, with an initializer equal to 0.

An example (non-normative) says:

If at the end of the translation unit containing

int i[];

the array i still has incomplete type, the implicit initializer
causes it to have one element, which is set to zero on program
startup;

So this:

char f[];

is treated as:

char f[] = 0;

which causes `f` to inherit its size from the initializer.

Except that

char f[] = 0;

is invalid. 6.7.9p16 says:

Otherwise, the initializer for an object that has aggregate or union
type shall be a brace-enclosed list of initializers for the elements
or named members.

A twist: That "shall" is not in a constraint, so it's undefined
behavior, not something that requires a diagnostic.

Apparently we have to interpret the "initializer equal to 0" to mean
`{ 0 }` rather than `0` in this case. And we're supposed to infer
from that that `f` has exactly one element; apparently `{ 0 }` is
"equal to 0" but `{ 0, 0 }` isn't.
 
K

kerravon

I'll stop using that syntax if the compiler is
more correct than me.

Wow, just wow. Thanks everyone for your responses.

I'll tell you a story. I normally don't have a
signature in my emails, but someone at work
complained that I was asking them to do things
and they had no idea who I was, and could I thus
include a signature.

The standard template for a signature includes a
job title. I'm a contractor so I didn't think I
had one, other than "contractor", so I just made
up "Junior C Programmer", since as I have stated
my whole career, my career goal is to have a title
of "Senior C Programmer", and I wanted to have
a goal to work towards. Note that I have been
programming in C since 1987. Also note that I have
written my own C runtime library for DOS/Windows/
Unix/OS2/MVS/CMS/VSE, which you can find here:

http://pdos.sourceforge.net

along with not one but two operating systems written
from scratch.

I also spent years getting GCC ported to MVS, CMS
and VSE, which were the very last commercial
programming environments that didn't have a free,
or standard, C compiler. You can find that here:

http://gccmvs.sourceforge.net

But despite all that, it took about 10 minutes for
comp.lang.c to (correctly) point out that the title
of "Junior C Programmer" was probably the right thing
to run with. I later found out (from Lync status
reported by others), that I actually do have an
official title, which is "Senior Software Engineer",
so I updated my title to that, but in spirit, I'm
still learning C. I actually bought the ISO C90
standard (or its AS3955-1991 equivalent), and it
really is a beautiful document, but I've never
read it cover to cover yet, so the title of "Junior"
is well-deserved.

I thought that int **x was the same as int *x[]
anywhere, but your pointed questions made me realise
just how messed up my thinking was (despite my
high (misplaced) confidence that I was right and
the compiler was being ridiculous).

Thanks. Paul.
 
8

88888 Dihedral

I'll stop using that syntax if the compiler is
more correct than me.



Wow, just wow. Thanks everyone for your responses.



I'll tell you a story. I normally don't have a

signature in my emails, but someone at work

complained that I was asking them to do things

and they had no idea who I was, and could I thus

include a signature.



The standard template for a signature includes a

job title. I'm a contractor so I didn't think I

had one, other than "contractor", so I just made

up "Junior C Programmer", since as I have stated

my whole career, my career goal is to have a title

of "Senior C Programmer", and I wanted to have

a goal to work towards. Note that I have been

programming in C since 1987. Also note that I have

written my own C runtime library for DOS/Windows/

Unix/OS2/MVS/CMS/VSE, which you can find here:



http://pdos.sourceforge.net



along with not one but two operating systems written

from scratch.



I also spent years getting GCC ported to MVS, CMS

and VSE, which were the very last commercial

programming environments that didn't have a free,

or standard, C compiler. You can find that here:



http://gccmvs.sourceforge.net



But despite all that, it took about 10 minutes for

comp.lang.c to (correctly) point out that the title

of "Junior C Programmer" was probably the right thing

to run with. I later found out (from Lync status

reported by others), that I actually do have an

official title, which is "Senior Software Engineer",

so I updated my title to that, but in spirit, I'm

still learning C. I actually bought the ISO C90

standard (or its AS3955-1991 equivalent), and it

really is a beautiful document, but I've never

read it cover to cover yet, so the title of "Junior"

is well-deserved.



I thought that int **x was the same as int *x[]

anywhere, but your pointed questions made me realise

just how messed up my thinking was (despite my

high (misplaced) confidence that I was right and

the compiler was being ridiculous).



Thanks. Paul.

int **x ; // a pointer to a pointer of an integer
int* x2[4000]; // an array of 4000 pointers to integers in the heap?
int i,j; //i-n for ineger idexing

// malloc might fail

for(j=0;j<4000;j++)
if (( x2[j]=malloc(4000* sizeof(int *))) ==NULL)
{i=-1; break; }

if (i==-1)
{ //error trapping here ....}

//x2[j] of 4000X4000 integers here can be used

x=x2 ; // an alias of x2


Can you get the difference?
 
B

Ben Bacarisse

88888 Dihedral said:
int **x ; // a pointer to a pointer of an integer
int* x2[4000]; // an array of 4000 pointers to integers in the heap?
int i,j; //i-n for ineger idexing

// malloc might fail

for(j=0;j<4000;j++)
if (( x2[j]=malloc(4000* sizeof(int *))) ==NULL)

Wrong type in the malloc call. Using the often quoted idiom here would
have prevented this problem:

x2[j] = malloc(4000 * sizeof *x2[j])
{i=-1; break; }

if (i==-1)
{ //error trapping here ....}

i may be -1 even without any error.
//x2[j] of 4000X4000 integers here can be used

x=x2 ; // an alias of x2

Can you get the difference?


Do you mean that they behave differently as operands of sizeof and of &?
 
8

88888 Dihedral

int **x ; // a pointer to a pointer of an integer
int* x2[4000]; // an array of 4000 pointers to integers in the heap?
int i,j; //i-n for ineger idexing
// malloc might fail
for(j=0;j<4000;j++)

if (( x2[j]=malloc(4000* sizeof(int *))) ==NULL)



Wrong type in the malloc call. Using the often quoted idiom here would

have prevented this problem:



x2[j] = malloc(4000 * sizeof *x2[j])


{i=-1; break; }

if (i==-1)
{ //error trapping here ....}



i may be -1 even without any error.

You are right check j for the value that equals 4001 or not is the
more elegant way.
//x2[j] of 4000X4000 integers here can be used
x=x2 ; // an alias of x2
Can you get the difference?




Do you mean that they behave differently as operands of sizeof and of &?



OK, I'll prevent the array allocated on the stack to be not destroyed
first if I want to use int ** x outside the function that defines
int* x2[4000].
 
I

Ike Naar

int **x ; // a pointer to a pointer of an integer
int* x2[4000]; // an array of 4000 pointers to integers in the heap?
int i,j; //i-n for ineger idexing

// malloc might fail

for(j=0;j<4000;j++)
if (( x2[j]=malloc(4000* sizeof(int *))) ==NULL)

Wrong type in the malloc call. Using the often quoted idiom here would
have prevented this problem:

x2[j] = malloc(4000 * sizeof *x2[j])
{i=-1; break; }

if (i==-1)
{ //error trapping here ....}

i may be -1 even without any error.

You are right check j for the value that equals 4001 or not is the
more elegant way.

Not 4001, but 4000.
 
8

88888 Dihedral

<snip>
int **x ; // a pointer to a pointer of an integer
int* x2[4000]; // an array of 4000 pointers to integers in the heap?
int i,j; //i-n for ineger idexing

// malloc might fail

for(j=0;j<4000;j++)
if (( x2[j]=malloc(4000* sizeof(int *))) ==NULL)

Wrong type in the malloc call. Using the often quoted idiom here would
have prevented this problem:

x2[j] = malloc(4000 * sizeof *x2[j])

{i=-1; break; }

if (i==-1)
{ //error trapping here ....}

i may be -1 even without any error.
You are right check j for the value that equals 4001 or not is the
more elegant way.



Not 4001, but 4000.

Yeh, you are right! //for zero based indexing
 
F

Fred K

The indexing base is irrelevant.
The relevant part is that as the loop is written,
the loop continues only if i < 4000
 
T

Tim Rentsch

Kenneth Brody said:
Is this warning considered ridiculous?

I'll stop using that syntax if the compiler is
more correct than me.

It depends on what your goal is.
% cat foo.c

char f[];

Did you mean "extern char f[];"? As in, "there is an array of chars
defined elsewhere, given the name 'f', and of unknown size"?

If you didn't mean "extern", then what, exactly, is that statement
supposed to mean?

Perhaps it's supposed to mean just what the C Standard
says it means: that 'f' is a character array; that
the character array 'f' is defined in this translation
unit; that the array could be given a specific size
in a later declaration, or later definition; and that,
if not given a specific size explicitly (perhaps as a
consequence of a subsequent definition including an
initializer), it will be defined implicitly as having
one element, initialized using the value zero.
 
T

Tim Rentsch

Ben Bacarisse said:
kerravon said:
% cat foo.c

char f[];

int main(void) { return (0); }
[snip] the effect should be as if you had written

char f[1];

The C standard gives meaning to this bizarre syntax for what I can only
imagine are historical reasons. There's no sensible reason to write
such code. [snip]

Here's one possible use case

typedef int (*Converter)( int );
Converter foos[];

int foo1( int x ){ ... }
int foo2( int x ){ ... }
int foo3( int x ){ ... }
...

Convertor foos[] = { foo1, foo2, foo3, ... etc ... };

Personally I think there is value in giving a declaration that
also stipulates the item being declared will be defined in
this source file. Obviously this example could be done
instead using 'extern' but for cases like this one I normally
would prefer omitting the 'extern' in the source file that
defines 'foos'.

The default semantics of there nominally being one element
is, I would guess, simply a consequence of the default
initialization rules.

Incidentally, a similar pattern is legal with struct's (or union's):

struct NotYetComplete something;

... functions ...

struct NotYetComplete {
... define struct contents here ...
};

... functions that can access the internals of 'something' ...
 
T

Tim Rentsch

Keith Thompson said:
Ben Bacarisse said:
char f[]; [...]
"foo.c", line 1: warning: null dimension: f

The warning is true, but possibly misleading. What you've written could
certainly be called a "null dimension" but the effect should be as if
you had written

char f[1];

The C standard gives meaning to this bizarre syntax for what I can only
imagine are historical reasons. There's no sensible reason to write
such code.

This makes it probable that others, who have suggested you did not mean
what you wrote, are right. One way or the other, you should stop using
this syntax because it either does not mean what you want, or (vanishing
unlikely) it's a confusing way to write what you do want (why would you
want am external one-element array?).

As far as I can tell, the rule that `char f[];` is treated as `char
f[1];` isn't even stated explicitly.

N1570 6.9.2p2, External definitions, says:

A declaration of an identifier for an object that has file scope
without an initializer, and without a storage-class specifier or
with the storage-class specifier static, constitutes a *tentative
definition*. If a translation unit contains one or more tentative
definitions for an identifier, and the translation unit contains
no external definition for that identifier, then the behavior
is exactly as if the translation unit contains a file scope
declaration of that identifier, with the composite type as of
the end of the translation unit, with an initializer equal to 0.

An example (non-normative) says:

If at the end of the translation unit containing

int i[];

the array i still has incomplete type, the implicit initializer
causes it to have one element, which is set to zero on program
startup;

So this:

char f[];

is treated as:

char f[] = 0;

No, it would be 'char f[] = {0};' as the implementation is
obliged to do by 6.7.9p16 -- implementations are obliged to
follow "shall" directives.
which causes `f` to inherit its size from the initializer.

Except that

char f[] = 0;

is invalid. 6.7.9p16 says:

Otherwise, the initializer for an object that has aggregate or union
type shall be a brace-enclosed list of initializers for the elements
or named members.

A twist: That "shall" is not in a constraint, so it's undefined
behavior, not something that requires a diagnostic.

Apparently we have to interpret the "initializer equal to 0" to mean
`{ 0 }` rather than `0` in this case. And we're supposed to infer
from that that `f` has exactly one element; apparently `{ 0 }` is
"equal to 0" but `{ 0, 0 }` isn't.

{ 0, 0 } is two initializers, not one.
 
K

Keith Thompson

Tim Rentsch said:
As far as I can tell, the rule that `char f[];` is treated as `char
f[1];` isn't even stated explicitly.

N1570 6.9.2p2, External definitions, says:

A declaration of an identifier for an object that has file scope
without an initializer, and without a storage-class specifier or
with the storage-class specifier static, constitutes a *tentative
definition*. If a translation unit contains one or more tentative
definitions for an identifier, and the translation unit contains
no external definition for that identifier, then the behavior
is exactly as if the translation unit contains a file scope
declaration of that identifier, with the composite type as of
the end of the translation unit, with an initializer equal to 0.

An example (non-normative) says:

If at the end of the translation unit containing

int i[];

the array i still has incomplete type, the implicit initializer
causes it to have one element, which is set to zero on program
startup;

So this:

char f[];

is treated as:

char f[] = 0;

No, it would be 'char f[] = {0};' as the implementation is
obliged to do by 6.7.9p16 -- implementations are obliged to
follow "shall" directives.

Implementations are obliged to follow *all* normative requirements
in the standard. 6.9.2p2 requires an implicit "initializer equal
to 0". I wouldn't say that `{0}` is "equal to 0".
which causes `f` to inherit its size from the initializer.

Except that

char f[] = 0;

is invalid. 6.7.9p16 says:

Otherwise, the initializer for an object that has aggregate or union
type shall be a brace-enclosed list of initializers for the elements
or named members.

A twist: That "shall" is not in a constraint, so it's undefined
behavior, not something that requires a diagnostic.

Apparently we have to interpret the "initializer equal to 0" to mean
`{ 0 }` rather than `0` in this case. And we're supposed to infer
from that that `f` has exactly one element; apparently `{ 0 }` is
"equal to 0" but `{ 0, 0 }` isn't.

{ 0, 0 } is two initializers, not one.

{ 0, 0 } is one initializer, which contains two more initializers.

N1370 6.7.9p1:

initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }

The idea that `char f[];` is implicitly `char f[1];` is sufficiently
non-obvious (and IMHO counterintuitive) that it should be stated
explicitly. We shouldn't have to depend on a flimsy and IMHO incorrect
chain of reasoning that appears only in a non-normative example.
 
B

Ben Bacarisse

Tim Rentsch said:
Ben Bacarisse said:
kerravon said:
% cat foo.c

char f[];

int main(void) { return (0); }
[snip] the effect should be as if you had written

char f[1];

The C standard gives meaning to this bizarre syntax for what I can only
imagine are historical reasons. There's no sensible reason to write
such code. [snip]

Here's one possible use case

typedef int (*Converter)( int );
Converter foos[];

int foo1( int x ){ ... }
int foo2( int x ){ ... }
int foo3( int x ){ ... }
...

Convertor foos[] = { foo1, foo2, foo3, ... etc ... };

Personally I think there is value in giving a declaration that
also stipulates the item being declared will be defined in
this source file. Obviously this example could be done
instead using 'extern' but for cases like this one I normally
would prefer omitting the 'extern' in the source file that
defines 'foos'.

A misunderstanding -- I use that style all the time. The thing I could
see no sensible reason for writing was what OP wrote: a tentative
definition and nothing else.
The default semantics of there nominally being one element
is, I would guess, simply a consequence of the default
initialization rules.

I don't quite see that. That the value should be zero, yes, but why
does it follow from them that there would be one element in the array?
Leaving the definition alone and thus making the object incomplete seems
to be just as natural a way to treat the situation.
Incidentally, a similar pattern is legal with struct's (or union's):

struct NotYetComplete something;

... functions ...

struct NotYetComplete {
... define struct contents here ...
};

... functions that can access the internals of 'something' ...

The same misunderstanding. Explicitly completing something later is
very natural. Having it be completed automatically is, to me, a little
odd.
 
T

Tim Rentsch

Ben Bacarisse said:
Tim Rentsch said:
[...snipped a lot, rearranged...]

A misunderstanding -- I use that style all the time.
Incidentally, a similar pattern is legal with struct's (or union's):

The same misunderstanding.

Okay, all good..
The thing I could see no sensible reason for writing was what
OP wrote: a tentative definition and nothing else. Explicitly
completing something later is very natural. Having it be
completed automatically is, to me, a little odd.

Probably I can imagine a scenario where havinng a default size
of one element would be useful. But it's easier to imagine
a scenario where having an option for a compiler warning is
more useful. :)

I don't quite see that. That the value should be zero, yes,
but why does it follow from them that there would be one
element in the array? Leaving the definition alone and thus
making the object incomplete seems to be just as natural a way
to treat the situation.

Ahh, because of the rule for how tentative definitions
are defined, without regard to incompleteness. Consider
this, a tentative definition for a complete struct type:

struct xyz { ... } some_struct; // tentative definition

... no explicit defintion, hence ...

struct xyz some_struct = { 0 }; // this definition supplied implicitly

Now apply that to a tentative definition for an array,
but with the array having an incomplete type:

int some_array[]; // tentative

... no explicit definition, hence ...

int some_array[] = { 0 }; // this definition supplied implicitly

You see how the default sizing rule falls out of the rule
for default initialization of objects tentatively defined
but not explicitly defined? That rule wasn't written with
incomplete types in mind (I am guessing), but it has the
effect of completing them in the case of incomplete arrays.
 
T

Tim Rentsch

Keith Thompson said:
Tim Rentsch said:
So this:

char f[];

is treated as:

char f[] = 0;

No, it would be 'char f[] = {0};' as the implementation is
obliged to do by 6.7.9p16 -- implementations are obliged to
follow "shall" directives.

Implementations are obliged to follow *all* normative requirements
in the standard. 6.9.2p2 requires an implicit "initializer equal
to 0". I wouldn't say that `{0}` is "equal to 0".

[...snip-some-more...]
{ 0, 0 } is two initializers, not one.

{ 0, 0 } is one initializer, which contains two more initializers.
[...snip elaboration...]

My comments were kind of half frivolous, because I find
it hard to believe someone would take this question so
seriously, when it seems perfectly obvious what is
intended. I do think my comments have some degree of
merit (and yours also), but not sufficient to overcome
the level of silliness present (for which I am more
to blame than you).

Therefore, rather than continuing on the particular
points above, I would like to quote from Defect Report #11
(slightly reformatted):

Question 3 Initialization of tentative definitions
---------------------------------------------------
If the file scope declaration

int i[10];

appears in a translation unit, subclause 6.7.2 suggests
that it is implicitly initialized as if

int i[10] = 0;

appears at the end of the translation unit. However, this
initializer is invalid, since subclause 6.5.7 prescribes
that the initializer for any object of array type must be
brace-enclosed. We believe that the intention of subclause
6.7.2 is that this declaration has an implicit initializer
of

int i[10] = {0};

Is this true?

Response
--------
Subclause 6.7.2 External object definitions contains the
following excerpt:

If a translation unit contains one or more tentative
definitions for an identifier, and the translation unit
contains no external definition for that identifier,
then the behavior is exactly as if the translation unit
contains a file scope declaration of that identifier,
with the composite type as of the end of the
translation unit, with an initializer equal to 0.

This statement describes an effect and not a literal token
sequence. Therefore, this example does not contain an
error.

Please note the first sentence in the last paragraph.
 

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,755
Messages
2,569,536
Members
45,017
Latest member
GreenAcreCBDGummiesReview

Latest Threads

Top