目录
原本的内存开辟方式有:
int val = 20 ; 在栈空间上开辟四个字节char arr [ 10 ] = { 0 }; 在栈空间上开辟 10 个字节的连续空间
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道, 那数组的编译时开辟空间的方式就不能满足了。 这时候就只能试试动态存开辟了。
void* malloc ( size_t size );
C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:
void free ( void* ptr );
-
- int arr[10] = {0}; // 默认这种开辟的空间是在栈区的,栈区用于存放临时变量
- int *p = (int *)malloc(40); // 动态内存开辟,开辟40个字节,返回为void *类型,动态开辟的是存放在 堆区
- if (p == NULL) // 开辟失败返回空指针
- {
- printf("%s\n", strerror(errno));
- return -1;
- }
- for (int i = 0; i < 10; i++)
- {
- *(p + i) = i;
- }
- for (int i = 0; i < 10; i++)
- {
- printf("%d", *(p + i)); // 0 1 2 3 4 5 6 7 8 9
- }
-
- free(p); // 先释放p指向的空间
- p = NULL; // 后此时p是空指针,再也找不到上面动态开辟的内存空间
C语言还提供了一个函数叫 calloc , calloc 函数也用来动态内存分配。原型如下:
void* calloc ( size_t num , size_t size );
- int *p = (int *)calloc(10, sizeof(int));
- if (p == NULL)
- {
- printf("%s\n", strerror(errno));
- return -1;
- }
- for (int i = 0; i < 10; i++)
- {
- printf("%d\n", *(p + i)); // 10个0
- }
- free(p); // 释放
- p = NULL; // 置空

所以如何我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务。
void* realloc ( void* ptr , size_t size );

当是情况 1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
当是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小 的连续空间来使用。这样函数返回的是一个新的内存地址。
- int *p = (int *)malloc(40);
- if (p == NULL)
- {
- printf("%s\n", strerror(errno));
- return -1;
- }
- for (int i = 0; i < 10; i++)
- {
- *(p + i) = i + 1; // 此时p指向的数组的值是 1~10
- }
- /*
- *realloc扩容:从原本40字节开辟至80字节
- 之所以不直接用p指针接收,是因为realloc可能扩容失败返回空指针,原本p指针还是好好地指向40个字节的数组,这下没扩容,反到给p赋值为了空指针
- */
- int *ptr = (int *)realloc(p, 80);
- if (ptr != NULL)
- {
- p = ptr;
- }
- for (int i = 0; i < 10; i++) // 验证
- {
- printf("%d ", *(p + i)); // 1~10
- }
- void test()
- {
- int *p = (int *)malloc(INT_MAX/4);
- *p = 20;//如果p的值是NULL,就会有问题
- free(p);
- }
- int i = 0;
- int *p = (int *)malloc(10*sizeof(int));
- if(NULL == p)
- {
- exit(EXIT_FAILURE);
- }
- for(i=0; i<=10; i++)
- {
- *(p+i) = i;//当i是10的时候越界访问
- }
- free(p);
- int a = 10;
- int *p = &a;
- free(p);//err
- int *p = (int *)malloc(100);
- p++;
- free(p);//p不再指向动态内存的起始位置
- int *p = (int *)malloc(100);
- free(p);
- free(p);//重复释放
- void GetMemory(char *p)
- {
- p = (char *)malloc(100);
- }
- void Test(void)
- {
- char *str = NULL;
- GetMemory(str);
- strcpy(str, "hello world");
- printf(str);
- }
因为:str空指针传入GetMemory函数后,形参相当于声明了一个临时p指针用于接收str指针变量,并开启了100字节的动态内存出函数后,临时变量p指向的空间会被销毁,而动态内存所开辟的内存空间依然还在,str依旧是空指针,这就造成后续 strcpy方法 将 "hello world"拷贝到str会崩溃(方法内部会对str进行解引用,NULL解引用会崩溃)
- void GetMemory(char **p) // 二级指针接收指针的地址
- {
- /* 解引用得到str指针变量,也就是对str所指向的空间动态开辟了100个字节的内存 */
- *p = (char *)malloc(100);
- }
- void Test(void)
- {
- char *str = NULL;
- GetMemory(&str); // 传递str指针变量的地址
- /* 此时str所指向的地址已经有了100个字节的空间,内部解引用,对str赋值字符串 hello world */
- strcpy(str, "hello world");
- printf(str); // hello world 打印方式等价于 printf("%s", str); 这种等价不可用于str[]的方式
- /* 释放动态内存 */
- free(str);
- str = NULL;
- }
- char *GetMemory(void)
- {
- char p[] = "hello world";
- return p;
- }
- void Test(void)
- {
- char *str = NULL;
- str = GetMemory();
- printf(str);
- }
因为p是临时指针变量,出函数,p销毁,所指向的空间被回收或者别占用,p所指向的地址已经不属于p了,当再去解引用,会有随机值。哦,当前都编译不通过 gcc
- void Test(void)
- {
- char *str = (char *) malloc(100);
- strcpy(str, "hello");
- free(str);
- if(str != NULL)
- {
- strcpy(str, "world");
- printf(str);
- }
- }
因为:free释放动态开辟的内存后,str所指向的空间已经被释放,但是str的地址还是原来的那个地址,并不是空指针进入if后,对不是自己的空间的地址解引用,str也就是野指针了,属于非法访问,程序崩溃
- void Test_three(void)
- {
- char *str = (char *)malloc(100);
- strcpy(str, "hello");
- free(str);
- str = NULL;
- if (str != NULL)
- {
- strcpy(str, "world");
- printf(str);
- }
- }