C语言接收函数返回的指针不随出栈销毁
在C语言中除了栈,还有堆可以存储数据,其存储的数据不随栈弹出而消失,但需要手动释放。今天我们使用动态内存分配方法,从函数内传出变长数组。
举个例子:
设想场景:你写了一个程序,从文件中读取所有的男同学学生信息。
学生信息包含了:姓名,年龄,性别,学号,语文成绩,数学成绩,英语成绩。除了学号是唯一的身份识别,其他身份特征是模糊的,也就是说通过性别读取,将读取一堆学生。我们尝试着定义了一个结构体数组来接收数据,数组长度为100,当读取数量超过100后,如果没有对指针加以限制,会导致数据越界,甚至栈溢出。为了数据安全,我们得确定数据长度才行!
数据放在栈中不太可行,将其放在堆中才是合理的解决办法( 内存充足的情况下 )。使用 realloc() 函数在堆中实时重分配空间,对要分配的数据加以补充。realloc()函数原型为:
void* realloc (void* ptr, size_t size);
void* 返回重分配内存的首地址
void* ptr 之前的分配的内存区域
size_t size 重分配的内存大小
注意:当ptr为 NULL 时,realloc() 与 malloc() 无区别;重分配的内存大于之前分配的内存,原数据不会受要影响,同时转移至新分配的的内存中,但新增加的内存中残存不确定的值,若重分配的内存小于之前内存空间大小,则切除多余的部分,但实际核验中,函数只是将内存大小缩小了,其连接的数据在内存充足的情况下,还一直存在,但不保证随时会消失;
接下来,是相关数据定义:
这是学生结构体,使用 typedef 重定义类型名称,它储存着学生的信息,STUDENT,LPSTU 分别为 struct stu 的别名和别名指针:
下面是定义文件指针,接收结果的指针,以及打开文件的方式。
这里定义了20个数据块,并初始化,以及定义了一个指针指向它,LSPSTU 就是 strucu stu *,紧接着写入了12个数据块。
接下来定义和实现了一个写入函数与输出函数,
int writeDta(FILE *fp,STUDENT * stu,size_t length);//写入函数
void printData(LPSTU student,size_t length);//输出函数
实际操作中,写入了15个数据,目的是产生垃圾数据,让主要功能函数实现甄别的能力。
接下来就是实现所述功能的函数,设想思路:
先读取文件,如果没有数据则不在堆中申请内存,返回文件异常,如果有数据, 无论数据是否有效,申请一个内存为10 * 44的暂存区,此处为初始化,暂存区大小为10 * 44,这是为了减少对内存的频繁扫描,即查找合适的内存大小供分配。
之后所有的数据要进行比对,只有匹配给出的数据,才能存储到暂存区,当有效数据个数大于暂存区时,进行重新分配,然后再存储到新的暂存区。
当读取至文件结尾, 数据筛选完毕, 这时收缩内存, 按照个数进行收缩, 假如已分配30 * 44的大小空间,但实际使用25 * 44, 最终将分配的内存大小收缩至25 * 44。
最后将指针指向该内存,并返回。
这是主要功能函数的定义:
LPSTU findStuByGender(FILE *fp,char *gender,STUDENT **student);
LPSTU 为 struct stu * 的别名;
FILE * fp 文件指针;
char *gender 要匹配的性别
STUDENT **student 传递二级指针,用于接收指向的变长数组
函数的实现:
如何获取分配内存区的长度?
up查了很多资料,发现两个办法,第一个是分配内存时,会在分配的内存的前4字节储存长度,实际操作一番发现并不是,可能是编译器不支持;第二是使用编译器自带的函数
size_t _msize(void *_Memory) 获取获取分配的长度( up 使用版本 gcc version 8.1.0 (x86_64-posix-seh-rev0 by MinGW-W64 project) )。函数内部还可以继续完善,up主已将函数重写了三遍,实在没精力了。
最后是代码的全貌:
up能力有限,若出现错误,欢迎在评论区指正。

