How to use zero length array in C

We can initialize the structure with a zero-length array as indicated in the link:

Zero-Length .

I am using the following structures:

typedef unsigned char UINT8;
typedef unsigned short UINT16;

typedef struct _CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;

typedef struct _CmdXHeader
{
    UINT8 len;
    UINT8 payload[0];
} CmdXhHeader;

      

Now CommandHeader.payload must point / contain the CmdXHeader structure. that is, the memory should look like this:

 -------------------------------------------------------------
| CommandHeader.len | CmdXHeader.len | CmdXHeader.payload ....|
 -------------------------------------------------------------

      

I can malloc the CmdXHeader / CommandHeader to customize the length easily. But how do I assign a value to the CmdXHeader payload, or how do I bind the CmdXHeader object to CommandHeader.payload?


My decision

Thanks for the whole answer. I solved it this way:

//Get the buffer for CmdXHeader:
size_t cmdXHeader_len = sizeof(CmdXHeader) + custom_len;
CmdXHeader* cmdXHeader = (CmdXHeader*) malloc(cmdXHeader_len);
//Get a temporary pointer and assign the data to it
UINT8* p;
p[0] = 1;
p[2] = 2;
.......

//Now copy the memory of p to cmdXHeader
memcopy(cmdHeader->payload, p, custom_len);

// allocate the buffer for CommandHeader
CommandHeader* commandHeader = (CommandHeader*) malloc (sizeof (CommandHeader) + cmdXHeader_len);

// populate the fields in commandHeader
commandHeader->len = custom_len;
memcpy(commandHeader->payload, cmdXHeader, cmdXHeader_len);

      

The commandHeader object now has the required memory and we can typecast in any way we want ...

+3


source to share


4 answers


A zero-length array at the end of a structure or elsewhere is in fact illegal (more precisely, a constraint violation) in standard C. This is the gcc-compliant extension.

This is one of several forms of structure-breaking. A somewhat more portable way to do this is to define an array of length 1 rather than 0.

Dennis Ritchie, the creator of the C language, called it "unreasonable chumminess with the C implementation".

The 1999 edition of the ISO standard introduces a feature called "flex array", a more reliable way to do this. Most modern C compilers support this feature (I suspect the Microsoft compiler doesn't).

This is discussed in detail in the 2.6 comp.lang.c FAQ .

As for how you access it, depending on which shape you are using, you can treat it the same way you would any array. The member name decays to a pointer in most contexts, allowing you to index it. As long as you have allocated enough memory, you can do things like:

CommandHeader *ch;
ch = malloc(computed_size);
if (ch == NULL) { /* allocation failed, bail out */ }
ch.len = 42;
ch.payload[0] = 10;
ch.payload[1] = 20;
/* ... */

      

Obviously, this is just a rough outline.



Note that sizeof

when applied to a type CommandHeader

or to an object of that type, will give you a result that does not include the flex array element.

Note also that identifiers starting with underscores are implementation reserved. You should never define such identifiers in your own code. There is no need to use different identifiers for the typedef name and struct tag:

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;

      

I also suggest using the standard types uint16_t

and uint8_t

as defined in <stdint.h>

(assuming your compiler supports it and also new in C99).

(Actually, the rules for identifiers starting with an underscore are a little more complicated. To quote N1570 , the latest draft of the standard, section 7.1 0.3:

  • All identifiers starting with an underscore and either an uppercase letter or another underscore are always reserved for any use.
  • All identifiers starting with an underscore are always reserved for use as file size identifiers in both the regular tag and the tag name.

And there are a few more classes of reserved identifiers.

But rather than defining which identifiers are file-scoped safe and which are file-scoped safe, it's much easier to avoid defining any identifiers that start with an underscore.)

+9


source


I am assuming you have some bytes in memory and want to find a pointer to the payload?

typedef struct _CmdXHeader
{
    UINT8 len;
    UINT8* payload;
} CmdXhHeader;

typedef struct _CommandHeader
{
    UINT16 len;
    CmdXhHeader xhead;
} CommandHeader;

      

Then you can pass your memory to the pointer to CommandHeader



uint8_t* my_binary_data = { /* assume you've got some data */ };

CommandHeader* cmdheader = (CommandHeader*) my_binary_data;

// access the data
cmdheader->xhead.payload[0];

      

IMPORTANT! If you don't package your structure, it will probably be word-aligned and not portable. See your compiler docs for specific syntax on how to package the structure.

Also, I would only do what you showed if you are consuming bytes (i.e. reading from a file or from wires). IF you are the data creator then I would heartily recommend against what you showed.

+1


source


   struct _CommandHeader *commandHeader = malloc(sizeof(struct _CommandHeader)+
                                  sizeof(struct _CmdXHeader));

      

0


source


Better to use payload [] instead of payload [0] in C99. Some of the C99 compilers discourage the use of a zero-length array. So, if you get the error:

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[0];
} CommandHeader;

      

you can always fix it like:

typedef struct CommandHeader
{
    UINT16 len;
    UINT8 payload[];
} CommandHeader;

      

0


source







All Articles