可变长度数组分析说明

2024-08-31 04:48

本文主要是介绍可变长度数组分析说明,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、零长度数组说明

         长度为的数组在标准c和c++中是不允许的,如果使用长度为的数组,编译时会产生错误,提示数组长度不能为。但在GNUc中,这种用法却是合法的。它的最典型的用法就是位于数组中的最后一项,如上面所示,这样做主要是为了方便内存缓冲区的管理。如果你将上面的长度为的数组换为指针,那么在分配内存时,需采用两步:首先,需为结构体分配一块内存空间;其次再为结构体中的成员变量分配内存空间。这样两次分配的内存是不连续的,需要分别对其进行管理。当使用长度为的数组时,则是采用一次分配的原则,一次性将所需的内存全部分配给它。相反,释放时也是一样的。

2、零长度代码示例

对于长度为的数组,在gcc手册中,有如下一段代码片段:

struct line {int length;char contents[0];
};struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

这段代码的主要含义是定义了一个结构体,并对其进行初始化。当采用malloc为其申请内存空间时,如上所示,申请了一段长度为结构体长度加可变长度的内存空间给结构体类型的指针,这时contents就指向申请的可变长度的内存空间。由于是一次申请的,所以这段可变长度的内存空间和前面的结构体长度的内存空间是连续的。对于这段可变长度的内存空间,可以采用数组的方式对其进行访问。对于整个结构体,当不再使用时,可以使用free函数一次性对其进行释放,而不必像指针那样分别释放。

3、零长度数组测试代码

测试代码如下:

#include <stdio.h>
#include <stdlib.h>
#define LENGTH 10struct test1
{int a;int *b;
}__attribute((packed));struct test2
{int a;int b[0];
}__attribute((packed));struct test3
{int a;int b[2];
}__attribute((packed));int main()
{struct test1 *var1;struct test2 *var2;struct test3 *var3;int i = 0;printf("the length of struct test1:%d\n",sizeof(struct test1));printf("the length of struct test2:%d\n",sizeof(struct test2));printf("the length of struct test3:%d\n",sizeof(struct test3));var1=(struct test1*)malloc(sizeof(struct test1));var1->a=1;var1->b=(int *)malloc(sizeof(int));*var1->b=1;printf("\nvar1->a=%d,*(var1->b)=%d\n",var1->a,*var1->b);var2=(struct test2*)malloc(sizeof(struct test2)+sizeof(int)*LENGTH);var2->a=2;printf("\nvar2->a=%d\n",var2->a);for(i=0;i<LENGTH;i++){var2->b[i]=i;printf("var2->b[%d]=%d\r\n",i,var2->b[i]);}printf("\n\n");var3=(struct test3*)malloc(sizeof(struct test3));var3->a=3;(var3->b)[0]=3;(var3->b)[1]=4;printf("var3->a=%d,(var3->b)[0]=%d, (var3->b)[1]=%d\n",var3->a,(var3->b)[0], (var3->b)[1]);free(var1->b);free(var1);free(var2);free(var3);return 0;
}

测试结果如下:

4、相关说明

  1. 尽管零长度数组的大小为零,但由于尾部填充,此类数组成员可能会增加封装类型的大小。零长度数组成员导致的偏移量与具有一个或多个相同类型元素的数组的偏移量相同。零长度数组的对齐与有元素的数组对齐方式相同。
  2. 不鼓励在其他上下文中声明零长度数组,包括作为结构对象的内部成员或作为非成员对象。访问在这种上下文中声明的零长度数组的元素是未定义的,可能被编译器检测出来。
  3. 在没有长度为零的数组扩展的情况下,在ISO C90中,上面示例中的内容数组通常被声明为只有一个元素。不像zerolength数组,它只决定封闭结构的大小以达到对齐的目的,一个元素数组总是占用至少与该类型的单个对象相同的空间。虽然不鼓励使用单元素数组,但是GCC可以处理访问以类似的方式尾随单元素数组成员到零长度数组。
  4. 在ISO C99声明可变长度类型(如上面的struct line)的首选机制是灵活数组成员,语法和语义略有不同:
    •灵活的数组成员被写为内容[],没有0。
    •灵活的数组成员具有不完整的类型,因此sizeof操作符可能不会被应用。作为零长度数组的原始实现的一个奇怪之处,sizeof计算结果为0。
    •灵活数组成员只能作为结构体的最后一个成员出现,否则非空。
    •包含灵活数组成员的结构,或包含此类结构(可能是递归的)的联合,可能不是结构或数组的成员。(但是,GCC允许这些使用作为扩展。)
    初始化效果相同
    struct f1 {int x; int y[];
    } f1 = { 1, { 2, 3, 4 } };
    struct f2 {struct f1 f1; int data[3];
    } f2 = { { 1 }, { 2, 3, 4 } };

     

  5. GCC允许C结构没有成员:

    struct empty {
    };

    结构的大小为零。在c++中,空结构是语言的一部分。G + +将空结构视为具有char类型的单个成员。

5、可变长数组

ISO C99允许可变长度的自动数组,作为GCC的扩展,在C90模式和c++中也支持这种特性。这些数组的声明与任何其他自动数组一样,但其长度不是常量表达式。数据的存储空间在声明处被分配,当包含声明的块作用域退出时,释放存储空间。例如:

FILE *
concat_fopen (char *s1, char *s2, char *mode)
{char str[strlen (s1) + strlen (s2) + 1];strcpy (str, s1);strcat (str, s2);return fopen (str, mode);
}

 跳转出数组名称的作用域后,会释放存储空间。不允许跳入数组名的作用域。

  • 作为扩展,GCC接受可变长度数组作为结构或联合的成员。例如:
void
foo (int n)
{struct S { int x[n]; };
}

可以使用函数alloca来获得类似于可变长度数组的效果。alloca函数在许多其他C实现中都可用(但不是全部)。另一方面,可变长度数组更优雅。

  • 使用可变长度数组作为函数的参数:
struct entry
tester (int len, char data[len][len])
{/* . . . */
}

数组的长度在分配存储时计算一次,并记住数组的范围,便于通过sizeof访问数组长度。如果希望先传递数组,然后再传递长度,可以在参数列表中使用前向声明--另一个GNU扩展。

struct entry
tester (int len; char data[len][len], int len)
{/* . . . */
}

分号前的' int len '是一个参数前向声明,它的目的是在解析数据声明时,使名称len能够正常解析。

可以在参数列表中写入任意数量的此类参数前向声明。它们可以用逗号或分号分隔,但最后一个必须以分号结束,分号后面跟着“真实的”参数声明。每个前向声明必须在参数名和数据类型上匹配“真实”声明。ISO C99不支持参数前向声明。

借鉴资料:

  • https://blog.csdn.net/zhaqiwen/article/details/7904515
  • 《Using the GNU Compiler Collection》
     

 

这篇关于可变长度数组分析说明的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1122927

相关文章

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

JavaScript对象转数组的三种方法实现

《JavaScript对象转数组的三种方法实现》本文介绍了在JavaScript中将对象转换为数组的三种实用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录方法1:使用Object.keys()和Array.map()方法2:使用Object.entr

Python sys模块的使用及说明

《Pythonsys模块的使用及说明》Pythonsys模块是核心工具,用于解释器交互与运行时控制,涵盖命令行参数处理、路径修改、强制退出、I/O重定向、系统信息获取等功能,适用于脚本开发与调试,需... 目录python sys 模块详解常用功能与代码示例获取命令行参数修改模块搜索路径强制退出程序标准输入

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺