`
jgsj
  • 浏览: 1027961 次
文章分类
社区版块
存档分类
最新评论

结构体中最后一个成员为[0]或[1]长度数组(柔性数组成员)的用法

阅读更多

结构体中最后一个成员为[0]长度数组的用法:这是个广泛使用的常见技巧,常用来构成缓冲区。比起指针,用空数组有这样的优势:(1)、不需要初始化,数组名直接就是所在的偏移;(2)、不占任何空间,指针需要占用int长度空间,空数组不占任何空间。“这个数组不占用任何内存”,意味着这样的结构节省空间;“该数组的内存地址就和它后面的元素地址相同”,意味着无需初始化,数组名就是后面元素的地址,直接就能当指针使用。

这样的写法最适合制作动态buffer,因为可以这样分配空间malloc(sizeof(structXXX) + buff_len); 直接就把buffer的结构体和缓冲区一块分配了。用起来也非常方便,因为现在空数组其实变成了buff_len长度的数组了。这样的好处是:(1)、一次分配解决问题,省了不少麻烦。为了防止内存泄露,如果是分两次分配(结构体和缓冲区),那么要是第二次malloc失败了,必须回滚释放第一个分配的结构体。这样带来了编码麻烦。其次,分配了第二个缓冲区以后,如果结构里面用的是指针,还要为这个指针赋值。同样,在free这个buffer的时候,用指针也要两次free。如果用空数组,所有问题一次解决。(2)、小内存的管理是非常困难的,如果用指针,这个buffer的struct部分就是小内存了,在系统内存在多了势必严重影响内存管理的性能。要是用空数组把struct和实际数据缓冲区一次分配大块问题,就没有这个问题。如此看来,用空数组既简化编码,又解决了小内存碎片问题提高了性能。

结构体最后使用0或1长度数组的原因:主要是为了方便的管理内存缓冲区(其实就是分配一段连续的内存,减少内存的碎片化),如果直接使用指针而不使用数组,那么,在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存已经与结构体的内存不连续了,所有要分别管理即申请和释放)而如果使用数组,那么只需要一次就可以全部分配出来,反过来,释放时也是一样,使用数组,一次释放。使用指针,得先释放结构体内的指针,再释放结构体,还不能颠倒顺序。

结构体中最后一个成员为[1]长度数组的用法:与长度为[0]数组的用法相同,改写为[1]是出于可移植性的考虑。有些编译器不支持[0]数组,可将其改成[]或[1].

不完整类型(incomplete type):它缺乏足够的信息例如长度去描述一个完整的对象。(1)、前向声明就是一种常用的不完整类型, class base; struct test; base和test只给出了声明,没有给出定义。不完整类型必须通过某种方式补充完整,才能使用它们进行实例化,否则只能用于定义指针或引用,否则只能用于指针或引用,因为此时实例化的是指针或引用本身,不是base或test对象。(2)、一个未知长度的数组也属于不完整类型:extern int a[]; extern不能去掉,因为数组的长度未知,不能作为定义出现。不完整类型的数组可以通过几种方式补充完整才能使用,大括号形式的初始化就是其中一种方式:int a[] = {10, 20};

柔性数组成员(flexible array member):也叫收缩性数组成员,这种代码结构产生于对动态结构体的需求。C99使用不完整类型实现柔性数组成员,在C99中,结构中的最后一个元素允许是未知大小的数组,这就叫柔性数组成员。但结构中的柔性数组成员前面必须至少一个其它成员。柔性数组成员允许结构中包含一个大小可变的数组。柔性数组成员只作为一个符号地址存在,而且必须是结构体的最后一个成员,sizefo返回的这种结构大小不包括柔性数组的内存。柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组。包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

C/C++标准规定不能定义长度为0的数组,因此,有些编译器就把0长度的数组成员作为自己的非标准扩展。

示例代码:

#include <iostream>
using namespace std;

typedef struct _FlexibleArray
{
	char ch;
	int arr[0];//int arr[];//int arr[1];
}FlexibleArray;

int main()
{
	cout<<sizeof(FlexibleArray)<<endl;

	const int LENGTH = 10;
	FlexibleArray* flexibleArray = (FlexibleArray*)new char[sizeof(FlexibleArray) + LENGTH * sizeof(int)];

	for (int i = 0; i < LENGTH; i ++) {
		flexibleArray->arr[i] = i * i;
	}

	for (int i = 0; i < LENGTH; i ++) {
		cout<<flexibleArray->arr[i]<<endl;
	}

	delete [] flexibleArray;

	return 0;
}

参考文献:

1、 http://blog.chinaunix.net/uid-26750459-id-3191136.html

2、 http://blog.csdn.net/ce123_zhouwei/article/details/8973073

3、http://blog.csdn.net/code_crash/article/details/4854939

分享到:
评论

相关推荐

    结构体中最后一个数组长度为零

    在某些情况下,我们可能需要创建包含可变长度数据的结构体,这时可以使用"结构体中最后一个数组长度为零"的技术。这个技术常用于表示如TLV(Type-Length-Value)格式的数据,其中长度可以根据需要变化。 在提供的...

    结构体零长度数组的意义(入门)1

    在结构体中定义一个零长度数组,例如`char bytes[0]`,可以视为结构体的结束标志,但其真正的目的是为了后续的数据扩展。 结构体零长度数组的主要用途是作为可变长度的数据存储区。例如,在上述的`user_def_t`...

    柔性数组成员

    柔性数组成员是一种特殊的数组类型,它可以被声明为结构体中最后一个成员,并且其大小在声明时未知。这种成员的主要优势在于它能够在不使用额外指针的情况下处理动态数据,从而提高程序的效率和简洁性。 #### 三、...

    C# 结构体与数组转换,结构体成员支持数组类型

    C#实现结构体与数组间的转换,包括:同时支持大小端;支持自定义数据类型;支持数组类型结构体成员,带单元测试

    结构体中数组成员赋值

    在这个定义中,`name` 是一个字符数组,长度为 15。现在,我们想给这个结构体赋值,例如,学号为 7,姓名为 "Zhangsan"。 要给结构体赋值,我们需要使用 `memset` 函数来初始化结构体,然后使用赋值语句来设置成员...

    vc结构体数组长度计算

    当定义一个结构体时,其成员在内存中的布局方式会影响结构体的总大小。为了提高访问效率,编译器会自动进行内存对齐处理,确保每个成员变量的地址能被其自身大小整除。例如,对于`int`型数据(假设占用4个字节),其...

    c++调用C# COM 参数是结构体数组

    在这个例子中,我们定义了一个 _CAPI_Point3d 结构体数组,数组长度为 count。 Marshal 类 在 C# 中,Marshal 类是用于在托管代码和非托管代码之间进行数据类型转换的。例如,在本例中,我们使用 Marshal....

    C#byte数组结构体互相转换示例.zip

    2. **结构体转字节数组**:创建一个方法用于将结构体实例转换为字节数组。 ```csharp public static byte[] StructToByteArray(MyStruct structInstance) { int size = Marshal.SizeOf(structInstance); byte[] ...

    定义嵌套结构体数组

    在这个例子中,`Student` 结构体不仅有姓名和年龄,还有一个名为 `courses` 的数组,该数组的元素是 `Course` 类型,表示学生可以选修的三门课程。通过这种方式,我们可以在一个结构体中存储多种不同类型的数据,...

    C#结构体和C++字节数组的转换

    这里的`BytesToStruct`方法接受一个字节数组和结构体的`Type`对象,首先检查字节数组的长度是否足够,然后分配内存,将字节数组的内容拷贝到内存,接着使用`Marshal.PtrToStructure`将内存中的数据转换为指定类型的...

    C#中byte数组和c++结构体的转换

    如果是和c++等写的程序通信的话,很多的都是传送结构体,在VC6.0中可以很方便的把一个char[]数组转换为一个结构体,而在C#却不能直接把byte数组转换为结构体,要在C#中发送结构体,可以按此方法实现。 C#调用c++dll...

    c#中结构数组的定义

    此外,C#中数组的大小并非其类型的一部分,这意味着在声明数组后,可以对其赋值不同长度的数组,如: ```csharp int[] numbers; numbers = new int[10]; // 创建一个包含10个元素的数组 numbers = new int[20]; // ...

    gsoap_返回值为结构体数组

    当在MFC项目中使用gSOAP处理返回值为结构体或结构体数组的Web服务时,我们需要理解一些关键概念和技术。 首先,让我们了解gSOAP的工作原理。gSOAP自动生成用于序列化和反序列化数据的代码,使得C/C++程序能够以SOAP...

    结构体零长度数组的意义(深入)1

    在 Linux 系统中,结构体最后的长度为 0 或者 1 的数组是一个非常重要的概念。这在 /usr/include/linux/if_pppox.h 里面有一个结构体的定义,名为 struct pppoe_tag: ```c struct pppoe_tag { __u16 tag_type; _...

    第8讲-结构体与结构体数组

    讲解区分结构体与结构体数组:结构体的引出、结构体变量的定义和引用、结构体数组

    mfc ActiveX 结构体数组的传递

    例如,假设我们有一个名为`MyStruct`的结构体,包含两个成员:int类型的`value1`和字符串类型的`value2`,那么可以这样声明: ```cpp BEGIN_DISPATCH_MAP(CMyActiveXCtrl, COleControl) DISP_FUNCTION_ID...

    C#调用C++ Dll关于结构体数组引用的传递及解析使用的展示代码

    这里定义了一个名为`DEVICE_INFO`的结构体,包含三个成员:一个整型变量`online`,以及两个字符数组`id`和`attr`。此外,还定义了一个名为`GetDeviceIdList`的函数,它接收一个`DEVICE_INFO`结构体数组的指针以及一...

    2.编写一个函数print, 打印一个学生的成绩数组, 该数组中有5个学生的数据记录,每个记录包括num,name,score[3],用主函数输入这些记录,用p

    1.定义一个结构体变量(包括年、月、日)。计算该日在本年中是第几天,注意闰年的问题。 2.编写一个函数print, 打印一个学生的成绩数组, 该数组中有5个学生的数据记录,每个记录包括num,name,score[3],用主函数...

    C语言实例循环嵌套数组指针函数结构体

    在C语言编程中,循环、嵌套、数组、指针、函数和结构体是核心概念,它们构成了C语言的强大功能和灵活性。以下是对这些概念的详细解释和实践应用。 一、循环 C语言中的循环主要有三种类型:for循环、while循环和do-...

    运用结构体数组与输入函数进行存放学生的学号姓名等数据

    在上面的代码中,我们定义了一个结构体数组`stu`,其中包含四个成员变量:`num`、`name`、`yuwenscore`和`shuxuescore`。这些成员变量分别用于存储学生的学号、姓名、语文成绩和数学成绩。 二、输入函数的应用 在...

Global site tag (gtag.js) - Google Analytics