可变长度数组分析说明

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

相关文章

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

mybatis-plus QueryWrapper中or,and的使用及说明

《mybatis-plusQueryWrapper中or,and的使用及说明》使用MyBatisPlusQueryWrapper时,因同时添加角色权限固定条件和多字段模糊查询导致数据异常展示,排查发... 目录QueryWrapper中or,and使用列表中还要同时模糊查询多个字段经过排查这就导致只要whe

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Olingo分析和实践之ODataImpl详细分析(重要方法详解)

《Olingo分析和实践之ODataImpl详细分析(重要方法详解)》ODataImpl.java是ApacheOlingoOData框架的核心工厂类,负责创建序列化器、反序列化器和处理器等组件,... 目录概述主要职责类结构与继承关系核心功能分析1. 序列化器管理2. 反序列化器管理3. 处理器管理重要方

SpringBoot改造MCP服务器的详细说明(StreamableHTTP 类型)

《SpringBoot改造MCP服务器的详细说明(StreamableHTTP类型)》本文介绍了SpringBoot如何实现MCPStreamableHTTP服务器,并且使用CherryStudio... 目录SpringBoot改造MCP服务器(StreamableHTTP)1 项目说明2 使用说明2.1

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原