可变长度数组分析说明

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

相关文章

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

Redis分布式锁使用及说明

《Redis分布式锁使用及说明》本文总结了Redis和Zookeeper在高可用性和高一致性场景下的应用,并详细介绍了Redis的分布式锁实现方式,包括使用Lua脚本和续期机制,最后,提到了RedLo... 目录Redis分布式锁加锁方式怎么会解错锁?举个小案例吧解锁方式续期总结Redis分布式锁如果追求

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的