C语言动态内存管理的经典笔试题柔性数组

发布时间:2026/7/31 18:10:49

C语言动态内存管理的经典笔试题柔性数组 目录一、动态内存管理的经典笔试题分析题目1题目2题目3题目4二、柔性数组1.什么是柔性数组2.柔性数组的特点3.柔性数组的使用一、动态内存管理的经典笔试题分析题目1#includestdio.h#includestdlib.hvoidGetMemory(char*p){p(char*)malloc(100);}voidTest(void){char*strNULL;GetMemory(str);strcpy(str,hello world);printf(str);}intmain(){Test();return0;}请问上面调用Test函数的运行结果是什么第一个是程序会崩溃为什么会崩溃呢先分析一下上面的代码。首先在Test函数里面创建了一个char*类型的指针变量str。其次又调用了GetMemory这个函数将str作为参数传给了GetMemory这个函数里面动态的申请了100个字节的空间调用完GetMemory这个函数回到Test函数里调用strcpy函数想将hello world这个字符串拷贝放到str所指向的内存空间里然后再通过printf函数打印str所指向空间里的内容到这里Test函数就调用完毕了。其次这个代码还存在的问题是对于malloc动态申请的内存没有释放。我们知道对于函数的传参分为两种传值和传地址。上面给函数GetMemory传递的是指针str是值传参而对于值传参来说形参是实参的一份临时拷贝。所以对形参的修改不会影响到实参。当调用GetMemory动态申请100个字节的空间以后出了这个函数局部指针变量p就被销毁了但是对于malloc动态申请的空间没有被释放那就一直占用着内存。这就是上面代码的其中一个问题又因为这里是值传递所以str的值并没有被修改str的值还是空指针(NULL)那调用strcpy函数想将字符串hello world拷贝放到str所指向的空间里就不可以了所以这里对NULL指针的解引用就导致了程序崩溃。上面的代码无非就是想动态的申请一块空间来存放一个字符串那要怎么修改上面的代码让这个程序正确的运行呢其实我们就是想修改指针变量str的值嘛让str成功的指向动态申请的空间的起始地址而已那就是想形参的修改能影响到实参那就采用传地址的形式就可以啦。对一级指针str取地址就要用一个二级指针来存放这个一级指针的地址。我们就要对GetMemory函数的定义进行修改#includestdio.h#includestdlib.hvoidGetMemory(char**p){*p(char*)malloc(100);}voidTest(void){char*strNULL;GetMemory(str);strcpy(str,hello world);printf(str);free(str);strNULL;}intmain(){Test();return0;}程序运行的结果由上图可知程序成功运行而且我们也不能忘了对动态申请的空间进行释放题目2#includestdio.hchar*GetMemory(void){charp[]hello world;returnp;}voidTest(void){char*strNULL;strGetMemory();printf(str);}intmain(){Test();return0;}程序运行结果首先分析一下为什么打印的结果不是hello world? 这里首先调用了Test函数在函数里创建了一个char*类型的指针变量str并赋值为NULL其次又在函数里调用了GetMemory函数在GetMemory函数里创建了一个字符数组p数组中存放的是hello world这个字符串。并将这个数组的首元素地址作为返回值赋给了指针str最后打印指针str所指向空间的内容。这段代码的错误在于在调用完GetMemory函数后在这个函数里创建的数组就销毁了空间会还给操作系统但是返回了这块空间的地址那用str接收了这块被销毁空间的地址再使用指针str去访问这块被销毁的空间就会出现野指针非法访问空间。这就是上面代码为什么打印出来乱码的原因。这种错误属于返回栈空间地址的问题。所以返回局部变量(或数组)的地址是不行的很能会造成非法访问。题目3#includestdio.hvoidGetMemory(char**p,intnum){*p(char*)malloc(num);}voidTest(void){char*strNULL;GetMemory(str,100);strcpy(str,hello);printf(str);}intmain(){Test();return0;}上面这段代码跟题目1的改正很相似乍一看好像这段代码没什么问题。但是有问题的这段代码唯一的问题就在于没有对动态申请的空间进行释放。这也是常见的动态内存管理中的错误。对动态申请的空间一定要记得释放题目4#includestdio.hvoidTest(void){char*str(char*)malloc(100);strcpy(str,hello);free(str);if(str!NULL){strcpy(str,world);printf(str);}}intmain(){Test();return0;}上面这段代码又存在什么问题呢首先调用函数Test在函数里动态的开辟了100个字节的空间并将这块空间的起始地址赋给了char*类型的指针变量str紧接着调用strcpy函数将字符串hello拷进这块空间里然后释放了这块动态开辟的空间。紧接着判断str是不是空指针不是就再一次调用strcpy函数将字符串world拷贝放进指针str所指向的空间里然后打印str所指向空间里的内容。上面这段代码存在的问题在于在动态开辟了100个字节的空间后就直接使用指针str了没有判断malloc是否开辟空间成功其次是在释放了这块动态开辟的空间后这块空间就还给了操作系统但是str还指向着这块空间也就是str并不是空指针紧接着又用这个str去访问这块被释放的空间就造成了非法访问空间。所以在上面的代码中第一次调用strcpy函数将hello这个字符串存进动态开辟的空间里在释放了这块空间后又调用strcpy函数又将world这个字符串非法的存进了这块已释放的空间中即world覆盖了那块被释放空间的内容打印出来的结果就是world改正这段代码就是要在释放动态开辟的空间后将指向这块空间的指针也置为空以免后面又使用这个指针非法访问空间。str置为空以后那后面的if语句也就没有了作用。二、柔性数组1.什么是柔性数组1.柔性数组(flexible array)也称为变长数组是一种动态数组的实现方式。2.与普通数组不同的是柔性数组在定义时不需要明确指定数组大小在程序运行时可以动态地分配和扩展数组大小。[引用]3.在c99版本中结构体中的最后一个成员允许是未知大小的数组这个数组就叫做『柔性数组』成员。例如structtag_name1{charc;intarr[];//柔性数组};structtag_name2{inti;charc;shortarr[0];//柔性数组};上面两个结构体中的最后一个成员都是柔性数组第二个结构体的最后一个数组成员的[ ]中是0意思是他没有指定元素个数也就没有指定数组大小也是柔性数组。上面两种形式的柔性数组在不同的编译器上可能会支持不同的形式。2.柔性数组的特点■结构体中的柔性数组成员前面必须至少有一个其他成员。■sizeof返回的这种结构体大小不会包括柔性数组的内存。■包含柔性数组成员的结构体要用malloc()函数进行内存的动态分配并且分配的内存应该大于结构体的大小以适应柔性数组的预期大小。对于柔性数组在结构体中必须是最后一个成员而且在柔性数组的前面最少还要有一个其他的结构体成员这是柔性数组的必须条件。其次由于柔性数组没有指定大小所以在计算含有柔性数组的结构体大小时是不会包含柔性数组成员的#includestdio.hstructtag_name{inti;intarr[];//柔性数组};intmain(){printf(%zd\n,sizeof(structtag_name));return0;}程序运行结果3.柔性数组的使用对于柔性数组的最后一个特点也是最重要的一点包含柔性数组的结构体要用malloc函数来进行动态内存分配并且分配的空间大小应该要大于结构体的大小也就是通过动态内存分配多出来的空间就是给这个柔性数组的。看下面一段代码#includestdio.h#includestdlib.hstructS{inti;intarr[];//柔性数组};intmain(){structS*p(structS*)malloc(sizeof(structS)5*sizeof(int));if(pNULL){perror(malloc);return1;}p-i10;intj0;for(j0;j5;j){p-arr[j]j;}//打印结构体中的内容printf(%d ,p-i);for(j0;j5;j){printf(%d ,p-arr[j]);}free(p);pNULL;return0;}程序运行结果上面使用malloc来为结构体动态的开辟空间由上图蓝色框里计算的就是结构体的大小它并不包含柔性数组而后面的绿色框里的大小就是为柔性数组开辟的空间两部分加起来才是我们需要开辟的总空间。我们还学过一个动态内存管理的函数叫realloc这个函数可以对已经动态开辟的空间大小进行调整以适应我们的需求这样对于柔性数组的大小就可以更改柔性的含义也就体现在这里。所以为什么要把柔性数组放在结构体的最后一个成员处就为了放便对这个数组的大小进行调整而不影响到前面成员在结构体中的存储。由上图如果我们要调整刚刚动态开辟的空间就用realloc函数调整蓝色框里还是结构体的大小橙色框里就是更改以后的柔性数组的新大小。两部分加起来才是调整以后的总大小。当然只要是动态开辟的空间就不要忘记对其返回值进行检查以免动态开辟空间失败最后也是不要忘记用完这块空间后要用free释放。当然还有一种形式也可以达到跟上面柔性数组一样的效果。就是我们在设计结构体时最后一个成员我们不放柔性数组我们改成指针变量我们可以先为这个结构体动态的开辟一块空间然后再动态开辟一块空间将这个空间的起始地址赋给结构体的最后一个成员也就是赋值给arr这个指针变量。后面如果要更改空间大小那就直接改arr指针所指向的空间即可。这样也能达到柔性的特点#includestdio.h#includestdlib.hstructS{intn;int*arr;};intmain(){structS*ps(structS*)malloc(sizeof(structS));if(psNULL){return1;}ps-arr(int*)malloc(5*sizeof(int));if(ps-arrNULL){return1;}ps-n10;inti0;//使用空间for(i0;i5;i){ps-arr[i]i;}//调整空间大小int*ptr(int*)realloc(ps-arr,10*sizeof(int));if(ptr!NULL){ps-arrptr;}//使用空间……//释放空间free(ps-arr);ps-arrNULL;free(ps);psNULL;return0;}这种形式的结构体也能达到柔性的特点让结构体的最后一个成员是指针变量让这个指针也指向一块动态开辟的空间到后面通过更改这个指针所指向的空间大小即可。当然要记得检查指针是否为空后面使用完空间也要记得释放空间。这里释放空间是要先释放arr所指向的空间再释放ps所指向的空间顺序不能乱。因为ps指针指向的结构体中包含了arr指针如果先释放ps指向的空间那这个结构体的空间就被销毁了包括存放的指针变量arr那后面就找不到arr这个指针了更找不到arr所指向的空间。所以顺序不能乱。上面两种方式一种是结构体中包含柔性数组另一种是结构体中包含指针。那这两种方式哪种更好呢答案是第一种。因为第二种方式动态的申请了两次空间在后面就可能会忘记要进行两次内存释放。柔性数组的优点第一个好处是方便内存释放如果我们的代码是在一个给别人用的函数中你在里面做了二次内存分配并把整个结构体返回给用户。用户调用free可以释放结构体但是用户并不知道这个结构体内的成员也需要free所以你不能指望用户来发现这个事。所以如果我们把结构体的内存以及其成员需要的内存一次性分配好了并返回给用户一个结构体指针用户做一次free就可以把所有的内存给释放掉。第二个好处是这样有利于访问速度连续的内存有益于提高访问速度也有益于减少内存碎片。对于上面说的内存碎片有两种类型外部碎片和内部碎片。① 外部碎片是指由于动态内存分配和释放过程中导致剩余的未分配内存块被零散占据无法满足大块内存的需求。虽然总的空闲内存足够但无法分配连续的内存空间。② 内部碎片是指已经分配给进程的内存块中存在着未被充分利用的空间。例如当为一个固定大小的数据结构分配内存但实际使用的空间小于分配的大小时就会产生内部碎片。[引用]结束啦

相关新闻