为什么存在动态内存管理?动态内存管理函数有哪些?常见的动态内存错误?什么是柔性数组?几个经典的笔试题?
问题一:为什么存在动态内存管理?
平时我们定义的变量、数组等都在栈上开辟空间,这里的局限性是,空间开辟大小固定,尤其数组还要指定长度,所需内存在编译时分配;但是,有时候我们需要的空间大小在程序运行时才能知道,因此我们需要动态内存开辟。
问题二:动态内存管理函数有哪些?
1.malloc和free
C语言提供的动态内存开辟函数:malloc
函数原型:void* malloc(sizt_t size) 它向内存申请一块连续可用的空间,返回指向这块空间的指针。
注意事项:
(1)开辟成功,返回一个指向开辟好空间的指针
(2)开辟失败,返回一个NULL指针,所以一定要检查函数的返回值
(3)返回值类型是一个void*的指针,所以说malloc函数并不知道开辟空间的类型,这个由使用者自己来决定。
(4)开辟参数大小如果为0,malloc行为未定义,取决于编译器。
C语言提供用来做动态内存的释放与回收函数:free
函数原型:void* free(void* ptr)
注意事项:
(1)如果参数ptr指向的空间不是动态开辟的,那么free函数的行为也是未定义的。
(2)如果参数ptr是NULL,则函数什么都不做
另外,malloc与free声明都在stdlib.h中。
2.calloc
C语言还提供了一个动态内存分配函数:calloc
函数原型:void*calloc(size_t num, size_t size) 它申请num个大小为size的空间,并且把空间的每个字节初始化为0.
注意事项:
(1)它与malloc函数区别在于:calloc函数会将内存初始化为0,参数不一样。
3.realloc
C语言提供了一个内存调整函数:realloc
函数原型:void*realloc(void* ptr, size_t size) 它是将ptr指向的内存大小调整为size,然后返回调整之后的起始地址。
注意事项:
(1)如果原有的内存空间之后又足够大的空间,那么直接扩展大小,原数据保留。
(2)如果原内存空间之后没有足够大空间供我们调整,那么在堆空间上另找一个合适大小的连续空间,并且将原数据拷过去,并且返回这块新地址。
问题三:常见的动态内存错误?
1. 对NULL指针解引用
2. 对动态开辟的空间越界访问
3. 对不是动态开辟的内存使用free来释放
4. 使用free释放一块动态开辟内存的一部分
5. 对同一块内存多次释放
6. 动态开辟的内存忘记释放(导致内存泄漏)
问题四:什么是柔性数组?
一个结构体中的最后一个元素允许是位置大小的数组,这个数组就叫做柔性数组。
例如:
1 typedef struct std
{
int i;
int a[];//柔性数组,也可以写成int a[0];
}type_a;
注意事项:
(1)结构体的柔性数组成员前面至少有一个其他成员
(2)sizeof返回的这种结构大小不包括柔性数组的内存
(3)包含柔性数组成员的结构用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
(4)可以一次为结构体分配大小(包括柔性数组),也可以将结构体育柔性数组分开动态申请,第一种方便内存释放,因为只需要释放一次,第二种方便访问(但是建议第一种)。
问题五:几个经典的笔试题?
以下4个案例运行分别会有什么样的结果,怎么改正?
第一个问题:
函数没有返回值,出了函数其栈帧消失,相当于往一个空指针里面拷数据,则会导致段错误。
改法:
6 char* GetMemory(char* p)
7 {
8 p=(char*)malloc(100);
9 return p;
10 }
11
12 void Test(void)
13 {
14 char* str=NULL;
15 char* p=NULL;
16 p=GetMemory(str);
17 strcpy(p, "hello world\n");
18 printf(p);
19 }