【转】c语言中内存的动态分配与释放(多维动态数组构建)

2024-08-28 23:18

本文主要是介绍【转】c语言中内存的动态分配与释放(多维动态数组构建),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一. 静态数组与动态数组
静态数组比较常见,数组长度预先定义好,在整个程序中,一旦给定大小后就无法再改变长度,静态数组自己自动负责释放占用的内存。
动态数组长度可以随程序的需要而重新指定大小。动态数组由内存分配函数(malloc)从堆(heap)上分配存储空间,只有当程序执行了分配函数后,才为其分配内存,同时由程序员自己负责释放分配的内存(free)。

二. 为什么要使用动态数组?
在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题,用静态数组的办法很难解决。为了解决上述问题,c语言提供了一些内存管理函数,这些内存管理函数结合指针可以按需要动态地分配内存空间,来构建动态数组,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。

三. 动态数组与静态数组的比较
对于静态数组,其创建非常方便,使用完也无需释放,要引用也简单,但是创建后无法改变其大小是其致命弱点!   
对于动态数组,其创建麻烦,使用完必须由程序员自己释放,否则严重会引起内存泄露。但其使用非常灵活,能根据程序需要动态分配大小。

四. 如何构建动态数组?
构建动态数组时,我们遵循下面的原则:
申请的时候从外层往里层,逐层申请;
释放的时候从里层往外层,逐层释放;

五. 构建动态数组所需指针
对于构建一维动态数组,需要一维指针;   
对于二维,则需要一维,二维指针;   
对于三维,需要一,二,三维指针;   
依此类推。

六. 动态内存分配与释放函数

/*动态内存分配与释放函数*/

void *malloc(unsigned int size);

void *calloc(unsigned int num, unsigned int size);

void *realloc(void *p,unsigned int size);

void free(void *p);
说明:
(1)
malloc()函数成功:返回所开辟空间首地址;失败:返回空指针;功能:向系统申请size字节堆的空间;
calloc()成功:返回所开辟空间首地址;失败:返回空指针;功能:按类型向系统申请num个size字节堆的空间;
realloc()成功:返回所开辟空间首地址;失败:返回空指针;功能:将p指向的空间变为个size字节堆的空间;
free()没有返回值,释放p指向的堆空间;
(2)
规定为void *类型,这并不是说该函数调用后无返回值,而是返回一个结点的地址,该地址的类型为void(无类型或类型不确定),即一段存储区的首址,其具体类型无法确定,只有使用时根据各个域值数据再确定。可以用强制转换的方法将其转换为别的类型。例如:


double *pd = NULL;
pd = (double *)calloc(10, sizeof(double));
表示将向系统申请10个连续的double类型的存储空间,并用指针pd指向这个连续的空间的首地址。并且用(double)对calloc()的返回类型进行转换,以便把double类型数据的地址赋值给指针pd。
(3)
使用sizeof的目的是用来计算一种类型的占有的字节数,以便适合不同的编译器。
(4)检查动态内存是否分配成功
由于动态分配不一定成功,为此要附加一段异常处理程序,不致程序运行停止,使用户不知所措。通常采用这样的异常处理程序段:


if (p == NULL) /* 或者if(!p)*/

{

printf("动态申请内存失败!\n");

exit(1); //异常退出

} 

(5)这四个函数头文件均包含在<stdlib.h>中。   
(6)分配的堆空间是没有名字的,只能通过返回的指针找到它。   
(7)绝不能对非动态分配存储块使用free。也不能对同一块内存区同时用free释放两次,如:


free(p);

free(p);
(8)调用 free()时, 传入指针指向的内存被释放, 但调用函数的指针值可能保持不变, 因为p是作为形参而传递给了函数。严格的讲, 被释放的指针值是无效的, 因为它已不再指向所申请的内存区。这时对它的任何使用便可能会可带来问题。所以在释放一个指针指向的内存后,将该指针赋值为0,避免该指针成为野指针:


int *p = (int *)malloc(sizeof(int));

free(p); /*释放p指向内存*/

p = 0; /*或者 p = NULL,释放p指向的内存后,将p指针赋值为0,避免p指针成为野指针*/
(9)malloc与calloc的区别,对于用malloc分配的内存区间,如果原来没有被使用过,则其中的每一位可能都是0;反之,如果这部分内存空间曾经被分配、释放和重新分配,则其中可能遗留各种各样的数据。也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常运行,但经过一段时间后(内存空间已被重新分配)可能会出现问题,因此在使用它之前必须先进行初始化(可用memset函数对其初始化为0),但调用calloc()函数分配到的空间在分配时就已经被初始化为0了。当你在calloc()函数和malloc()函数之间作选择时,你需考虑是否要初始化所分配的内存空间,从而来选择相应的函数。
六.动态数组构建过程
以三维整型数组为例int array[x][y][z]
先遵循从外到里,逐层申请的原则:
最外层的指针就是数组名array,他是一个三维指针,指向的是array[],array[]是二维指针,所以给array申请内存空间需要一个三维指针int *** p;


/*给三维数组array[x][y][z]动态分配内存*/

int *** p = (int ***)malloc(x * sizeof(int **));
/*或者如下*/
array = (int ***)malloc(x * sizeof(int **))

/*指针p指向的是array三维数组的第一维,有x个元素,所以要sizeof(x * (int **))*/
次层指针是array[],它是一个二维指针,指向的是array[][],array[][]是一维指针:


int i, j;

for (i = 0; i < x; i++)

{

array[i] = (int **)malloc(y * sizeof(int *));

}
最内层指针是array[][],它是个一维指针,所指向的是array[][][],其是个整型常量。所以给array[][]申请内存应:


int i, j;

for (i = 0; i < x; i++)

{

for (j = 0; j < y; j++)

{

array[i][j] = (int *)malloc(z * sizeof(int));

}

}
综合以上三步:


/*动态构建三维数组内存分配函数*/

/*

* pArr: 指向三维数组首地址

* x: 三维数组第一维元素个数

* y: 三维数组第二维元素个数

* z: 三维数组第三维元素个数

*/
void Create3DActiveArray(int ***pArr, int x, int y, int z)
{

int i, j, k;

pArr = (int ***)malloc(x * sizeof(int **));


for (i = 0; i < x; i++)

{

pArr[i] = (int **)malloc(y * sizeof(int *));
for (j = 0; j < y; j++)

{

pArr[i][j] = (int *)malloc(z * sizeof(int));

for (k = 0; k < z; k++)

{

pArr[i][j][k] = i + j + k;

}

}

}
}
内存释放函数:


void Free3DActiveArray(int ***pArr, int x, int y)

{

int i, j, k;

for (i = 0; i < x; i++)

{

for (j = 0; j < y; j++)

{

free(pArr[i][j]);

pArr[i][j] = 0;

}

free(pArr[i]);

pArr[i] = 0;

}

free(pArr);

}

/*

2012年2月29日 12:00:32

目的:多维数组构建和释放,这里以构建一个动态3维数组为例

*/

#include <stdio.h>

#include <malloc.h>

void Malloc3DActiveArray(int *** pArr, int x, int y, int z);

void Free3DActiveArray(int *** pArr, int x, int y);

//void Display3DArray(int *** pArr, int x, int y, int z);

int main(void)

{

int x, y, z;

int *** array = NULL;

printf("输入一维长度: ");

scanf("%d",&x);

printf("输入二维长度: ");

scanf("%d",&y);

printf("输入三维长度: ");

scanf("%d",&z);

Malloc3DActiveArray(array, x, y, z);

Free3DActiveArray(array, x, y);

array = NULL;

return 0;

}

void Malloc3DActiveArray(int *** pArr, int x, int y, int z)

{

int i, j, k;

pArr = (int ***)malloc(x * sizeof(int **));

for (i = 0; i < x; i++)

{

pArr[i] = (int **)malloc(y * sizeof(int *));

for (j = 0; j < y; j++)

{

pArr[i][j] = (int *)malloc(z * sizeof(int));

for (k = 0; k < z; k++)

{

pArr[i][j][k] = i + j + k + 1;

printf("%d ", pArr[i][j][k]);

}

printf("\n");

}

printf("\n");

}

}

void Free3DActiveArray(int *** pArr, int x, int y)

{

int i, j;

for (i = 0; i < x; i++)

{

for (j = 0; j < y; j++)

{

free(pArr[i][j]);

pArr[i][j] = 0;

}

free(pArr[i]);

pArr[i] = 0;

}

free(pArr);

}


/*

2012年2月29日 12:32:02

功能:动态构建4维数组,学习动态构建多维数组,并释放多维数组

*/

#include <stdlib.h>

#include <stdio.h>

int main()

{

int n1,n2,n3,n4;

int ****array;

int i,j,k,m;

puts("输入一维长度:");

scanf("%d",&n1);

puts("输入二维长度:");

scanf("%d",&n2);

puts("输入三维长度:");

scanf("%d",&n3);

puts("输入四维长度:");

scanf("%d",&n4);

array = (int ****)malloc(n1 * sizeof(int***));//第一维

for (i = 0; i < n1; i++)

{

array[i] = (int***)malloc(n2 * sizeof(int**)); //第二维

for (j = 0; j < n2; j++)

{

array[i][j] = (int**)malloc(n3 * sizeof(int*)); //第三维

for (k = 0; k < n3; k++)

{

array[i][j][k] = (int *)malloc(n4 * sizeof(int));//第四维

for (m = 0; m < n4; m++)

{

array[i][j][k][m] = i + j + k + m + 1;

printf("%d\t", array[i][j][k][m]);

}

printf("\n");

}

printf("\n");

}

printf("\n");

}

for (i = 0; i < n1; i++)

{

for (j = 0; j < n2; j++)

{

for (k = 0; k < n3; k++)

{

free(array[i][j][k]);//释放第四维指针

array[i][j][k] = 0;

}

free(array[i][j]);//释放第三维指针

array[i][j] = 0;

}

free(array[i]);//释放第二维指针

array[i] = 0;

}

free(array);//释放第一维指针

array = NULL;

return 0;

}

/*

在vc++6.0中输出结果为:

-----------------------------------

输入一维长度:

1

输入二维长度:

2

输入三维长度:

3

输入四维长度:

4

1 2 3 4

2 3 4 5

3 4 5 6

2 3 4 5

3 4 5 6

4 5 6 7

Press any key to continue

*/

这篇关于【转】c语言中内存的动态分配与释放(多维动态数组构建)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进