C语言的柔性数组,可能你听过,但未实际用过

tech2024-08-01  46

文章目录

语法形式特性如何使用特性验证我在项目中的使用总结 引用外网的一段内容( 原文链接):

Flexible Array Member(FAM) is a feature introduced in the C99 standard of the C programming language.

For the structures in C programming language from C99 standard onwards, we can declare an array without a dimension and whose size is flexible in nature.Such an array inside the structure should preferably be declared as the last member of structure and its size is variable(can be changed be at runtime).The structure must contain at least one more named member in addition to the flexible array member.

有可能你之前也听说过柔性数组,但还并未在项目中实际用过,刚好我最近做一个项目就用到了柔性数组,或许可以给你一些启发。 柔性数组是C99标准引入的特性,所以当你的编译器提示不支持的语法时,请检查你是否开启了C99选项或更高的版本支持。

语法形式

struct vectord { short len; // 必须至少有一个其它成员 char arr[]; // 柔性数组必须是结构体最后一个成员(也可是其它类型,如:int、double、...) };

特性

用sizeof计算结构体大小时,柔性数组成员是不计入结果的。即: // 假设short类型占用两个字节 结构体大小 = sizeof(struct vectord) = 2 + 0; 整个结构体的内存空间是连续的。

如何使用

定义成结构体变量。如果定义成变量的话,那么柔性数组是操作不了的,因为无法为其分配内存,强行访问的话会产生段错误。

struct vectord vec;

定义成结构体指针。这才是正确操作。

struct vectord *pvec; // 分配内存(假设需要一个20个元素的数组) pvec = malloc(sizeof(struct vectord) + 20 * sizeof(*pvec->arr));

特性验证

先来看第一个特性,使用如下的代码测试结构体的大小。

struct vectord { short len; char arr[]; }; int main(int argc,char *argv[]) { printf("The size of structure is:%ld\r\n",sizeof(struct vectord)); return 0; }

测试结果,柔性数组确实没有参与计算:

The size of structure is:2

下面验证第二个特性。这里与结构体内嵌指针的形式进行对比。

struct vectord { short len; char arr[]; }; struct test { short len; char *p; }; int main(int argc,char *argv[]) { struct vectord *pvec; struct test *pt; pvec = malloc(sizeof(struct vectord) + 20); pt = malloc(sizeof(struct test)); pt->p = malloc(20); printf("%p,%p\r\n",&pvec->len,&pvec->arr); printf("%p,%p\r\n",&pt->len,&pt->p); return 0; }

测试结果,包含柔性数组成员的结构体内存确实是连续的,而内嵌指针的方式却不一定:

0x560511a0f670,0x560511a0f672 0x560511a0f690,0x560511a0f698

我在项目中的使用

项目需求是串口1接收到一帧数据(不定长)之后,我需要在这帧数据的前面再封装进一帧数据,然后将组成的一帧新的数据通过串口2再发出去。即:

串口1->1234567 | V 组帧->abcdefg1234567 | V abcdefg1234567->串口2

这里有以下常用的方法:

直接分两次发送,调用两次串口发送函数就行了。定义一个大数组,将两帧合并后再发送。暂时想不到还有什么其它好方法。

上面两种方法都可以实现,第一种需要调用两次串口发送函数,第二种则是空间置换,都不是很好的方案。 这个时候柔性数组就派上用场了,根据其特性,内存是动态的可按需分配,不会额外浪费空间,而且其内存空间是连续的,所以可以像数组一样通过下标访问;这两个特性刚好就可以将上面两种方案的优点结合到一起。所以可以使用如下的方案:

#pragma pack(1) struct vectord { uint16_t packet_len; char buffer1[4]; char buffer2[]; }; #pragma pack() void uart2_send(uint8_t *data,uint32_t len); int main(int argc,char *argv[]) { struct vectord *pvec; pvec = malloc(sizeof(struct vectord) + 4); // 模拟组帧 pvec->packet_len = 2 + 4 + 4; strncpy(pvec->buffer1,"abcd",4); strncpy(pvec->buffer2,"1234",4); // 模拟硬件串口的发送 uart2_send((uint8_t *)pvec,pvec->packet_len); return 0; } void uart2_send(uint8_t *data,uint32_t len) { uint32_t i = 0; printf("%02x %02x ",data[1],data[0]); i += 2; for(;i < len;i++) { printf("%c",data[i]); } printf("\r\n"); }

测试结果:

00 0a abcd1234

总结

柔性数组还有很多适合使用的场合,我这里只是其中一种,只要符合其特性的都可以采用柔性数组来完成。善于利用各种语法特性,可以将复杂的代码简单化,也更能检测自己是否已经掌握了其真正的用法。

欢迎扫码关注我的微信公众号

最新回复(0)