在Linux源代码中,有很多的结构体最后都定义了一个元素个数为0个的数组,如/usr/include/linux/if_pppox.h中有这样一个结构体:
struct pppoe_tag {
__u16 tag_type;
__u16 tag_len;
char tag_data[0];
} __attribute ((packed));
又如在asterisk的源码中的Pbx.c:
struct ast_ignorepat {
const char *registrar;
struct ast_ignorepat *next;
char pattern[0];
};
结构体最后的长度为0的数组是GCC中广泛使用技巧,常用来构成可变长缓冲区。
在创建时,malloc一段结构体大小加上可变长数据长度的空间给它:malloc(sizeof(struct pppoe_tag)+ buff_len),可变长部分按数组访问方式访问;释放时,直接把整个结构体free掉就可以了。
例子如下:
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]=….
…
释放时:
free(sample_tag)
这样的好处有两个:
一次分配解决问题,省了不少麻烦。为了防止内存泄漏,如果是分两次分配(结构体和缓冲区),那么要是第二次malloc失败了,必须回滚释放第一个分配的结构体。这样带来了编码麻烦。
其次,分配了第二个缓冲区以后,如果结构里面用的是指针,还要为这个指针赋值。同样,在free这个buffer的时候,用指针也要两次free。而且小内存的管理是非常困难的,如果用指针,这个buffer的struct部分就是小内存了,在系统内存在多了势必严重影响内存管理的性能。要是用空数组把struct和实际数据缓冲区一次分配大块问题,就没有这个问题。
所以,结构体最后使用0或1的长度数组的原因,主要是为了方便的管理内存缓冲区,如果你直接使用指针而不使用数组,那么,你在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存已经与结构体的内存不连续了,所以要分别管理即申请和释放)而如果使用数组,那么只需要一次就可以全部分配出来;反过来,释放时也是一样,使用数组,一次释放,使用指针,得先释放结构体内的指针,再释放结构体。还不能颠倒次序。
这个技巧其实就是分配一段连续的的内存,减少内存的碎片化。在Linux操作系统开发或者嵌入式开发,这种技巧尤其常见。
PS:某些编译器不支持长度为0的数组的定义,在这种情况下只要将它定义成char tag_data[1],使用方法相同。
相关推荐
结构体是一种数据的归类方式,相比数组或变量更具有整体全面性,例如一个数组只可以放一些按照元素顺序存放的单元变量,即 buffer = {x, x, x, x, x…},i 有多大,数组内元素就有多少。那么我们这时候如果我们用这...
3.直接为CharNums结构体数组中的第一个元素赋值,其中字符设置为字符数组中的第一个元素,出现次数为1,并设置变量count为1 4.之后字符数组中每一个字符均与CharNums结构体数组中所有字符进行逐一比较。若存在相同...
今天在看一段代码时出现了用结构体实现变长...在结构中,data是一个数组名;但该数组没有元素;该数组的真实地址紧随结构体MyData之后,而这个地址就是结构体后面数据的地址(如果给这个结构体分配的内容大于这个结构体
用结构体创建一个数组,对数组进行了基本的操作,包括数组的插入、删除、排序、求长度等
1. 定义顺序表的结构体:顺序表的结构体包含两个成员变量,一个是指向存储数据的数组的指针,另一个是记录当前顺序表中元素的个数。 2. 初始化顺序表:初始化顺序表时,将顺序表的长度设置为0。 3. 插入元素:在顺序...
一般来说,结构中最后一个元素允许是未知大小的数组,这个数组就是柔性数组。但结构中的柔性数组前面必须至少一个其他成员,柔性数组成员允许结构中包含一个大小可变的数组,sizeof返回的这种结构大小不包括柔性数组...
移动数组元素到另一数组 大小写转换 删除指定的字符 子字符串查找 字符统计 字符串逆置 回文数 数字字符串转换成整数 比较字符串长度 子字符串移动 字符串连接 在链表中查找元素 结构体和链表排序 求链表中的极值
第四章 复合数据类型 四种符合数据类型:数组、slice、map和结构体 数组和结构体是聚合类型 数组是由同构的元素组成 结构体则是由异构的元素组成 slice和map则是动态的数据结构,它们将根据...数组的长度是数组类型的一
<br>实验四 综合(课程设计) 内容及步骤: 1、假定一维数组a[n]中的每个元素值均在[0,200]区间内,用C++编写一个算法,分别统计出落在[0,20],[21,50],[51,80],[81,130],[131,200]等各区间内的元素...
要求用户输入一条新的员工信息,这些信息保存在结构体类型数组中空的元素的各字段中,并写入到文件中进行保存; 3)系统修改功能 根据用户输入的待修改的员工编号,查找该员工的编号的记录,若找到该记录,则修改除...
数组的元素个数 135 5-2 多维数组 138 多维数组 138 总结 142 第6章 函数 145 6-1 什么是函数 146 main函数和库函数 146 什么是函数 146 函数定义 147 函数调用 148 三个数中的最大值 151 将函数的返回值...
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。...
5.2. 一个递归函数的例子( recursive functions) 121 5.3. 多返回值 121 5.4. 命名返回值 121 5.5. 可变函数参数 122 5.6. Defer 123 5.6.1. Defer语句介绍 123 5.6.2. Defer使用场景 128 5.7. 什么是头等...
% A 和 B 必须属于同一类型,并且属于以下类型之一: % 结构体、元胞数组、数值或逻辑数组或字符串。 % A 和 B 可能包含以上所列类型的其他元素(如果它们% 是元胞数组或结构)。 % 如果 A 和 B 是结构体,和/或...
4.1.4 指定结构体元素的位字段 72 4.1.5 结构数组 72 4.1.6 嵌套结构 73 4.1.7 结构体的赋值 73 4.1.8 指向结构体的指针 73 4.1.9 指向结构体数组的指针 73 4.1.10 结构中的数组成员和指针成员 73 4.1.11 在堆中创建...
面向对象方法中的对象,是系统中用来描述客观事物的一个实体,它是用来构成系统的一个基本单位,由一组属性和一组行为构成。 面向对象的方法将数据及对数据的操作方法放在一起,作为一个相互依存、不可分离的整体--...
/*在数组temp中查找编号为searchinput值的元素,并返回该数组元素的下标值*/ if(p!=-1) /*若找到该记录*/ { printheader(); printdata(temp[p]); printf(END); printf("press any key to return"); getchar...
根据上面的流程图可以看到如果是一步一步的写程序,势必会让程序变得冗长且不易阅读,因而我想到使用循环的方法,将流程图中类似的结构体做成一个循环体来实现,使程序源代码变得十分的简洁,且容易被阅读和修改。...
计算一个字符串长度 71.编写输入/输出函数 72.创建链表 73.反向输出链表 74.连接两个链表 75.算一道简单题目 76.调用函数求1/2+1/4+...+1/n 77.填空练习(指向指针的指针) 78.找到年龄最大的人 79.字符串排序 80....
的每个元素这个数组表示一个单独连接的补丁。 FVOUT = SPLITFV(FV) 将 FV 作为具有“面”和“顶点”字段的结构体 例如: fullpatch.vertices = [2 4; 2 8; 8 4; 8 0; 0 4; 2 6; 2 2; 4 2; 4 0; 5 2; 5 0]; ...