当前位置:网站首页>C language 0 length array (variable array / flexible array) explanation
C language 0 length array (variable array / flexible array) explanation
2022-06-09 22:36:00 【User 6280468】
Zero length array concept :
as everyone knows , GNU/GCC In standard C/C++ On this basis, it has made practical expansion , Zero length array (Arrays of Length Zero) Is one of the well-known extensions .
Most of the time , It is used in variable length arrays , Its definition is as follows :
struct Packet
{
int state;
int len;
char cData[0]; // there 0 Long structure provides very good support for variable length structure
};
First of all, 0 Length array , Also called flexible array Make an explanation :
- purpose : The length is 0 The main purpose of the array is to meet the need for variable length structures
- usage : At the end of a structure , Declare a length of 0 Array of , You can make the structure variable in length . For compilers , In this case, the length is 0 The array of does not take up space , Because the array name itself doesn't take up space , It's just an offset , The array name itself represents an immutable address constant
( Be careful : The array name will never be a pointer !), But for the size of this array , We can dynamically allocate
Be careful : If the structure is through calloc、malloc or person new And other dynamic allocation methods , Free up space when you don't need it .
advantage : Instead of declaring a pointer variable in a structure 、 Then carry out dynamic analysis Matching method , This method is more efficient . Because when accessing the contents of an array , No indirect access is required , Two memory accesses are avoided .
shortcoming : In the structure , The array is 0 Must be declared at the end of , send There are certain restrictions on use .
For compilers , The array name is just a symbol , It doesn't take up any space , It's in the structure , It just represents an offset , Represents an immutable address constant !
0 The purpose of the length array :
Let's imagine a scene like this , The data buffer we use in network communication , The buffer contains a len Fields and data Field , Identify the length of the data and the transmitted data respectively , There are several common design ideas :
- Fixed length data buffer , Set a sufficient size MAX_LENGTH Data buffer for
- Set a pointer to the actual data , Every time I use it , Dynamically open up data buffer space according to the length of data
We consider their advantages and disadvantages from the design applied in the actual scene . The main considerations are , Development of buffer space , Release and access .
1、 Fixed length bag ( Open up space , Release , visit ):
For example, I want to send 1024 Bytes of data , If you use a fixed length bag , Suppose the length of a fixed length packet MAX_LENGTH by 2048, It will be wasted 1024 Bytes of space , It also causes unnecessary waste of traffic :
- Data structure definition :
// Fixed length buffer
struct max_buffer
{
int len;
char data[MAX_LENGTH];
};
- Data structure size : Consider alignment , So the size of the data structure >= sizeof(int) + sizeof(char) * MAX_LENGTH
Considering the data overflow , In variable length packets data The array length is usually set to be long enough to hold the largest amount of data , therefore max_buffer Medium data In many cases, arrays are not filled with data , So there is waste
- The construction of packets : If we want to send CURR_LENGTH = 1024 Bytes , How do we construct this packet ; Generally speaking , We will return a data structure pointing to the buffer max_buffer The pointer to :
/// open up
if ((mbuffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL)
{
mbuffer->len = CURR_LENGTH;
memcpy(mbuffer->data, "Hello World", CURR_LENGTH);
printf("%d, %s\n", mbuffer->len, mbuffer->data);
}
- visit : This memory is divided into two parts ; The first part 4 Bytes p->len, As a Baotou ( That's the extra part ), This header is used to describe the length of the data section immediately after the header , Here is 1024, So the first four bytes are assigned to 1024 ( Since we are going to construct indefinite length packets , So how long is this bag , therefore , We have to use a variable to indicate the length of the packet , This is it. len The role of ); And the memory immediately after that is the real data part , adopt p->data, Last , Carry out a memcpy() Memory copy , Put the data to be sent into this memory
- Release : When the data space is released after use , Just release it
/// The destruction
free(mbuffer);
mbuffer = NULL;
2、 Summary :
- Use fixed length arrays , As a data buffer , To avoid buffer overflow , The size of the array is usually set to enough space MAX_LENGTH, But in actual use , achieve MAX_LENGTH There's very little data on length , So most of the time , Most of the buffer space is wasted
- But the process is simple , It's easy to open up and release data space , No need for programmers to think about extra operations
3、 Pointer packet ( Open up space , Release , visit ):
If you set the length of the top to MAX_LENGTH Replace the fixed length array with a pointer , Dynamic development at each use CURR_LENGTH Size space , Then avoid causing MAX_LENGTH - CURR_LENGTH Waste of space , Only the space of a pointer field is wasted :
- Packet definition :
struct point_buffer
{
int len;
char *data;
};
- Data structure size : Consider alignment , So the size of the data structure >= sizeof(int) + sizeof(char *)
- Space allocation : But it also causes the use of memory allocation , There are two steps
// =====================
// Pointer array Occupy - open up - The destruction
// =====================
/// Occupy
printf("the length of struct test3:%d\n",sizeof(struct point_buffer));
/// open up
if ((pbuffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL)
{
pbuffer->len = CURR_LENGTH;
if ((pbuffer->data = (char *)malloc(sizeof(char) * CURR_LENGTH)) != NULL)
{
memcpy(pbuffer->data, "Hello World", CURR_LENGTH);
printf("%d, %s\n", pbuffer->len, pbuffer->data);
}
}
First , You need to allocate a memory space for the structure ; Secondly, allocate memory space for member variables in the structure .
In this way, the memory allocated twice is not continuous , They need to be managed separately . When using arrays of length , The principle of one-time distribution is adopted , Allocate all the required memory to it at one time .
- Release : contrary , It's the same when it's released :
/// The destruction
free(pbuffer->data);
free(pbuffer);
pbuffer = NULL;
- Summary :
- Use pointer results as buffers , Only one more pointer size space is used , No need to use MAX_LENGTH An array of lengths , It won't cause a lot of waste of space
- But that's when opening up space , Need to open up extra space for data fields , When casting, you also need to display the space to release the data field , But in actual use , Often open up space in a function , And then back to the user pointing to struct point_buffer The pointer to , At this time, we can't assume that users know the details of our development , And release space according to the agreed operation , So it's inconvenient to use , Even cause memory leaks .
4、 Variable length data buffer ( Open up space , Release , visit )
Fixed length array is easy to use , But it's a waste of space , The pointer form uses only one more pointer space , It won't waste a lot of space , But it needs to be allocated many times , Multiple releases , So is there a way to implement it without wasting space , And easy to use ?
GNU C Of 0 Length array , Also called variable length arrays , A flexible array is such an extension . about 0 This feature of long arrays , It's easy to construct into a structure , Such as buffer , Packets and so on :
- Data structure definition :
// 0 Length array
struct zero_buffer
{
int len;
char data[0];
};
- Data structure size : Such variable length arrays are often used to construct indefinite length packets in network communication , Don't waste space, waste network traffic , because char data[0]; It's just an array name , It doesn't take up storage space :
sizeof(struct zero_buffer) = sizeof(int)
- Open up space : So when we use it , Just open up a space once
/// open up
if ((zbuffer = (struct zero_buffer *)malloc(sizeof(struct zero_buffer) + sizeof(char) * CURR_LENGTH)) != NULL)
{
zbuffer->len = CURR_LENGTH;
memcpy(zbuffer->data, "Hello World", CURR_LENGTH);
printf("%d, %s\n", zbuffer->len, zbuffer->data);
}
- Release space : The same goes for freeing up space , Release once
/// The destruction
free(zbuffer);
zbuffer = NULL;
- summary :
// zero_length_array.c
#include <stdio.h>
#include <stdlib.h>
#define MAX_LENGTH 1024
#define CURR_LENGTH 512
// 0 Length array
struct zero_buffer
{
int len;
char data[0];
}__attribute((packed));
// Fixed length array
struct max_buffer
{
int len;
char data[MAX_LENGTH];
}__attribute((packed));
// Pointer array
struct point_buffer
{
int len;
char *data;
}__attribute((packed));
int main(void)
{
struct zero_buffer *zbuffer = NULL;
struct max_buffer *mbuffer = NULL;
struct point_buffer *pbuffer = NULL;
// =====================
// 0 Length array Occupy - open up - The destruction
// =====================
/// Occupy
printf("the length of struct test1:%d\n",sizeof(struct zero_buffer));
/// open up
if ((zbuffer = (struct zero_buffer *)malloc(sizeof(struct zero_buffer) + sizeof(char) * CURR_LENGTH)) != NULL)
{
zbuffer->len = CURR_LENGTH;
memcpy(zbuffer->data, "Hello World", CURR_LENGTH);
printf("%d, %s\n", zbuffer->len, zbuffer->data);
}
/// The destruction
free(zbuffer);
zbuffer = NULL;
// =====================
// Fixed length array Occupy - open up - The destruction
// =====================
/// Occupy
printf("the length of struct test2:%d\n",sizeof(struct max_buffer));
/// open up
if ((mbuffer = (struct max_buffer *)malloc(sizeof(struct max_buffer))) != NULL)
{
mbuffer->len = CURR_LENGTH;
memcpy(mbuffer->data, "Hello World", CURR_LENGTH);
printf("%d, %s\n", mbuffer->len, mbuffer->data);
}
/// The destruction
free(mbuffer);
mbuffer = NULL;
// =====================
// Pointer array Occupy - open up - The destruction
// =====================
/// Occupy
printf("the length of struct test3:%d\n",sizeof(struct point_buffer));
/// open up
if ((pbuffer = (struct point_buffer *)malloc(sizeof(struct point_buffer))) != NULL)
{
pbuffer->len = CURR_LENGTH;
if ((pbuffer->data = (char *)malloc(sizeof(char) * CURR_LENGTH)) != NULL)
{
memcpy(pbuffer->data, "Hello World", CURR_LENGTH);
printf("%d, %s\n", pbuffer->len, pbuffer->data);
}
}
/// The destruction
free(pbuffer->data);
free(pbuffer);
pbuffer = NULL;
return EXIT_SUCCESS;
}
GNU Document in Variable length array support :
Reference resources :
6.17 Arrays of Length Zero
C Struct Hack – Structure with variable length array
stay C90 Before , Does not support 0 An array of lengths , 0 Length array is GNU C An extension of , Therefore, the early compilers could not be compiled ; about GNU C Added extensions , GCC Compile options are provided to clearly identify them :
- -pedantic Options , Then the corresponding warning message will be generated where the extended syntax is used
- -Wall Use it to make GCC Generate as many warning messages as possible
- -Werror, It requires GCC Treat all warnings as errors
// 1.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char a[0];
printf("%ld", sizeof(a));
return EXIT_SUCCESS;
}
Let's compile :
gcc 1.c -Wall # Show all warnings
#none warning and error
gcc 1.c -Wall -pedantic # Yes GNU C The extension of shows warnings
1.c: In function ‘main’:
1.c:7: warning: ISO C forbids zero-size array ‘a’
gcc 1.c -Werror -Wall -pedantic # Show all warnings at the same time GNU C The extension of shows warnings , Use the warning as error Show
cc1: warnings being treated as errors
1.c: In function ‘main’:
1.c:7: error: ISO C forbids zero-size array ‘a’
0 Length array is actually a flexible array that points to the continuous memory space behind it :
struct buffer
{
int len;
char data[0];
};
It was not introduced in the early days 0 Length array , We solve it by means of fixed length array and pointer , however :
- A fixed length array defines a buffer large enough , It's easy to use , But every time it causes a waste of space
- The way of the pointer , It is necessary to ask programmers to release space many times free operation , In the process of using, we often return the pointer to the buffer in the function , We cannot guarantee that everyone understands and complies with our release methods
therefore GNU And then it was 0 Extension of length array . When using data[0] When , That is to say 0 Length array ,0 Length array as array name , It doesn't take up storage space .
stay C99 after , A similar extension has been added , It's just that char payload[] This form ( So if you really need to use it when compiling -pedantic Parameters , Then you can put char payload[0] Change the type to char payload[], So you can compile and pass , Of course, your compiler must support C99 The standard , If the compiler is too old , That may not support )
// 2.c payload
#include <stdio.h>
#include <stdlib.h>
struct payload
{
int len;
char data[];
};
int main(void)
{
struct payload pay;
printf("%ld", sizeof(pay));
return EXIT_SUCCESS;
}
Use -pedantic After compiling , No warnings , This grammar is C The standard
gcc 2.c -pedantic -std=c99
So the end of the structure , It refers to the memory data behind it . Therefore, we can take this type of structure as the header format of the data message , And the last member variable , It just happens to be the data content .
GNU The manual also provides two other structures to illustrate , Easier to understand :
struct f1 {
int x;
int y[];
} f1 = { 1, { 2, 3, 4 } };
struct f2 {
struct f1 f1;
int data[3];
} f2 = { { 1 }, { 5, 6, 7 } };
I put f2 Inside 2,3,4 Changed to 5,6,7 To differentiate . If you type out the data . The following information :
f1.x = 1
f1.y[0] = 2
f1.y[1] = 3
f1.y[2] = 4
That is to say f1.y Pointing to {2,3,4} The data in this memory . So we can easily get ,f2.f1.y The data pointed to is just f2.data Content. . Printed data :
f2.f1.x = 1
f2.f1.y[0] = 5
f2.f1.y[1] = 6
f2.f1.y[2] = 7
If you are not sure if it takes up space . You can use it. sizeof Let's calculate . We can know sizeof(struct f1)=4, That is to say int y[] Actually, it doesn't take up space . But this 0 An array of lengths , Must be placed at the end of the structure . If you didn't put it at the end . When compiling , There will be the following mistakes :
main.c:37:9: error: flexible array member not at end of struct
int y[];
^
This way , You may have questions , If you will struct f1 Medium int y[] Replace with int *y, And how ? This involves arrays and pointers . Sometimes , These two are the same , Sometimes there is a difference .
The first thing to say is this , Support 0 Extension of length array , The focus is on arrays , That is to say, it can't be used int *y Pointer to replace .sizeof The length of is different . hold struct f1 To such :
struct f3 {
int x;
int *y;
};
stay 32/64 Place below , int It's all 4 Bytes , sizeof(struct f1)=4, and sizeof(struct f3)=16
because int *y Is a pointer , Pointer in 64 Place below , yes 64 Bit , sizeof(struct f3) = 16, If in 32 Bit environment , sizeof(struct f3) It is 8 了 , sizeof(struct f1) unchanged . therefore int *y It can't replace int y[] Of ;
The code is as follows :
// 3.c
#include <stdio.h>
#include <stdlib.h>
struct f1 {
int x;
int y[];
} f1 = { 1, { 2, 3, 4 } };
struct f2 {
struct f1 f1;
int data[3];
} f2 = { { 1 }, { 5, 6, 7 } };
struct f3
{
int x;
int *y;
};
int main(void)
{
printf("sizeof(f1) = %d\n", sizeof(struct f1));
printf("sizeof(f2) = %d\n", sizeof(struct f2));
printf("szieof(f3) = %d\n\n", sizeof(struct f3));
printf("f1.x = %d\n", f1.x);
printf("f1.y[0] = %d\n", f1.y[0]);
printf("f1.y[1] = %d\n", f1.y[1]);
printf("f1.y[2] = %d\n", f1.y[2]);
printf("f2.f1.x = %d\n", f1.x);
printf("f2.f1.y[0] = %d\n", f2.f1.y[0]);
printf("f2.f1.y[1] = %d\n", f2.f1.y[1]);
printf("f2.f1.y[2] = %d\n", f2.f1.y[2]);
return EXIT_SUCCESS;
}
0 Other characteristics of length array :
1、 Why? 0 The length array does not take up storage space :
0 What is the difference between a length array and a pointer implementation , Why? 0 Length arrays do not take up storage space ?
In fact, it essentially involves a C The difference between array and pointer in language . char a[1] Inside a and char *b Of b Is it the same ?
《 Programming Abstractions in C》(Roberts, E. S., Mechanical industry press ,2004.6)82 The page says :
“arr is defined to be identical to &arr[0]”.
in other words ,char a[1] Inside a It's actually a constant , be equal to &a[0]. and char *b There is a real pointer variable b There is . therefore ,a=b It's not allowed , and b=a Permissible . Both variables support subscript access , So for a[0] and b[0] Is there a difference in essence ? We can use an example to illustrate .
See the following two procedures gdb_zero_length_array.c and gdb_zero_length_array.c:
// gdb_zero_length_array.c
#include <stdio.h>
#include <stdlib.h>
struct str
{
int len;
char s[0];
};
struct foo
{
struct str *a;
};
int main(void)
{
struct foo f = { NULL };
printf("sizeof(struct str) = %d\n", sizeof(struct str));
printf("before f.a->s.\n");
if(f.a->s)
{
printf("before printf f.a->s.\n");
printf(f.a->s);
printf("before printf f.a->s.\n");
}
return EXIT_SUCCESS;
}
// gdb_pzero_length_array.c
#include <stdio.h>
#include <stdlib.h>
struct str
{
int len;
char *s;
};
struct foo
{
struct str *a;
};
int main(void)
{
struct foo f = { NULL };
printf("sizeof(struct str) = %d\n", sizeof(struct str));
printf("before f.a->s.\n");
if (f.a->s)
{
printf("before printf f.a->s.\n");
printf(f.a->s);
printf("before printf f.a->s.\n");
}
return EXIT_SUCCESS;
}
You can see that both programs have access exceptions , But the location of the segment error is different
We compile the two programs into an assembly , Natural household diff See how their assembly code differs
gcc -S gdb_zero_length_array.c -o gdb_test.s
gcc -S gdb_pzero_length_array.c -o gdb_ptest
diff gdb_test.s gdb_ptest.s
1c1
< .file "gdb_zero_length_array.c"
---
> .file "gdb_pzero_length_array.c"
23c23
< movl $4, %esi
---
> movl $16, %esi
30c30
< addq $4, %rax
---
> movq 8(%rax), %rax
36c36
< addq $4, %rax
---
> movq 8(%rax), %rax
# printf("sizeof(struct str) = %d\n", sizeof(struct str));
23c23
< movl $4, %esi #printf("sizeof(struct str) = %d\n", sizeof(struct str));
---
> movl $16, %esi #printf("sizeof(struct str) = %d\n", sizeof(struct str));
from 64 Bit system , We can see that , The size of variable length array structure is 4, The structure size in the form of pointer is 16:
f.a->s
30c30/36c36
< addq $4, %rax
---
> movq 8(%rax), %rax
You can see that there is :
- about char s[0] Come on , Assembly code is used addq Instructions , addq $4, %rax
- about char*s Come on , Assembly code is used movq Instructions , movq 8(%rax), %rax
addq Yes %rax + sizeof(struct str), namely str At the end of the structure is char s[0] The address of , This step is just to get its address , and movq It's about putting the content in the address , So it is sometimes translated as leap Instructions , See the next column
You can see it here , Accessing the name of a member array actually gets the relative address of the array , The access member pointer is actually the content of the relative address ( This is the same as accessing other variables that are not pointers or arrays ):
- Access relative addresses , The program will not crash, however , Accessing the contents of an illegal address , The program will crash.
// 4-1.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *a;
printf("%p\n", a);
return EXIT_SUCCESS;
}
4-2.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char a[0];
printf("%p\n", a);
return EXIT_SUCCESS;
}
$ diff 4-1.s 4-2.s
1c1
< .file "4-1.c"
---
> .file "4-2.c"
13c13
< subl $16, %esp
---
> subl $32, %esp
15c15
< leal 16(%esp), %eax
---
> movl 28(%esp), %eax
- about char a[0] Come on , Assembly code is used leal Instructions , leal 16(%esp), %eax:
- about char *a Come on , Assembly code is used movl Instructions , movl 28(%esp), %eax
2、 Address optimization :
// 5-1.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char a[0];
printf("%p\n", a);
char b[0];
printf("%p\n", b);
return EXIT_SUCCESS;
}
because 0 Length array is GNU C An extension of , Not allowed by the standard library , So some clever and weird code , The result depends on the implementation of compiler and optimization strategy .
Like the code above , a and b The compiler optimizes the address of , because a[0] and b[0] It is unusable for programs , It brings us to what ?
Compiler constant for the same string , Often the address is optimized to one place , Reduce space occupation :
// 5-2.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
const char *a = "Hello";
printf("%p\n", a);
const char *b = "Hello";
printf("%p\n", b);
const char c[] = "Hello";
printf("%p\n", c);
return EXIT_SUCCESS;
}
Article reference :https://kernel.blog.csdn.net/article/details/64131322
边栏推荐
- [BP prediction] BP neural network based on AdaBoost realizes data regression prediction with matlab code
- 设计备忘录解矩阵链(动态规划法)的一些思考
- ERC20协议API接口规范
- IAR open project compilation unresponsive, stuck & stm32cubemx error generating project
- Digital supervision of construction sites and the wisdom of scientific warfare
- Web3在遥远的未来?不,它已经来了!
- Mina中的区块证明
- 继承的所有特征
- Web3中的 重复的 Web1历程
- Maximum value and subscript of acquisition matrix of C language test question 168
猜你喜欢

目前28岁,从20年开始北漂闯荡到入职软件测试,我成功捧起15K薪资

At present, I am 28 years old, and I have successfully won the 15K salary since I started to wander to the north in 20 years and started to test the entry software

Web3中的 重复的 Web1历程

All inherited features

华为设备配置Hub and Spoke

Campus Ruijie router User Guide

Find my product | the new TWS headset supports the find my function, and apple find my is more widely used in third-party manufacturers

Aquanee will land in gate and bitmart in the near future, providing a good opportunity for low-level layout

Industrial Internet + Digital Integrated Management cloud platform for hazardous chemical safety production

Lidar related introduction
随机推荐
【刷题篇】布尔运算
NPM and yarn
Calculation of C language test question 163 the day of a certain day is the day of the corresponding year, and the total number of days in the year; Calculates the number of days between two dates. Th
Aquanee will land in gate and bitmart in the near future, providing a good opportunity for low-level layout
Web3中的 重复的 Web1历程
SQL advanced processing
【刷题篇】最长递增子序列
C语言试题162之圆周率π
After the naked resignation in the post epidemic era, the interview with the software test engineer was rejected. Write down some insights and interview experiences
为什么健身?
Slightly more complex queries
汛期建筑施工现场安全生产风险智能防控
Find My技术|物联网时代,苹果Find My实现真正的智能防丢
[translation paper] a progressive morphological filter for removing nonground measurements from airport lidar dat
[image encryption and decryption] image encryption and decryption based on chaotic sequence combined with dwt+svd (including correlation test) including Matlab source code
Mina中的区块证明
实验一:在FW上配置静态路由实现互通
Web3 in the distant future? No, it has come!
稍微复杂的查询
Erc20 protocol API interface specification