ridiculous warning or not?

Discussion in 'C Programming' started by kerravon, Aug 20, 2012.

  1. kerravon

    kerravon Guest

    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
    kerravon, Aug 20, 2012
    #1
    1. Advertising

  2. kerravon

    Eric Sosman Guest

    On 8/20/2012 5:41 AM, kerravon wrote:
    > 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

    --
    Eric Sosman
    d
    Eric Sosman, Aug 20, 2012
    #2
    1. Advertising

  3. kerravon

    army1987 Guest

    On Mon, 20 Aug 2012 02:41:21 -0700, kerravon wrote:

    > char f[];


    How big do you want that to be?



    --
    [ T H I S S P A C E I S F O R R E N T ]
    Troppo poca cultura ci rende ignoranti, troppa ci rende folli.
    -- fathermckenzie di it.cultura.linguistica.italiano
    <http://xkcd.com/397/>
    army1987, Aug 20, 2012
    #3
  4. On 20/08/2012 10:41, kerravon wrote:
    > 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
    >
    Andrew Cooper, Aug 21, 2012
    #4
  5. kerravon <> writes:

    > 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?).

    --
    Ben.
    Ben Bacarisse, Aug 21, 2012
    #5
  6. Ben Bacarisse <> writes:
    > kerravon <> writes:

    [...]
    >> 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.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Aug 21, 2012
    #6
  7. kerravon

    kerravon Guest

    On Monday, August 20, 2012 7:41:21 PM UTC+10, kerravon wrote:
    > 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.
    kerravon, Aug 21, 2012
    #7
  8. On Tuesday, August 21, 2012 6:25:29 PM UTC+8, kerravon wrote:
    > On Monday, August 20, 2012 7:41:21 PM UTC+10, kerravon wrote:
    >
    > > 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?
    88888 Dihedral, Aug 21, 2012
    #8
  9. 88888 Dihedral <> writes:
    <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.

    > //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 &?

    --
    Ben.
    Ben Bacarisse, Aug 21, 2012
    #9
  10. On Wednesday, August 22, 2012 4:07:24 AM UTC+8, Ben Bacarisse wrote:
    > 88888 Dihedral <> writes:
    >
    > <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.
    >
    >
    >
    > > //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 &?
    >
    >
    >
    > --
    >
    > Ben.



    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].
    88888 Dihedral, Aug 21, 2012
    #10
  11. kerravon

    Ike Naar Guest

    On 2012-08-21, 88888 Dihedral <> wrote:
    > On Wednesday, August 22, 2012 4:07:24 AM UTC+8, Ben Bacarisse wrote:
    >> 88888 Dihedral <> writes:
    >>
    >> <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.
    Ike Naar, Aug 21, 2012
    #11
  12. On Wednesday, August 22, 2012 6:09:36 AM UTC+8, Ike Naar wrote:
    > On 2012-08-21, 88888 Dihedral <> wrote:
    >
    > > On Wednesday, August 22, 2012 4:07:24 AM UTC+8, Ben Bacarisse wrote:

    >
    > >> 88888 Dihedral <> writes:

    >
    > >>

    >
    > >> <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
    88888 Dihedral, Aug 21, 2012
    #12
  13. kerravon

    Fred K Guest

    On Tuesday, August 21, 2012 3:13:20 PM UTC-7, 88888 Dihedral wrote:
    > On Wednesday, August 22, 2012 6:09:36 AM UTC+8, Ike Naar wrote: > On 2012-08-21, 88888 Dihedral <> wrote: > > > On Wednesday, August 22, 2012 4:07:24 AM UTC+8, Ben Bacarisse wrote: > > >> 88888 Dihedral <> writes: > > >> > > >> <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


    The indexing base is irrelevant.
    The relevant part is that as the loop is written,
    the loop continues only if i < 4000
    Fred K, Aug 22, 2012
    #13
  14. kerravon

    Tim Rentsch Guest

    Kenneth Brody <> writes:

    > On 8/20/2012 5:41 AM, kerravon wrote:
    >> 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.
    Tim Rentsch, Sep 7, 2012
    #14
  15. kerravon

    Tim Rentsch Guest

    Ben Bacarisse <> writes:

    > kerravon <> writes:
    >
    >> % 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' ...
    Tim Rentsch, Sep 7, 2012
    #15
  16. kerravon

    Tim Rentsch Guest

    Keith Thompson <> writes:

    > Ben Bacarisse <> writes:
    >> kerravon <> writes:

    > [...]
    >>> 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.
    Tim Rentsch, Sep 7, 2012
    #16
  17. Tim Rentsch <> writes:
    > Keith Thompson <> writes:

    [...]
    >> 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.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Will write code for food.
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Sep 7, 2012
    #17
  18. Tim Rentsch <> writes:

    > Ben Bacarisse <> writes:
    >
    >> kerravon <> writes:
    >>
    >>> % 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.

    --
    Ben.
    Ben Bacarisse, Sep 8, 2012
    #18
  19. kerravon

    Tim Rentsch Guest

    Ben Bacarisse <> writes:

    > Tim Rentsch <> writes:
    >> [...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. :)


    >> 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.


    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.
    Tim Rentsch, Sep 8, 2012
    #19
  20. kerravon

    Tim Rentsch Guest

    Keith Thompson <> writes:

    > Tim Rentsch <> writes:
    >> Keith Thompson <> writes:

    > [...snip-a-lot...]
    >>> 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...]
    >
    >>> 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.
    > [...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.
    Tim Rentsch, Sep 8, 2012
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. license_rant_master
    Replies:
    37
    Views:
    1,800
  2. Nebojsa Topolscak
    Replies:
    3
    Views:
    562
    Jon Caldwell
    Jan 14, 2005
  3. Replies:
    17
    Views:
    786
  4. Rob
    Replies:
    6
    Views:
    391
    brucie
    Dec 18, 2003
  5. Tomás

    This is ridiculous!

    Tomás, Mar 4, 2006, in forum: C++
    Replies:
    49
    Views:
    1,008
    Old Wolf
    Mar 13, 2006
Loading...

Share This Page