当前位置:网站首页>The use of char[0] and char[1] in C language structure

The use of char[0] and char[1] in C language structure

2022-06-24 03:52:00 Good pie notes

1. Write it at the front

I'm doing it Linux 64 Bit driver compatible 32 Bit application adaptation process , I deeply feel the trouble caused by pointer operation , Especially in the application layer 32 After the bit pointer is passed to the kernel layer , The pointer size becomes 64 position , Frequent resizing is required , It's very uncomfortable . When I'm about to finish all my work , Listen to a colleague say you can use char[0] Use instead of pointer , I almost got a mouthful of old blood .“ Why didn't you say …”. Next, from the Internet google, Discover the subtlety of this usage , Close up this article , To take notes . (PS: I still want to thank my colleague YYL, Let me again get To a skill ^_^)

Add... At the end of the structure char[0] or char[1] The usage of is GNU C An extension of , stay ISO/IEC 9899-1999 Inside , It's illegal to write that . This is used in C99 called   Flexible array . A flexible array member must be preceded by at least one other type member . Structures that contain flexible array members should use malloc Dynamic memory allocation , And the allocated memory should be larger than the size of the structure , To fit the expected size of the flexible array .

2. The purpose of quoting this usage

The main purpose is to facilitate the management of memory buffers , If you use pointers directly instead of arrays , that , When you allocate a memory buffer , You must assign the structure once , Then redistribute the pointer in the structure once ,( At this time, the allocated memory is not continuous with the memory of the structure , Therefore, it is necessary to manage separately, that is, application and release ) And if you use an array , Then it only takes one time to allocate them all , In turn, , The same is true when released , Using arrays , One release , Use the pointer , You have to release the pointer in the structure first , Re release structure . You can't reverse the order yet .

In fact, it is to allocate a continuous section of memory , Reduce memory fragmentation .

3. usage

Copy

1 2 3 4 5 6 7

struct Msg { ... // Other members ... // Other members int nLen; // commonly char data[0] Will be preceded by a length nLen Express data Size char data[0]; // char[0] or char[1] It has to be at the end };

One thing we need to know is :char data[0] This array has no elements , Its address is followed by nLen After the address , If the allocated memory is larger than the actual size of the structure , So the big part is data The content of .

In actual use , It's usually used this way

Copy

1 2 3

int dataBytes = 10; // It is specified here that data The data size of struct Msg *p = (struct Msg *)malloc(sizeof(struct Msg) + dataBytes); // Dynamic allocation p->nLen = dataBytes; // Assign the length to nLen, To facilitate other parts to use this structure

If you don't understand , A string of codes , At a glance :

Copy

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

// test.c #include <stdio.h> #include <string.h> typedef struct body1 { int a; int b; }__attribute ((packed)) BODY1; //__attribute ((packed)) Is to force no byte alignment typedef struct body2 { int len; char *data; }__attribute ((packed)) BODY2; typedef struct body3 { int len; char data[0]; }__attribute ((packed)) BODY3; typedef struct body4 { int len; char data[1]; }__attribute ((packed)) BODY4; int main() { BODY1 b1; BODY2 b2; BODY3 b3; BODY4 b4; memset(&b1, 0, sizeof(BODY1)); memset(&b2, 0, sizeof(BODY2)); memset(&b3, 0, sizeof(BODY3)); memset(&b4, 0, sizeof(BODY4)); printf("sizeof(b1) = %ld\n", sizeof(b1)); printf("sizeof(b2) = %ld\n", sizeof(b2)); printf("sizeof(b3) = %ld\n", sizeof(b3)); printf("sizeof(b4) = %ld\n", sizeof(b4)); printf(" b2 addr = %p\n", &b2); printf("b2.data addr = %p\n", b2.data); printf(" b3 addr = %p\n", &b3); printf("b3.data addr = %p\n", b3.data); printf(" b4 addr = %p\n", &b4); printf("b4.data addr = %p\n", b4.data); return 0; }

The procedure is in 64 Compile under bit system , The operation results are as follows :

Copy

1 2 3 4 5 6 7 8 9 10

sizeof(b1) = 8 sizeof(b2) = 12 sizeof(b3) = 4 sizeof(b4) = 5 b2 addr = 0x7ffded4f3633 b2.data addr = (nil) b3 addr = 0x7ffded4f363f b3.data addr = 0x7ffded4f3643 b4 addr = 0x7ffded4f3643 b4.data addr = 0x7ffded4f3647

As can be seen from the above results :

  • char data[0] It doesn't take up any space , and char *data Occupied the size of a pointer variable , Never put char data[0] As a pointer , It is actually an offset , This offset points to the space immediately behind the structure .
  • char[1] It takes up space , If you do not force byte alignment , Then the size of the structure will be 8.char[0] and char[1] The effect is the same .
  • b3 Of data Address , yes b3 The address at the beginning of the structure plus len Occupied by 4 Address of byte ,b4 Is the same .

4. With pointer and char[0] The difference between

  1. Use pointers in structures : Creation time , The system first allocates memory for the structure , Reallocate the pointer to data Of memory . Two pieces of memory are not continuous . When released , Free the memory pointed to by the pointer first , Then release the structure memory .
  2. Used in structures char[0]: Creation time , The system allocates the memory of the structure and data Of memory , The two pieces of memory are contiguous ( More specifically, a block of memory ). When released , One time release .

============================================================

In everyday programming , Sometimes you need to store a dynamic length string in the structure , General practice , Is to define a pointer member in the structure , This pointer member points to the dynamic memory space where the string is located , for example :

123456

typedef struct test{int a;double b;char *p;};

p Point to string . This method causes the string to be separated from the structure , Not conducive to operation . If you concatenate a string directly with a structure , Isn't it better ? therefore , You can change the code to this :

123

char a[] = "hello world";test *stpTest = (test *)malloc(sizeof(test) + strlen( a ) + 1 );strcpy(stpTest + 1, a );

thus ,( char* )(stpTest + 1 ) It's just a string "hello world" The address of the . Now p Become superfluous , Can be removed . however , Another problem arises : Always use ( char* )((stpTest + 1 ) inconvenient . If you can find a way , This string can be referenced directly , Without occupying the space of the structure , It's perfect , The code structure that meets this condition should be a non object symbolic address , Place a at the end of the structure 0 An array of lengths is a wonderful solution . however ,C/C++ The standard cannot define the length as 0 Array of , therefore , Some compilers put 0 Length of the array members as their own Nonstandard extensions .

Before we talk about flexible array members , First, I want to introduce incomplete types (incomplete type). An incomplete type is a type , It lacks enough information, such as length, to describe a complete object ,

Its appearance reflects C Programmers' ultimate pursuit of refining code , This code structure arises from the need for dynamic structures .

In view of the important role of this code structure ,C99 Even included it in the standard .C99 Use incomplete types to implement flexible array members , stay C99 in , The last element in the structure is allowed to be an array of unknown size , This is called a flexible array (flexible array) member ( Also called scalable array members ), But a flexible array member in a structure must be preceded by at least one other member . Flexible array members allow structures to contain an array of variable size . A flexible array member exists only as a symbolic address , And it has to be the last member of the structure ,sizeof The size of the structure returned does not include the memory of the flexible array . Flexible array members can be used not only for character arrays , It can also be arrays with other types of elements . Structures that contain flexible array members use malloc () Function to dynamically allocate memory , And the allocated memory should be larger than the size of the structure , To fit the expected size of the flexible array . See the following example for the use of flexible arrays :

123456

typedef struct test{int a;double b;char c[0];};

Some compilers will report errors and cannot compile. They can be changed to :

123456

typedef struct test{int a;double b;char c[];};

Allocate memory to the structure through the following expression :

1

test *stpTest = (test *)malloc(sizeof(test)+100*sizeof(char));

c Is a flexible array member , If you put stpTest The dynamically allocated memory pointed to is considered as a whole ,c Is a structure member whose length can change dynamically , The word "flexibility" comes from this .c The length of is 0, So it doesn't take up test Space , meanwhile stpTest->c Namely “hello world” The first address , No need to use ( char * )( stpTest + 1 ) Such ugly code . that 0 An array of elements doesn't take up space , Then we can do the lengthening operation . So we give the structure pointer c Allocated a block of memory . use stpTest->c[n] You can simply access variable length elements .

Of course , Since the top is used malloc Function allocates memory , Definitely need to use free Function to free memory :

1

free(stpTest);

Standard forms should be used wherever possible , In Africa C99 The occasion of , You can use the pointer method . It should be noted that :C89 I don't support this kind of thing ,C99 Add it to the standard as a special case . however ,C99 What is supported is that incomplete type, instead of zero array, It's the same int a[0]; This form is illegal ,C99 The form of support is in the form of int a[]; It's just that some compilers put int a[0]; As Nonstandard extensions To support , And in C99 There was this nonstandard extension before the release ,C99 After the release , Some compilers combine the two .

           Because the array has no elements , The array allocates space in the structure , therefore sizeof(struct Mydata) = 4.          malloc The application is 14 Bytes of contiguous space , It returns a pointer to this 14 Bytes , Cast to struct INFO When , front 4 Bytes are considered Mydata structure , The latter part is copied “123456789” The content of .

3、 In practice

     In practice , The length of data is unknown , In this way, the space can be saved by changing the length of the array . Operate on pointer , Convenient data type conversion . The test procedure is as follows :

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <stdint.h>
 5 
 6 typedef struct
 7 {
 8     int data_len;
 9     char data[0];
10 }buff_st_1;
11 
12 typedef struct
13 {
14     int data_len;
15     char *data;
16 }buff_st_2;
17 
18 typedef struct 
19 {
20     int data_len;
21     char data[];
22 }buff_st_3;
23 
24 typedef struct 
25 {
26     uint32_t id;
27     uint32_t age;
28 }student_st;
29 
30 
31 void print_stu(const student_st *stu)
32 {
33     printf("id:%u,age:%u\n", stu->id, stu->age);
34 }
35 
36 int main()
37 {
38     student_st *stu = (student_st *)malloc(sizeof(student_st));
39     stu->id = 100;
40     stu->age = 23;
41 
42     student_st *tmp = NULL;
43 
44     buff_st_1 *buff1 = (buff_st_1 *)malloc(sizeof(buff_st_1) + sizeof(student_st));
45     buff1->data_len = sizeof(student_st);
46     memcpy(buff1->data, stu, buff1->data_len);
47     printf("buff1 address:%p,buff1->data_len address:%p,buff1->data address:%p\n",
48         buff1, &(buff1->data_len), buff1->data);
49 
50     tmp = (student_st*)buff1->data;
51     print_stu(tmp);
52 
53     buff_st_2 *buff2 = (buff_st_2 *)malloc(sizeof(buff_st_2));
54     buff2->data_len = sizeof(student_st);
55     buff2->data = (char *)malloc(buff2->data_len);
56     memcpy(buff2->data, stu, buff2->data_len);
57     printf("buff2 address:%p,buff2->data_len address:%p,buff2->data address:%p\n",
58         buff2, &(buff2->data_len), buff2->data);
59 
60     tmp = (student_st *)buff2->data;
61     print_stu(tmp);
62 
63     buff_st_3 *buff3 = (buff_st_3 *)malloc(sizeof(buff_st_3) + sizeof(student_st));
64     buff3->data_len = sizeof(student_st);
65     memcpy(buff3->data, stu, buff3->data_len);
66     printf("buff3 address:%p,buff3->data_len address:%p,buff3->data address:%p\n",
67         buff3, &(buff3->data_len), buff3->data);
68 
69     tmp = (student_st*)buff1->data;
70     print_stu(tmp);
71 
72     free(buff1);
73 
74     free(buff2->data);
75     free(buff2);
76 
77     free(buff3);
78     free(stu);
79     return 0;
80 }

  use char *data, Secondary allocation is required , The operation is more troublesome , It's easy to cause memory leaks . Instead, use variable length arrays directly , It just needs to be allocated once , Then you can take the value .

原网站

版权声明
本文为[Good pie notes]所创,转载请带上原文链接,感谢
https://yzsam.com/2021/09/20210917190323854f.html