可变长度数组分析说明

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

相关文章

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

redis和redission分布式锁原理及区别说明

《redis和redission分布式锁原理及区别说明》文章对比了synchronized、乐观锁、Redis分布式锁及Redission锁的原理与区别,指出在集群环境下synchronized失效,... 目录Redis和redission分布式锁原理及区别1、有的同伴想到了synchronized关键字

MySQL 临时表创建与使用详细说明

《MySQL临时表创建与使用详细说明》MySQL临时表是存储在内存或磁盘的临时数据表,会话结束时自动销毁,适合存储中间计算结果或临时数据集,其名称以#开头(如#TempTable),本文给大家介绍M... 目录mysql 临时表详细说明1.定义2.核心特性3.创建与使用4.典型应用场景5.生命周期管理6.注

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Java中数组与栈和堆之间的关系说明

《Java中数组与栈和堆之间的关系说明》文章讲解了Java数组的初始化方式、内存存储机制、引用传递特性及遍历、排序、拷贝技巧,强调引用数据类型方法调用时形参可能修改实参,但需注意引用指向单一对象的特性... 目录Java中数组与栈和堆的关系遍历数组接下来是一些编程小技巧总结Java中数组与栈和堆的关系关于

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

MySQL中读写分离方案对比分析与选型建议

《MySQL中读写分离方案对比分析与选型建议》MySQL读写分离是提升数据库可用性和性能的常见手段,本文将围绕现实生产环境中常见的几种读写分离模式进行系统对比,希望对大家有所帮助... 目录一、问题背景介绍二、多种解决方案对比2.1 原生mysql主从复制2.2 Proxy层中间件:ProxySQL2.3

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl