在C语言中我们在栈上开辟的空间是固定的,一旦确定好大小就不能随意改变,就想你创建了
动态内存函数
malloc
由于可能会返回NULL,一旦被解引用就是野指针的非法访问。
所以我们要对返回的指针做检查
下面我们来实践一下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(sizeof(int) * 5);
if (p == NULL)
{
perror("malloc");
return 1;
}
// 使用
return 0;
}
free
介绍完malloc 函数后,我们来介绍一下如何释放空间,由于堆的空间是有限的,如果一直开辟不去释放空间,那就会一直占用堆上的空间,导致重新运行时内存泄漏。所以我们需要学会释放空间!
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(sizeof(int) * 5);
if (p == NULL)
{
perror("malloc");
return 1;
}
// 使用
free(p);
p = NULL;
return 0;
}
calloc
这个函数也是申请 num 个 size 大小的字节空间,唯一和malloc不同的是,calloc会将空间全部初始化为0
realloc
这个函数就是扩大空间的,如果你觉得malloc或者calloc开辟的空间小了,就传入一个指针,输入你想要扩大的字节数,就可以了。
这里的开辟方式分两种:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)calloc(5, sizeof(int));
if (p == NULL)
{
perror("calloc");
return 1;
}
// 使用
int* ptr = (int*)realloc(p, 50);
if (ptr == NULL)
{
perror("realloc");
return 1;
}
p = ptr;
//使用
free(p);
p = NULL;
return 0;
}
练习
题目1
void test()
{
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;
free(p);
}
如果p的值是NULL,就是对空指针解引用,发生错误
题目2
void test()
{
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;
}
free(p);
}
当i是10的时候就会越界访问
题目3
void test()
{
int a = 10;
int* p = &a;
free(p);
}
对非动态内存开辟的空间不能使用free
题目4
void test()
{
int* p = (int*)malloc(100);
p++;
free(p);
}
由于p已经不是原先的起始位置,释放的空间不彻底,导致内存泄漏
题目5
void test()
{
int* p = (int*)malloc(100);
free(p);
free(p);
}
对一块动态开辟的内存多次释放是错误的,在第一次释放的时候,空间以及还给操作系统了,free是无权访问操作系统的空间,所以在使用free后,我们可以将指针置空,避免多次释放。
题目6
void test()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while (1);
}
这个就是忘记对开辟的内存进行释放,导致内存泄漏。
笔试题
题目1
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
首先str还是NULL,因为形参的改变不会影响实参,如果要改变str需要传入str的地址,对NULL解引用程序发生崩溃,并且对于开辟的内存空间要记得释放
题目2
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
临时变量p一旦出了GetMemory 函数就被销毁了,str非法访问内存空间。
题目3
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
忘记释放内存,发生内存泄漏
题目4
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
free已经释放了str所指向的内存空间,但是free不会将str置空,后面的代码段内存空间进行了非法访问!
柔性数组
柔性数组就是数组的大小是未定义的,这种一般出现在结构体上,如果不知道结构体的大小的话,可以看我上上篇的文章《结构体详解》。
柔性数组的创建:
struct S
{
int i;
char arr[];
};
特点
使用
当我们要使用结构体是,就需要对这个柔性数组开辟空间:
#include <stdio.h>
#include <stdlib.h>
struct S
{
int i;
char arr[];
};
int main()
{
struct S* s1;
s1 = (struct S*)malloc(sizeof(int) + 10 * sizeof(char));
return 0;
}
这样子我们就获得了10个字节的arr数组的空间了。
优点
我们在学习链表的时候会定义下面的结构体:
#include <stdio.h>
#include <stdlib.h>
struct S
{
int data;
struct S* next;
};
int main()
{
struct S* p;
p = (struct S*)malloc(sizeof(struct S));
if (p == NULL)
{
perror("malloc p");
return 1;
}
p->next = (struct S*)malloc(sizeof(struct S));
if (p->next == NULL)
{
perror("malloc next");
return 1;
}
//使用
free(p->next);
p->next = NULL;
free(p);
p = NULL;
return 0;
}