0-length array in GNU C

Source: Internet
Author: User


In standard C and C + +, arrays with a length of 0 are forbidden. However, in GNU C, there is a very strange usage, that is, an array of length 0, such as array[0]; many people may find it inconceivable that an array of length 0 is meaningless, but here, it represents a completely different layer of meaning, which is not portable, so If you are committed to writing portable, or slightly cross-platform code, these trick are best to be collected.

In the GNU guide, it is so written:

struct line


int length;

Char Contents[0];


.... ommit code here


struct line *thisline = [struct line *] malloc (sizeof (struct line) + this_length);

Thisline->length = This_length;


This usage is mainly used to change the size of the Buffer,struct line to 4, the structure of the contents[0] does not occupy any space, even a pointer to the space is not accounted for, contents here just represents a constant pointer, this feature is implemented by the compiler, That is, when using thisline->contents, this pointer represents a buffer in the allocated memory address, such as malloc (sizeof line) + This_length return is the 0X8F00A40, Where thisline->contents points is (0x8f00a40 + sizeof), and here sizeof (struct line) is just four bytes of an int.

For this usage, the struct pointer we define can point to a memory buffer of any length, a technique that is quite handy when used in a variable-length buffer.

A friend might say, why not just define the last contents as a pointer? The difference here is that if it is defined as a pointer, it needs to occupy 4Bytes, and it must be artificially assigned after the memory is applied. If you use this usage, this constant pointer does not occupy space and does not require a value assignment.

However, the convenience is not absolute, when releasing the allocated memory, because the function free will think that *thisline just point to a 4-byte pointer, that will only release the length of space, and for the back of the big head of the buffer is blind, this requires human intervention Instead, the allocated memory can be freed directly with free (thisline->contents) for the subsequent declaration of pointers. ASSERT: Unless necessary, do not use this feature easily, GNU C can be compiled through, so you are using VC + +, then do not have to try, compile can not pass.


Purpose: The main purpose of an array of length 0 is to satisfy the need for a variable length structure.

Usage: At the end of a struct, declare an array of length 0, which makes the structure variable-length. For the compiler, an array of length 0 does not take up space, because the array name itself does not occupy space, it is just an offset, the name of the array itself represents a non-modifiable address constant (note: The array name will never be a pointer!) ), but for the size of this array, we can assign it dynamically. For example:

typedef struct{
int Len;
Char data[0];

int my_length = 10;

test_t *p_test = (test_t *) malloc (sizeof (test_t) + my_length);
P_test->len = My_length;


Free (p_test);

The array in the struct can then be accessed as if it were a normal array.

Note: If a struct is generated by a dynamic allocation method such as Calloc, malloc, or new, the space is freed when it is not needed.

Advantage: This method is more efficient than declaring a pointer variable in a struct and then allocating it dynamically. Because there is no need for indirect access when accessing the contents of an array, two visits are avoided.

Disadvantage: In structs, arrays with arrays of 0 must be declared at last, with certain restrictions on use.

Another explanation:

In a Linux system,/usr/include/linux/if_pppox.h has such a structure:
struct Pppoe_tag {
__u16 Tag_type;
__u16 Tag_len;
Char tag_data[0];
} __attribute ((packed));
The last member is a variable-length array, which is best defined in this way for the structure of the TLV (type-length-value) Form or for other structures that require variable lengths. Very convenient to use, when created, malloc a structure size plus variable length of data length of the space to it, variable-length parts can be accessed as an array, release, the entire structure can be free. Examples are as follows:
struct Pppoe_tag *sample_tag;
__u16 Sample_tag_len = 10;
Sample_tag = (struct Pppoe_tag *) malloc (sizeof (struct Pppoe_tag) +sizeof (char) *sample_tag_len);
Sample_tag->tag_type = 0xFFFF;
Sample_tag->tag_len = Sample_tag_len;
Sample_tag->tag_data[0]= ....
When released,
Free (Sample_tag)

Can I replace it with Char *tag_data? In fact it and Char *tag_data is a big difference, in order to illustrate this problem, I wrote the following program:
Example 1:test_size.c
Ten struct TAG1
20 {
int A;
+ int B;
}__attribute ((packed));
Tag2 struct
80 {
int A;
+ int B;
110Char *c;
}__attribute ((packed));
TAG3 struct
150 {
int A;
int b;
180Char c[0];
}__attribute ((packed));
TAG4 struct
220 {
int A;
int b;
250Char c[1];
260}__attribute ((packed));
280 int Main ()
290 {
Tag2 L_tag2 of a struct;
310 struct TAG3 l_tag3;
The l_tag4 of the tag4 struct;
340 memset (&l_tag2,0,sizeof (struct tag2));
memset (&l_tag3,0,sizeof (struct tag3));
memset (&l_tag4,0,sizeof (struct tag4));
380 printf ("size of Tag1 =%d\n", sizeof (struct tag1));
390 printf ("size of Tag2 =%d\n", sizeof (struct tag2));
+ printf ("size of Tag3 =%d\n", sizeof (struct tag3));
420 printf ("L_tag2 =%p,&l_tag2.c =%p,l_tag2.c =%p\n", &l_tag2,&l_tag2.c,l_tag2.c);
430 printf ("l_tag3 =%p,l_tag3.c =%p\n", &l_tag3,l_tag3.c);
("L_tag4 =%p,l_tag4.c =%p\n", &l_tag4,l_tag4.c);
Exit (0);

__attribute ((packed)) is intended to force No 4-byte alignment, which makes it easier to explain the problem.
The running results of the program are as follows:
Size of Tag1 = 8
Size of Tag2 = 12
Size of Tag3 = 8
Size of Tag4 = 9
L_tag2 = 0xbffffad0,&l_tag2.c = 0xbffffad8,l_tag2.c = (nil)
L_tag3 = 0XBFFFFAC8,L_TAG3.C = 0xbffffad0
L_tag4 = 0xbffffabc,l_tag4.c = 0xbffffac4

From the above program and running results can be seen: Tag1 itself consists of two 32-bit integers, so accounted for 8 bytes of space. The TAG2 consists of two 32-bit integers, plus a char * pointer, so it takes 12 bytes. Tag3 is really see the difference between Char C[0] and Char *c, Char c[0] in C is not a pointer, is an offset, this offset points to a, B immediately after the space, so it actually does not occupy any space. Tag4 adds to this point in more details. Therefore, if the last member of the above struct Pppoe_tag is defined with Char *tag_data, it will be inconvenient to use in addition to a pointer variable that takes up 4 bytes :

Method One, when created, you can first allocate a piece of memory for the struct Pppoe_tag, and then allocate the memory for Tag_data, so that in the release, the Tag_data occupied memory is released first, and then the memory occupied by the Pppoe_tag is released;

Method Two, when created, directly for the struct pppoe_tag allocation of a struct Pppoe_tag size plus tag_data memory, from the example of 420 lines can be seen, tag_data content to be initialized, to let Tag_data point to STRCT The memory behind the Pppoe_tag.
struct Pppoe_tag {
__u16 Tag_type;
__u16 Tag_len;
Char *tag_data;
} __attribute ((packed));

struct Pppoe_tag *sample_tag;
__u16 Sample_tag_len = 10;
Method One:
Sample_tag = (struct Pppoe_tag *) malloc (sizeof (struct pppoe_tag));
Sample_tag->tag_len = Sample_tag_len;
Sample_tag->tag_data = malloc (sizeof (char) *sample_tag_len);
Sample_tag->tag_data[0]= ...
When released:
Free (sample_tag->tag_data);
Free (Sample_tag);

Method Two:
Sample_tag = (struct Pppoe_tag *) malloc (sizeof (struct Pppoe_tag) +sizeof (char) *sample_tag_len);
Sample_tag->tag_len = Sample_tag_len;
Sample_tag->tag_data = ((char *) sample_tag) +sizeof (struct pppoe_tag);
Sample_tag->tag_data[0]= ...
When released:
Free (Sample_tag);
So no matter how that method is used, there is no such definition as char tag_data[0].

speaking so much, essentially involves a C language inside the array and pointer to the difference problem. Char A[1] A and char *b b the same? "Programming abstractions in C" (Roberts, E. S., mechanical industry Press, 2004.6) 82 pages Inside said: "Arr is defined to being identical to &arr[0]" 。 In other words, Char a[1] A is actually a constant, equal to &a[0]. While Char *b is a real pointer to variable B exists. So, a=b is not allowed, and b=a is allowed. Two variables support subscript access, is there a difference in nature for a[0] and b[0]? We can use an example to illustrate this.

Example two:
Ten #include <stdio.h>
#include <stdlib.h>
+ int Main ()
50 {
-Char a[10];
*b Char;
Exit (0);

After compiling, you can see its assembly with Objdump:
080483f0 <main>:
80483F0:55 Push%EBP
80483f1:89 e5 mov%esp,%ebp
80483f3:83 EC Sub $0x18,%esp
80483f6:c6 f6 fe movb $0xfe,0xfffffff6 (%EBP)
80483fa:8b F0 mov 0xfffffff0 (%EBP),%eax
80483fd:83 C0 Add $0x2,%eax
8048400:C6-FE Movb $0xfe, (%EAX)
8048403:83 C4 f4 Add $0xfffffff4,%esp
8048406:6a XX Push $0x0
8048408:e8 f3 FE FF FF call 8048300 <_init+0x68>
804840d:83 C4 Add $0x10,%esp
8048410:c9 leave
8048411:C3 ret
8048412:8d B4-XX (%esi,1),%esi
8048419:8d BC-XX (%edi,1),%edi

As can be seen, A[2]=0xfe is directly addressed, directly to the 0xFE write &a[0]+2 address, and B[2]=0xfe is an indirect addressing, first the content of B (address) out, add 2, and then 0xFE write the calculated address. So a[0] and b[0] are essentially different.

But when an array is a parameter, there is no difference between the pointer.
int Do1 (char a[],int len);
int Do2 (char *a,int len);
There is no difference between a in these two functions. is a pointer variable that is actually present.

By the way, the definition of the last member of the struct Pppoe_tag is char tag_data[0], and some compilers do not support the definition of an array of length 0, in which case it can only be defined as Char tag_data[1], using the same method.

Summary: Through the above reprinted article, can be clearly found that the advantages of this method is actually to simplify the memory management, we assume that in the ideal memory state, then the allocation of memory space, can be ordered down (of course, Actually because of memory fragmentation and other reasons will be different) we can use the last array of pointers directly without interval to jump to the allocated array buffer, which is very common under Linux, under the Windows I just saw in MFC similar, other cases do not remember clearly, only remember that the MFC is said , you can assign a pointer to the struct body directly +1.

0-length array in GNU C

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.