1.为什么要有动态内存分配
2.malloc和free
3.calloc
4.realloc
5.笔试题
6.总结c/c++中程序内存区域划分
1.为什么要有动态内存分配
为了调整申请的空间大小,使程序员可以申请和释放空间,提高程序的灵活性
2.malloc和free
作用:分配一块连续可用的空间
头文件:stdlib.h
参数:类型是size_t,size是预分配大小(单位是字节)
返回值:类型是void*,若开辟失败,返回NULL;若开辟成功,返回这块空间(malloc开辟的空间)的首指针。
用法:开辟空间后,我们要保留这块空间的首指针,但malloc的返回值类型是void*,保留时要注意强制类型转换。最后,当开辟的空间不再用时,要用free函数释放空间,否则该空间将被一直占用,直到程序结束时才会被操作系统收回。
作用:释放动态开辟的内存。
头文件:stdlib.h
参数:类型是void*,参数是需解除分配的内存块;参数可以是NULL,此时函数什么也不做
返回值:无
上述代码看似没有问题,但却有个大问题。
如果malloc开辟空间失败呢?
返回NULL,而且我们还对NULL解引用了,这是非常可怕的,所以,修改如下:
这样才合适。
还有一个问题,释放pa指向的空间后,pa和p就是野指针了,要置NULL才合适
3.calloc
作用:开辟num个size字节的空间,并将每个开辟的字节初始化为0
头文件:stdlib.h
参数:类型都是size_t,num是开辟的元素个数,size是为每个元素分配的大小
返回值:和malloc一致
特点:与malloc区别不大,但malloc是不会对开辟空间初始化的,但calloc会。
4.realloc
作用:调整动态开辟的空间大小
头文件:stdlib.h
参数1:类型是void*,参数1是需调整的动态开辟的空间
参数2:类型是size_t,参数2是调整后的空间大小
参数1可以是NULL,此时将开辟一块size字节的空间,并返回首指针
返回值:类型是void*,若开辟失败,返回NULL;若开辟成功,返回调整后空间的首指针
注意:开辟后的空间未必是在原来的位置!所以要保留realloc的返回值才比较合适
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
int main()
{int* p = (int*)malloc(16);//开辟空间,保留首指针if (p == NULL){exit(-1);}int* pa = p;p = (int*)realloc(pa, 32);if (p == NULL){exit(-1);}pa = p;free(pa);//回收空间pa = NULL;//防野指针p = NULL;return 0;
}
5.笔试题
题目1
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void getmemory(char* p)
{p = (char*)malloc(100);
}
void test()
{char* str = NULL;getmemory(str);strcpy(str, "helo world");printf(str);
}
int main()
{test();return 0;
}
其实吧,并不会打印hello world,str是空指针,将指针变量传给getmemory不会改变指针变量,形参只是实参的一份临时拷贝,改变形参不会改变实参。
应将指针变量的地址传上去,然后解引用才能改变指针变量str
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void getmemory(char** p)
{*p = (char*)malloc(100);
}
void test()
{char* str = NULL;getmemory(&str);strcpy(str, "helo world");printf(str);
}
int main()
{test();return 0;
}
这样就没问题了
题目2
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char* getmemory()
{char p[] = "hello world";return p;
}
void test()
{char* str = NULL;str = getmemory();printf(str);
}
int main()
{test();return 0;
}
看起来没什么问题,因为字符串常量是存储在代码段的,不是栈区,即使出了函数也不会销毁,char p【】等价于char* p, 而p是指向字符串常量的指针,出函数后,p就被销毁了,但是变量p里面装的指针(也就是字符串常量的首指针)被返回了,应该是可以打印的。
但是吧,有个误区(上面红字是错的),我们忽略了字符数组初始化的特殊性,本代码的实质是将字符串拷贝一份,放到字符数组中,而这个字符数组是在栈区的,p是字符数组的首指针。出了函数,p指向的空间就被回收了,所以不能打印。
但是,我们只要稍稍修改:
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char* getmemory()
{const char* p = "hello world";return (char*)p;
}
void test()
{char* str = NULL;str = getmemory();printf(str);
}
int main()
{test();return 0;
}
这个代码是可以打印的,因为p不再是数组名了,字符数组的特殊初始化没有了,p就是字符串常量的首指针!
题目3
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void getmemory(char** p,int num)
{*p = (char*)malloc(num);
}
void test()
{char* str = NULL;getmemory(&str, 100);strcpy(str, "hello");printf(str);
}
int main()
{test();return 0;
}
这个呀,没什么问题,是对的。因为我们取出str的地址,可以通过解引用的方式改变str。另外,在函数里用malloc申请内存,也是在堆区申请,函数结束对堆区的空间不影响。
6.总结c/c++中程序内存区域划分
堆区:动态开辟内存是在堆区上开辟的
栈区:声明变量,调用函数是在栈区申请内存
代码段:可执行代码,只读常量(例如字符串常量)
数据段(静态区):全局变量,静态变量储存在静态区