# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(四)

本文介绍了C语言中的零长度数组和变长数组特性,主要讨论了它们在GCC编译器中的支持情况。零长度数组在C99标准中被引入,不占用内存,常用于构建变长结构体。通过实例展示了如何利用零长度数组创建可变大小的结构体,并在内核中应用,如USB驱动中的USBRequestBlock结构体。零长度数组在内核中用于动态适应不同数据传输需求,提供了一种灵活的内存管理方式。
摘要由CSDN通过智能技术生成

工欲善其事必先利其器-C语言拓展–嵌入式C语言(四)

零长度数组

零长度数组、变长数组都是GNU C编译器支持的数组类型。

什么是零长度数组?

首先肯定长度是为0的数组

ANSI C规定定义一个数组长度必须为一个常数,那么就是这个数组的长度在编译的时候就确定了。

int a[10];

但是在C99标准中规定可以定义一个变长的数组。

int len;
int a[len]

这样可以让我们的数组在运行的时候再确定,这是变长。

GUN C编译器支持这样定义

int a[0];

零长度数组不占用内存存储空间。可以用sizeog进行测试哦。

零长度一般很少单独使用,常常作为结构体的一个成员,构成一个变长结构体。

struct buffer{
	int len;
	int a[0];
};

这个buffer的大小是4.

下面来展示一下这个玩意怎么用的诶?

首先的用处在于通过零长度数组去访问结构体的内存,但是还不占内存。

闲话少说,给大爷们上代码。

struct buffer{
	int len;
	int a[0];
};


struct buffer *buf;
buf =  (struct buffer *)malloc(sizeof(struct buffer)+20);
buf->len = 20;
strcpy(buf->a,"hello zhaixue.cc!\n");
puts(buf->a)//打印到屏幕

free(buf);//配套释放内存
return 0;

使用malloc申请一片内存,大小为sizeof(buffer)+20,即24字节。其中4字节用来表示内存的长度20,剩下的20字节空间,可以通过结构体成员a直接访问这片内存。这里不就把一个字符串放进去了。

内核中的零长度数组

前面说了零长度数组在内核中一般以变长结构体的形式出现。现在看看实际内核是怎么用这个的。来看个内核中USB驱动的应用。

网卡驱动中我们都知道有个玩意叫套接字:socket buffer,这个用来传输数据包的

同样在USB驱动中,也有个类似的叫做URB:USB Request Block,即USB请求块,用来传输USB数据包。它的结构体长这个样子:

在这里插入图片描述
这个结构体内定义了USB数据包的传输方向传输地址传输大小传输模式等。这些细节我们不深究,只看最后一个成员

struct usb_iso_packet_descriptor iso_fream_desc[0];

这个是干什么的呢?主要用于USB的同步传输。

USB有4种传输模式:中断传输、控制传输、批量传输和同步传输。不同的USB设备对传输速度、传输数据安全性的要求不同,所采用的传输模式也不同。

USB摄像头对视频或图像的传输实时性要求较高,对数据的丢帧不是很在意,丢一帧无所谓,接着往下传就可以了。所以USB摄像头采用的是USB同步传输模式。

USB摄像头一般会支持多种分辨率,从16*16到高清720P多种格式。不同分辨率的视频传输,一帧图像数据的大小是不一样的,对USB传输数据包的大小和个数需求是不一样的。那么USB到底该如何设计,才能在不影响USB其他传输模式的前提下,适配这种不同大小的数据传输需求呢?

当用户设置不同分辨率的视频格式时,USB就使用不同大小和个数的数据包来传输一帧视频数据。通过零长度数组构成的这个变长结构体就可以满足这个要求。USB驱动可以根据一帧图像数据的大小,灵活地申请内存空间,以满足不同大小的数据传输。而且这个零长度数组又不占用结构体的存储空间。当USB使用其他模式传输时,不受任何影响,完全可以当这个零长度数组不存在。

牛吧!!!

不过吧有个想法关于指针的。

为啥不用指针呢?指针指向的内存也可变化啊。

首先说说这个事:数组名在作为函数参数进行参数传递时,就相当于一个指针。那数组名是指针吗?

数组名在作为参数传递时,传递的确实是一个地址,但数组名绝不是指针,两者不是同一个东西。

数组名用来表征一块连续内存空间的地址,而指针是一个变量,编译器要给它单独分配一个内存空间,用来存放它指向的变量的地址。

其实就是数组名是不能变化的,而指针相当于一个盒子,你可以往里面放东西,但是数组名它就个东西。

对于一个指针变量,编译器要为这个指针变量单独分配一个存储空间,然后在这个存储空间上存放另一个变量的地址,我们就说这个指针指向这个变量。

对于数组名,编译器不会再给它分配一个单独的存储空间,它仅仅是一个符号,和函数名一样,用来表示一个地址。

int array1[10] = {1,2,3,4,5,6,7,8};
int array2[0];
int *p = &array1[5]

在这里插入图片描述
交叉编译

反汇编
在这里插入图片描述

数组名array1仅仅表示这40个连续存储空间的首地址

指针变量p,编译器给它分配了0x2104c这个存储空间

在这里插入图片描述
array2的地址为0x21054,在BSS段的后面。array2符号表示的默认地址是一片未使用的内存空间,仅此而已,编译器绝不会单独再给其分配一个存储空间来存储数组名。(bbs段是未初始化的数据,不占用磁盘空间,是在程序执行前由内核初始化完成。)

因此零长度数组不会对结构体定义造成冗余,而且使用起来很方便。

参考资料:《嵌入式C语言自我修养:从芯片、编译器到操作系统》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TrustZone_Hcoco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值