随笔misc: 结构体中空数组的作用

有时候我们会见到结构体中使用长度为0的数组,如下,可以理解,使用来表示边长的内存空间。下图时linux libfdt中例子。在《c高级编程 基于模块化设计思想的C语言开发》一书中也有讲到这一节。
在这里插入图片描述
当然都是用来表示变长的内存的。
可以先下一个结论:这个data[0]是表示这个变量后紧挨着的内存区域,且不占内存空间,准确来说不占栈空间,但是栈用代码段空间。
看测试代码:

#include <unistd.h>
#include <stdio.h>


struct Test{
	int len;
	char data[0];
};

void main()
{
	int a = 12345678;
	struct Test b;
	b.len = 4;
	printf("sizeof(struct Test):%d\n", sizeof(struct Test));
	printf("sizeof(b):%d\n", sizeof(b));
	printf("a:%d\n", a);
	printf("b.data:%p,&b.data:%p, *(int*)b.data:%d\n", b.data, &(b.data), *(int*)(b.data));
}

结果:
在这里插入图片描述
可以看到Test 结构体的长度还是4,为一个int的大小,data不占栈空间。这个怎么理解呢?比如你定义一个数组

void main()
{
` char a[10];
}

这个a代表堆栈中的一个内存空间,你打印a的值为这个这个内存的首地址。但是这个a却是不占堆栈空间的,它是被编译进代码段的,我们是通过代码计算直接访问这个内存空间的,a不占栈空间。
在这里插入图片描述
我们看测试代码的反汇编代码

00010440 <main>:
   10440:	e92d4800 	push	{fp, lr}
   10444:	e28db004 	add	fp, sp, #4
   10448:	e24dd008 	sub	sp, sp, #8
   1044c:	e306314e 	movw	r3, #24910	; 0x614e
   10450:	e34030bc 	movt	r3, #188	; 0xbc
   10454:	e50b3008 	str	r3, [fp, #-8]
   10458:	e3a03004 	mov	r3, #4
   1045c:	e50b300c 	str	r3, [fp, #-12]
   10460:	e3a01004 	mov	r1, #4
   10464:	e3000538 	movw	r0, #1336	; 0x538
   10468:	e3400001 	movt	r0, #1
   1046c:	ebffff9e 	bl	102ec <printf@plt>
   10470:	e3a01004 	mov	r1, #4
   10474:	e3000550 	movw	r0, #1360	; 0x550
   10478:	e3400001 	movt	r0, #1
   1047c:	ebffff9a 	bl	102ec <printf@plt>
   10480:	e51b1008 	ldr	r1, [fp, #-8]
   10484:	e3000560 	movw	r0, #1376	; 0x560
   10488:	e3400001 	movt	r0, #1
   1048c:	ebffff96 	bl	102ec <printf@plt>
   10490:	e24b300c 	sub	r3, fp, #12
   10494:	e2833004 	add	r3, r3, #4
   10498:	e5930000 	ldr	r0, [r3]
   1049c:	e24b300c 	sub	r3, fp, #12
   104a0:	e2832004 	add	r2, r3, #4
   104a4:	e24b300c 	sub	r3, fp, #12
   104a8:	e2831004 	add	r1, r3, #4
   104ac:	e1a03000 	mov	r3, r0
   104b0:	e3000568 	movw	r0, #1384	; 0x568
   104b4:	e3400001 	movt	r0, #1
   104b8:	ebffff8b 	bl	102ec <printf@plt>
   104bc:	e320f000 	nop	{0}
   104c0:	e24bd004 	sub	sp, fp, #4
   104c4:	e8bd8800 	pop	{fp, pc}

栈里变量我们是用sp和fp间接寻址来访问的,c语言中变量的概念,在汇编中已经没有概念了,所以这个a[0]的a其实没有存储,只是一个内存空间的别名,变成了一句汇编指令

   104a4:	e24b300c 	sub	r3, fp, #12
   104a8:	e2831004 	add	r1, r3, #4

如果*是个指针呢?

void main()
{
	char *p;
}

如果是指针,则它是内存中的一变量,需要占栈空间,其存储的值是一地址,即其指向另一块内存空间。

在这里插入图片描述
总结:数组和指针都是一种变量类型,不同长度的数组属于不通类型,这个使用typedef就可以显现,或者用数组类型强转时就会提示类型不一致。都是内存空间的一个别名,其符号本身不占内存空间,在代码段中用sp/fp间接寻址,但是其代表的内存是占内存空间的,而指针类型的长度固定为机器子长,arm中为4。而数组类型的长度是其[]中的长度相关的,如果为0,我们依然可以访问到这一块内存,在汇编中通过sp间接访问,由于其char [0]的长度为0,所以用sizeof读取时,长度不计算在内
数组是直接一块内存空间的别名,而指针是间接指向一块内存空间,所以数组a不占栈空间,而指针要栈用栈空间。

既然是表示变长的内存空间,为何不使用指针呢?

答案是指针没法表示该结构体变量后紧跟着的内存区域,且指针占栈空间,所以就没法表示紧跟着的空间。我们可以给指针赋值为紧跟着的空间,但是也只能是空出一个指针大小的空间了。
在这里插入图片描述

用处

格式化内存空间,比如帧结构,无需弹指针。比如TLV结构,Type-Length-Value结构,我们可以定义结构体:

struct {
	char type;
	int length;
	char value[0];
}

使用时,根据length的长度,防卫value的内存区域,以防越界。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值