Unsized array declaration in a struct
Asked Answered
B

2

16

Why does C permit this:

typedef struct s
{
  int arr[];
} s;

where the array arr has no size specified?

Boudoir answered 26/11, 2013 at 15:19 Comment(2)
Standard C does not permit the structure shown. There must be at least one named member in the structure as well as the flexible array member or FAM (and the FAM must be last). A minimal structure might be typedef struct s { int size; int arr[]; } s; which allows you to record the size of the array (in the size member). That size is almost certainly necessary to be able to access the array member reliably.Coolidge
Try working in a language like (UCSD) Pascal that does not allow flexible arrays and you will appreciate this feature.Shimberg
P
15

This is C99 feature called flexible arrays, the main feature is to allow the use variable length array like features inside a struct and R.. in this answer to another question on flexible array members provides a list of benefits to using flexible arrays over pointers. The draft C99 standard in section 6.7.2.1 Structure and union specifiers paragraph 16 says:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. [...]

So if you had a s* you would allocate space for the array in addition to space required for the struct, usually you would have other members in the structure:

s *s1 = malloc( sizeof(struct s) + n*sizeof(int) ) ;

the draft standard actually has a instructive example in paragraph 17:

EXAMPLE After the declaration:

  struct s { int n; double d[]; };

the structure struct s has a flexible array member d. A typical way to use this is:

   int m = /* some value */;
   struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));

and assuming that the call to malloc succeeds, the object pointed to by p behaves, for most purposes, as if p had been declared as:

    struct { int n; double d[m]; } *p;

(there are circumstances in which this equivalence is broken; in particular, the offsets of member d might not be the same).

Pallmall answered 26/11, 2013 at 15:21 Comment(5)
it compiles with gcc -std=c89Boudoir
@Boudoir gcc may allow it as an extension outside of C99 like it does other featrues such as VLA, if you use -pedantic it should provide a warning if this is the case. Just tried gcc warns warning: ISO C90 does not support flexible array members [-pedantic].Pallmall
@Boudoir Odd as it may seem, the compiler option -std= does not enforce standard compliance. In order to compile according to a given standard you must use gcc -std=c89 -pedantic-errors. Which will correctly give "error: ISO C90 does not support flexible array members"Sanbo
OP's example is actually incorrect. The struct must have at least two members, of which one may be the flexible array member.Devitrify
@Devitrify yeah, I guess this is a GNU extension to flexible array members, hmmm.Pallmall
F
4

You are probably looking for flexible arrays in C99. Flexible array members are members of unknown size at the end of a struct/union.

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

You may also look at the reason for the struct hack in the first place.

It's not clear if it's legal or portable, but it is rather popular. An implementation of the technique might look something like this:

    #include <stdlib.h>
    #include <string.h>

    struct name *makename(char *newname)
    {
        struct name *ret =
            malloc(sizeof(struct name)-1 + strlen(newname)+1);
                    /* -1 for initial [1]; +1 for \0 */
        if(ret != NULL) {
            ret->namelen = strlen(newname);
            strcpy(ret->namestr, newname);
        }

        return ret;
    }

This function allocates an instance of the name structure with the size adjusted so that the namestr field can hold the requested name (not just one character, as the structure declaration would suggest).

Despite its popularity, the technique is also somewhat notorious - Dennis Ritchie has called it "unwarranted chumminess with the C implementation." An official interpretation has deemed that it is NOT strictly conforming with the C Standard, although it does seem to work under all known implementations. Compilers that check array bounds carefully might issue warnings.

Franconia answered 26/11, 2013 at 15:23 Comment(2)
I'm curious what Dennis Ritchie would advocate instead of the struct hack? IMHO, the best alternative would have been to have compilers not squawk at zero-size array declarations, in which case compilers could have safely assumed that if an array in a struct has a declared size of N, no attempt would be made to access any element beyond N-1u or the last one for which space is occupied, whichever is lower.Pl
OP's example is actually incorrect. The struct must have at least two members, of which one may be the flexible array member.Devitrify

© 2022 - 2024 — McMap. All rights reserved.