文章广告位
入驻说明

文章最后更新时间:2024-02-15 19:36:16

第十二章:实现可变数组 struct{int namelen; char namestr[1];};,了解C++基本语言特性变量和类型

  变长数组(非const 变量来定义数组的长度)是每个C++开发人员梦寐以求的东西。通常实现 C++变长数组时,主要通过 new(或 malloc)实现。如下面这段代码:

nt inamelen=100:
char *pNameStr=new char [inamelen ];

  但这种实现有两个显著的缺点:

  (1) pNameStr指针无法记录自己的长度,它的长度必须另行存储,而且还需明确知道记录 pNameStr 长度的变量是 inamelen。假如记错了,那么你的程序就会存在崩溃的风险。

  (2) pNameStr 在使用完后必须还给内存,否则就会存在内存泄漏的风险,因为pNameStr存储区在堆上。

  我们继续看这样的结构体,它可以记录数组的长度。首先看一下这样的实现形式:

struct NameStr
{
  Int namelen;       //名称字符串长度
  char*psznamestr;   //名称字符串指针
}
//创建一个可变数组,数组长度为n
Struct NameStr  MyName;
MyName.namelen =n;
MyName.psznamestr= malloc(n*sizeof(char ))

  这种结构体的数组大小是动态分配的,但这样分配的地址是不连续的(即psznamestr 指向的数组不是接在 MyName.namelen 的后面),这样有时候会不方便,比如说用 memcpy 去从 MyName 首地址开始复制,只能复制 namelen和 psznamestr的值(即数组的首地址),如果想要得到数组里的数据,还要用memcpy 从 psznamestr 的值再来复制一次,这样就很麻烦,特别是在 socket 编程的时候,每次传出的数据都是“大小+数据”的模式,如果是这样写的结构体,就需要调用两次传输函数。但是下面这种写法可以解决这个麻烦。

  在 C89 中,有一种称为 struct hack 的方法来得到可变数组。我们首先看下面这个可变数组实现:

struct NameStr
{
  intnamelen;        // 名称字符串长度
  char namestr[1];   // 名称字符串数组地址
}
//创建一个可变数组,数组长度为n
struct NameStr*p NameStr=malloc(sizeof(struct NameStr) + (n-1)*sizeof(char ));

  动态数组Namestr 分配于堆上,在堆内存上的布局如图2-4所示。创建时通过申请内存生成,使用完毕后必须释放申请的内存。虽然这种实现同样必须申请和释放内存,但是它有一个明确的优点,即这种实现可以记录动态数组的长度。

第十二章:实现可变数组 struct{int namelen; char namestr[1];};,了解C++基本语言特性变量和类型

  小心struct hack陷阱

  ● struct hack 动态数组必须分配于堆上。它通过 struct 数据结构实现,动态数组成员(如 namestr)必须是 struct 的最后一个数据成员。

  ● struct hack 可记录动态数组的长度。struct hack 同样存在一个缺点,即不需要编程人员手工分配和释放。

  struct hack方式虽然解决了动态数组的问题,但是 struct hack 得到的数组长度n让人感觉有些蹊跷(在内存申请时只申请了n-1个,而实际上分配的数组长度却为 n)。所以为了解决这种问题,C99提供了类似但合法的 struct 机制。采用这种机制,上述可变数组实现可改写成下述代码:

struct NameStr
{
  int  namelen;      //名称字符串长度
  char namestr [];    //名称字符串数组地址
}
//创建一个可变数组,数组长度为n
struct NameStr*p NameStr=malloc(sizeof(struct NameSt) + (n)*sizeof(char ));

  C99的这种 struct 机制让 struct hack 动态数组实现更符合编程人员的视觉需要,也更容易理解。但它依然没有解决编程人员手动内存分配而引入的内存泄漏风险。

  最后,我们看一种真正的动态数组实现方式,这种实现方式一可解决编程人员手动内存操作的内存泄漏风险,二可具有普通数组的一切特性,但是这种数组的长度可以是一个非 const 变量。这就是 C99 新引入的变长数组功能。

  新的C99标准新增了变长数组的支持,用户可以像以前定义数组一样定义一个变长数组。数组长度可以是一个非 const 变量。可变数组的空间大小直到程序运行时才能确定,因此只有程序在运行时才能为数组分配空间。GCC编译器会在程序运行时根据实际指定的大小(变量当前的值)调节ESP的值,为数组在栈上分配适当大小的空间。由于在运行时才能为数组分配空间,在开始分配空间之前空间的大小是不确定的,因此分配空间的起始地址也是不确定的(如要在栈上分配两个可变长数组的情况下)。

  为了在以后的代码中对可变长数组的内容进行引用操作,程序必须通过某种方式获取可变长数组的地址。在 GCC 编译器中,会在相对于 EBP 固定偏移量的栈上分配的一个固定大小的区域(称为内情向量)来记录可变长数组的信息,如数组的开始地址等,后续代码通过内情向量中的起始地址访问可变长数组。因为数组依靠在程序运行时动态地调整EBP来分配空间,所以这种类型的数组只能够定义在栈内,不能够定义在数据段(全局数组、静态数组)上。

  下面这段代码是采用动态数组的编程实现:

#include<stdio.h>
#include<stdlib.h>

int main()
{
  unsigned int uiArrySize =0;       //变长数组非const常量长度存储单元
  fscanf(stdin, "%d",&uiArraySize);
  int aiArray[uiArySize];           //定义变长数组
  if(0 ==uiArry Size)               //判断长度是否为0。若非0,则打印数组数据
  {
    printf("aiArray array is a empty array!\n");
    returm 0;
}
Else
{
    /*错误:loop初始化声明仅仅适用于C99模式下
    Note:需应用-std=c99 或-std-gnu99 编译选项*/
    for(inti=0;i<uiArrySize;i++)
  {
  printf("%d\t", aiArra [i]);
  }
 }
}

  小心陷阱

  ● 虽然大部分编译器已支持C99标准,但是至今依然有很多编译器不支持可变数组,它们遵从的依然是C89标准。

  ● 可变数组必须分配到栈上,不能分配到数据段上,如全局数组、静态数组就不能定义为可变数组。

  请谨记

  ● C99支持的可变数组功能的确可以给编程带来很多方便,但请谨慎使用,因为并不是所有的编译器都支持这种新功能。

  ● C89支持 struct hack可变数组实现,虽然要求编程人员自己去申请和释放数组,但是在要求跨平台和兼容性较好的编程中,这种实现未必不是一个最佳的选择。

第十二章:实现可变数组 struct{int namelen; char namestr[1];};,了解C++基本语言特性变量和类型
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA打赏
共{{data.count}}人
人已打赏
技术教程

第十一章:为何struct x1{struct x1 stX};无法通过编译,了解C++基本语言特性变量和类型

2024-2-20 11:02:00

技术教程

九大关键点揭秘,轻松掌握系统技术教程

2024-2-22 9:16:00

0 条回复 A文章作者 M管理员
夸夸
夸夸
还有吗!没看够!
    暂无讨论,说说你的看法吧
个人中心
购物清单
优惠代劵
今日签到
有新私信 私信列表
快速搜索
关注我们
  • 扫码打开当前页

你已经到达了世界的尽头

  • 3350

    文章数目

  • 223

    注册用户

  • 1766

    总评论数

  • 287

    建站天数

  • 47573

    总访问量

  • 波浪
  • 波浪
  • 波浪
  • 波浪