龙空技术网

计算机内存,C语言如何动态管理内存?linux C第50讲

远峰linux编程学堂 573

前言:

当前大家对“c语言图书馆管理”大体比较关怀,小伙伴们都需要了解一些“c语言图书馆管理”的相关资讯。那么小编也在网上搜集了一些有关“c语言图书馆管理””的相关知识,希望小伙伴们能喜欢,朋友们一起来学习一下吧!

内存动态管理

在C语言代码中,定义一个变量,就会在内存中申请对应变量类型的空间,用于给变量存放数据。例如:

long num; //定义long类型的变量,申请 4 个字节的内存空间

char buf[8]; //定义数组,数组元素是char类型,每个元素占用1个字节空间,数组总共占用8个字节空间;

那么,我们来开发一个图书馆管理系统,在代码中定义一个函数,获取图书馆中图书的数量,代码如下:

程序编译运行的结果如下:

可以看到,编译的时候,有一个“warning”警告,提示在get_book_num()函数中返回一个局部变量的地址。

运行程序,调用get_book_num()函数,在该函数中定义一个int类型的局部变量,如下:

int book_num = 168;

定义的book_num变量是一个局部变量,存放图书馆图书数量,然后,get_book_num()函数返回该book_num变量的地址。

在main()函数中,接收get_book_num()函数的book_num变量的地址,然后,通过指针,访问book_num变量的值。最终,发现访问book_num变量的值,是错误的值,而不是get_book_num()函数中定义的168这个数值。为什么?

此时,就涉及到两个问题:

(1) 局部变量生命周期的问题;

(2) 内存堆和内存栈的问题;

弄清楚了这两个问题,就知道get_book_num()函数返回的book_num变量地址为何不可以使用,我们应该怎么样设计程序,让get_book_num()函数实现正确的功能。

一个程序被加载到系统中运行,程序中定义的变量、函数就会被加载到内存中,由CPU进行运算,运行程序。

那么,程序中定义的变量、函数加载到内存中,有两种存储区域:内存堆和内存栈。它们有如下的定义:

(1) 内存栈上存储的数据,会被自动销毁。数据的生命周期结束的时候,内存栈上的数据被清除。

(2) 定义局部变量,是在内存栈上申请空间。局部变量的生命周期结束时,该变量就会从内存栈上清除。

(3) 内存堆上存储的数据,不会被自动销毁。必须在代码中进行释放(开发人员必须要手动释放),否则程序不会自动销毁内存堆上的数据。

此时,我们学习了内存栈和内存堆的定义,就知道了上面程序问题的原因,是因为:

(1) 在get_book_num()函数中定义的book_num变量是局部变量,该变量是在内存栈上申请空间,存放book_num变量的数据。

(2) 在main()函数中调用get_book_num()函数,获取内存栈上book_num变量的地址,并存放到pnum变量中。但是,退出get_book_num()运行之后,book_num局部变量被销毁

(3) book_num局部变量在内存栈上被销毁之后,pnum指针存放book_num变量的地址,通过pnum指针访问book_num变量的值,就是一个随机的数据(因为,局部变量已经被销毁)。因为book_num局部变量在内存栈上被销毁之后,地址被系统回收,重新存放其他数据。

在上面的例子中,我们调用get_book_num()函数之后,再调用func()函数,就是为了让程序操作内存栈,回收book_num局部变量的空间。如果把func()函数给屏蔽,发现pnum指针可以正确获取到168这个数值。

所以,需要调用func()函数,让程序操作内存栈,回收book_num局部变量申请的内存栈空间。

此时,我们知道了问题的原因,那么,怎么样解决这个问题?

我们知道“内存堆”上的数据不会被系统自动销毁,那么,可以把book_num变量的数据存储到“内存堆”上,退出get_book_num()函数之后,数据不会被自动销毁,就可以通过pnum获取到正确的数据了。

那么,怎么样在“内存堆”上存放数据?

在C语言中,提供了malloc()、free()等函数,用于内存动态管理。可以在内存堆上申请和销毁空间。函数的定义如下:

//在内存堆上申请 num 个连续的内存块, 每个内存块是 size 个字节;

//空间的总容量是 num*size 个字节;

//就相当于一个数组, num是数组元素的个数, size 是每个元素的字节大小;

//成功返回申请到内存空间的首地址, 失败返回 NULL;

void *calloc( size_t num, size_t size );

//释放内存堆上的空间, 参数 ptr 是需要释放内存堆的首地址;

void free( void *ptr );

//在内存堆上申请 size 个字节的内存块;

//成功返回申请到内存空间的首地址, 失败返回 NULL;

void *malloc( size_t size );

//调整内存堆上的空间容量, 修改参数 ptr 指向内存块的容量;

//参数 size 是新调整后的内存块容量;

//成功返回申请到内存空间的首地址, 失败返回 NULL;

void *realloc( void *ptr, size_t size );

那么,我们修改上面的程序,在get_book_num()函数中,调用malloc()函数,在内存堆上申请存储空间,存放图书数量的值。程序代码如下:

程序的编译和运行结果如下:

可以看到,在get_book_num()函数中调用malloc()函数,申请内存堆上的空间,然后,返回申请到的空间首地址。

由于内存堆上的数据不会被系统自动销毁,所以,退出get_book_num()函数之后,内存堆上申请的空间还存在,所以,通过pnum指针访问内存堆上的数据,可以正确获取到内存堆上存储的数据。

最后,内存堆上申请的空间,需要调用free()函数进行释放。

在内存堆上申请内存空间的时候,函数的返回值是void*类型,那么,我们可以通过指针强制类型转换,转换成我们对应的类型,例如:

//申请16个字节的空间,返回申请到的内存空间首地址;

char* p = (char*)malloc(16);

//申请8个int类型的空间,返回申请到的内存空间首地址;

int* p1 = (int*)malloc(8*sizeof(int));

注意,下面讲解的是结构体类型,这个知识点我们在后面再深入详解。这里,我们只是结合malloc()函数的使用,讲解malloc()函数返回的void*类型,可以转换成其他指针类型。

因为,我们在程序设计中,经常用到malloc()函数申请内存块,存放结构体变量的数据,所以,这里,作者结合实际的开发经验,就穿插提前讲解结构体类型和malloc()函数的使用。

下面,是使用calloc()函数来创建一个内存块,相当于创建一个结构体类型的数组。

//定义一个结构体类型;

struct test

{

int x;

int y;

};

//申请3个连续空间的struct test结构体变量, 等效于 struct test pt[3]; 数组;

struct test* pt = (struct test*)calloc(3, sizeof(struct test));

因为动态申请的内存空间可以用于存放任何数据,所以,返回的是void*类型的指针。然后,根据用户自己的定义,想存放什么类型的数据,就转换成对应的指针类型。

标签: #c语言图书馆管理 #c语言实现内存管理 #c语言中的动态存储管理