Arrays of Length Zero 长度为0的数组的使用

        声明零长度数组是 GNU 对标准 C 的扩展,可以参考这里 Zero-Length 。若要在一个结体体中声明一个 0 长度数组则这个声明必须放在结构体的最后,如:

struct hci_dev_req {
	uint16_t dev_id;
	uint32_t dev_opt;
};

struct hci_dev_list_req {
	uint16_t dev_num;
	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
};

这个0长度数组的声明是不占用所在结构体的大小的,如:
 

#include <stdio.h>
#include <stdlib.h>

typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

struct hci_dev_req {
        uint16_t dev_id;
        uint32_t dev_opt;
};

struct hci_dev_list_req {
        uint16_t dev_num;
        struct hci_dev_req dev_req[0];  /* hci_dev_req structures */
};

int main()
{
        printf("sizeof struct hci_dev_req = %lu\n", sizeof(struct hci_dev_req));
        printf("size of struct hci_dev_list_req = %lu\n", sizeof(struct hci_dev_list_req));
        return 0;
}

sizeof(hci_dev_list_req) 的大小只是计算了 uint16_t dev_num 的大小,因为默认是4字节对齐,所以这里sizeof 结果为 4。在上面的链接中有这样一句话:Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero。

那这样声明有什么好处呢?

A zero-length array can be useful as the last element of a structure that is really a header for a variable-length object。翻译:零长度数组可以用作结构的最后一个元素,该元素实际上是可变长度对象的头。它的好处就是用于可变长对象,那如果用指针代替是不是也可以实现可变长对象呢?我觉得是可以的,如可以这样声明:

#include <stdio.h>
#include <stdlib.h>

typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

struct hci_dev_req {
        uint16_t dev_id;
        uint32_t dev_opt;
};

struct hci_dev_list_req {
        uint16_t dev_num;
        struct hci_dev_req *dev_req;    /* hci_dev_req structures */
};

int main()
{
        printf("sizeof struct hci_dev_req = %lu\n", sizeof(struct hci_dev_req));
        printf("size of struct hci_dev_list_req = %lu\n", sizeof(struct hci_dev_list_req));
        return 0;
}

此时 sizeof(hci_dev_list_req) = 16,因为在64 位系统下一个指针占用 8 byte 大小,这里应该是按8 byte 对齐了,所以为16,如果设置成 4 byte 对齐,那应该为 12,如:

 

可以看到仅仅是换成了指针,数据结构的长度就增加了 12 byte。而且在实际使用的时候,这个指针可能指向的是 malloc 分配的内存空间,即:

#include <stdio.h>
#include <stdlib.h>

typedef unsigned short uint16_t;
typedef unsigned int uint32_t;

struct hci_dev_req {
	uint16_t dev_id;
	uint32_t dev_opt;
};

#pragma pack(4)
struct hci_dev_list_req {
	uint16_t dev_num;
	struct hci_dev_req *dev_req;    /* hci_dev_req structures */
};
#pragma pack()

int main()
{
	printf("sizeof struct hci_dev_req = %lu\n", sizeof(struct hci_dev_req));
	printf("size of struct hci_dev_list_req = %lu\n", sizeof(struct hci_dev_list_req));

	int num = 16;//
	struct hci_dev_list_req dl;

	dl.dev_req = (struct hci_dev_req*)malloc(sizeof(struct hci_dev_req) * num);

	/* do something*/

	free(dl.dev_req);

	return 0;
}

 使用malloc 或 new 分配的内存,你还必须进行手动释放,否则就会造成内存泄漏。那使用0长度数组的好处呢?看下面的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma pack (1)
typedef struct private_data {
    char tag[4];
    int version;
    char type;
    char data[0];
}PrivateData;

#pragma pack ()


int main()
{
    printf("sizeof PrivateData = %lu\n", sizeof(PrivateData)); // 9 bytes

	/*这样申请内存后,sendBuf->data 就指向了 sendBuf+sizeof(PrivateData) 的位置,
	* 即可以存放数据的内存的头了,这样做的好处是只需要释放一次 free(sendBuf) 即可。
	*/
	size_t totalMem = sizeof(PrivateData) + 256;
	PrivateData *sendBuf = (PrivateData*)malloc(totalMem);
	memset(sendBuf, 0, totalMem);
	
    sendBuf->tag[0] = 'D';
    sendBuf->tag[1] = 'H';
    sendBuf->tag[2] = 'A';
    sendBuf->tag[3] = 'V';
    sendBuf->version = 0x10;
    sendBuf->type = 0x11;
    memcpy(sendBuf->data, "hello world", 11);
    
    printf("sendBuf = %c, %c, %c, %c, data[0] = %s\n", sendBuf->tag[0], sendBuf->tag[1], sendBuf->tag[2], sendBuf->tag[3], sendBuf->data);

	//这里只要释放sendBuf就可以
	free(sendBuf);
    return 0;
}

使用0长度数组的好处就是实现了可变长对象,方便内存的申请和释放。

在 bluez 源码中就有这种用法,如下:

//有类型声明如下
struct hci_dev_req {
	uint16_t dev_id;
	uint32_t dev_opt;
};

struct hci_dev_list_req {
	uint16_t dev_num;
	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
};


static void print_dev_list(int ctl, int flags)
{
	struct hci_dev_list_req *dl;
	struct hci_dev_req *dr;
	int i;

    /* 申请了一大块内存 */
	if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) +
		sizeof(uint16_t)))) {
		perror("Can't allocate memory");
		exit(1);
	}
	dl->dev_num = HCI_MAX_DEV;
	dr = dl->dev_req;/* 这里指向了数据位置 */

	if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
		perror("Can't get device list");
		free(dl);
		exit(1);
	}

    /* 这里操作实际的设备个数  */
	for (i = 0; i< dl->dev_num; i++) {
		di.dev_id = (dr+i)->dev_id;
		if (ioctl(ctl, HCIGETDEVINFO, (void *) &di) < 0)
			continue;
		print_dev_info(ctl, &di);
	}

	free(dl);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值