【Linux C小技巧】零长度数组的使用

本期主题:

讲清Linux C的零长度数组使用,内容包括:

  1. 零长度数组是什么原理
  2. 为什么要使用零长度数组
  3. 与指针的差异

往期链接:


目录


0.前言

有时候我们在看代码时,会有这种结构体,定义了一个数组长度为0的数组,如下面代码,我初次看见时非常疑惑,那么零长度数组的意义是什么呢?

c 复制代码
typedef struct {
	int a;
	uint8_t c[0];
} test_a;

1.零长度数组原理

  1. 长度为0的数组在标准c和c++中是不允许的,如果使用长度为0的数组,编译时会产生错误,提示数组长度不能为0
  2. 但在GNU C中,这种用法却是合法的。它的最典型的用法就是位于数组中的最后一项,如上面所示,这样做主要是为了方便内存缓冲区的管理

2.零长度数组特点1:不占空间

看下面这段代码:设计一个包含零长度数组的结构体test_a,还有一个包含指针的结构体test_c

c 复制代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>

typedef struct {
	int a;
	uint8_t c[0];
} test_a;

typedef struct {
	int b;
} test_b;

/*这里使用packed,是因为我编译环境是64位
 *如果不用packed,担心影响大家判断
 */
typedef struct {
	int 	c;
	uint8_t *ptr;
} __attribute__ ((packed)) test_c;

int main(void)
{
	test_a a;
	test_b b;
	test_c c;
	printf("sizeof test_a: %ld, sizeof test_b: %ld, sizeof test_c: %ld\n",
			sizeof(a), sizeof(b), sizeof(c));
	return 0;
}

测试结果:

bash 复制代码
$ ./a.out
sizeof test_a: 4, sizeof test_b: 4, sizeof test_c: 12

结论:

可以看出,零长度数组是不占用空间的,这是它优于指针的一个点

3.零长度数组特点2:分配的空间可以连续

设想一个这样的场景,需要设计发送消息的代码,消息包括消息头和payload字段,有两种设计方式:

c 复制代码
typedef struct {
	msg_head_t msg_head;
	uint8_t    data[0];
} msg_t;

typedef struct {
	msg_head_t msg_head;
	uint8_t    *data;
} msg_t;

你觉得哪种更好?

从底层发送接口的角度来考虑,设计函数时,有一个消息的整体指针作为入参就可以了,所以当然是第一种数据连续的底层更好适配。这是零长度数组的第二个特点,比指针更为灵活,空间连续

看测试代码:

c 复制代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

typedef struct _msg_a {
	int 	len;
	uint8_t data[0];
} msg_a_t;

typedef struct _msg_b {
	int 	len;
	uint8_t *data;
} msg_b_t;

int main(void)
{
	int data_len = 10;
	msg_a_t *ptr_msg_a = (msg_a_t *)malloc(sizeof(msg_a_t) + data_len);
	msg_b_t *ptr_msg_b = (msg_b_t *)malloc(sizeof(msg_b_t));
	ptr_msg_b->data = malloc(data_len);
	
	printf("a: %p, a_data: %p, b: %p, b_data: %p\n",
			ptr_msg_a, ptr_msg_a->data, ptr_msg_b, ptr_msg_b->data);
	return 0;
}

结果:

bash 复制代码
$ ./a.out
a: 0x560958fb1260, a_data: 0x560958fb1264, b: 0x560958fb1280, b_data: 0x560958fb12a0

能看出零长度数组确实是连续空间,符合预期。

参考自 嵌入式C语言自我修养 05:零长度数组

相关推荐
不屈的铝合金7 小时前
MySQL 数据库服务多实例部署指南
运维·数据库·mysql·多实例部署·维度隔离
学习者0077 小时前
NE相关知识之------路由知识
运维·服务器
杨云龙UP8 小时前
SQL Server定时自动备份配置:使用SSMS维护计划向导配置数据库每日自动备份_20260101
运维·服务器·数据库·sql·sqlserver·桌面
oscar9998 小时前
CI_CD Pipeline趋势:加速集成与交付
运维·ci/cd·devops
java_logo8 小时前
ComfyUI Docker 镜像部署指南
运维·docker·容器·comfyui部署·docker部署comfyui·comfyui部署文档·comfyui部署教程
天才程序YUAN8 小时前
Windows自动修改系统环境变量(PATH)中所有 D 盘路径的脚本
运维·windows
眠りたいです8 小时前
docker-compose:使用docker-compose对多容器应用进行管理并进行wordpress简单站点的搭建
运维·nginx·docker·容器·wordpress·busybox
一路往蓝-Anbo8 小时前
C语言从句柄到对象 (七) —— 给对象加把锁:RTOS 环境下的并发安全
java·c语言·开发语言·stm32·单片机·嵌入式硬件·算法
长河_讲_ITIL48 小时前
在硅基的倒影中寻找自我:写在AI智能体元年的一场思想突围
运维·人工智能·itss·itil·itil认证·itil培训
赵民勇8 小时前
tr命令用法详解与技巧总结
linux·shell