目录
导读:
一、动态内存的优点
二、动态内存的建立与分配
1. 用malloc函数开辟动态存储区
2. free函数释放动态存储区
3. 实际操作
4. calloc 函数开辟动态存储区
4.1 calloc基本概念
4.2 实际操作
5. realloc函数重新分配动态存储
5.1 realloc基本概念
5.2 实际操作
三、常见的动态内存错误
1. 对NULL指针的解引用操作
2. 对动态开辟空间的越界访问
四、深层次思考
1. 代码:
思考:
错误修改:
2. 修改
动态内存管理是在程序运行时动态地分配和释放内存空间,可以让程序更加灵活和高效。
本章节会对malloc、calloc、realloc以及内存的释放——free等进行详细介绍
也会解答一些容易思考错误的题目
灵活性:动态内存管理使程序能够根据需要动态地分配和释放内存,从而提高程序的灵活性和可扩展性。
内存资源利用率:动态内存管理可以优化内存使用,避免浪费内存资源。
可靠性:动态内存管理可以减少内存泄漏和内存溢出等问题,从而提高程序的稳定性和可靠性。
程序性能:动态内存管理可以提高程序的性能,因为程序可以在需要时分配内存,而不是在程序执行时一次性分配所有内存。
多任务处理:动态内存管理可以使多任务处理更加容易,因为程序可以在运行时动态地分配和释放内存,从而避免多个任务之间的内存冲突。
对内存的动态分配是通过系统提供的库函数来实现的,主要有malloc,calloc,free,realloc这4个函数。
void* malloc (size_t size);
其作用是在内存的动态存储区扽配一个长度为size的 连续空间。
此函数的值(“即返回值”)是所分配区域的第一个字节的地址。
或者说此函数是一个指针型函数,返回的指针指向该分配区域的第一个字节
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
- 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己 来决定。
- 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。
void free (void* ptr);
作用是释放动态分配的内存空间。在使用malloc、calloc等函数分配内存空间后,需要使用free函数释放已经使用完的内存空间,以便其他程序或系统使用其内存空间。
- 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数ptr是NULL指针,则函数什么事都不做。
注意:
使用free函数释放已经释放的内存空间会导致不可预知的错误,如内存泄漏、内存破坏等问题。
因此,在使用free函数释放动态分配的内存空间时,需要慎重考虑,确保操作正确无误。
int main()
{
int i, n;
char* buffer;
printf("How long do you want the string? \n");
scanf("%d", &i);
buffer = (char*)malloc(i + 1);
if (buffer == NULL)//判断buffer指针是否为空
{
exit(1);//如果为空则退出
}
for (n = 0; n < i; n++)
{
buffer[n] = rand() % 26 + 'a';//随机生成
}
buffer[i] = '\0';
printf("Random string: %s\n", buffer);
free(buffer);//释放内存
return 0;
}
生成一个长度由用户指定的字符串,并用字母字符填充。此字符串的可能长度仅受malloc可用内存量的限制。
运行结果:
void* calloc (size_t num, size_t size);
calloc函数是C语言中的一个动态内存分配函数之一,它可以动态地分配内存并初始化为0。
num是要分配的元素个数,size是每个元素的字节数。它会在内存中分配num * size字节的连续空间
- 如果分配成功,该函数返回一个指向分配的内存块的指针
- 如果分配失败,则返回 NULL
与 malloc
函数不同的是,calloc
函数会在内存分配时清零,因此在某些情况下会比 malloc
更加安全,尤其是在处理字符串等需要清零的数据时。
但是,由于 calloc
会在分配内存时清零,因此在内存空间较大的情况下,使用 calloc
会比 malloc
的效率低一些。
int main()
{
int i, n;
int* pData;
printf("要输入的数字数量: \n");
scanf("%d", &i);
pData = (int*)calloc(i, sizeof(int));
if (pData == NULL)
{
exit(1);
}
for (n = 0; n < i; n++)
{
printf("输入数字 #%d: ", n + 1);
scanf("%d", &pData[n]);
}
printf("您已输入: ");
for (n = 0; n < i; n++)
{
printf("%d ", pData[n]);
}
free(pData);
return 0;
}
这个程序只是存储数字,然后打印出来。但每次执行程序时,它存储的项目数量都可以调整,因为它在运行时分配了所需的内存。
void* realloc (void* ptr, size_t size);
realloc函数在C语言中用于重新分配已经存在的内存空间的大小
ptr
表示要重新分配大小的内存指针,size
表示重新分配的内存大小
- 如果分配成功会返回一个指针,指向重新分配后的内存空间
- 如果分配不成功,则返回NULL
- 如果重新分配的大小比原来的大,则增加的内存空间会保留原来的值
- 如果重新分配的大小比原来的小,则会将多余的部分释放掉
注意:
使用realloc函数重新分配内存时,如果原来的内存空间存在指针或者其他相关的数据,需要在重新分配内存之前进行拷贝或者修改,以免出现内存覆盖的问题。
realloc在调整内存空间的是存在两种情况:
程序会提示用户输入数字,直到输入一个零字符。每次引入新值时,数字指向的内存块都会增加int的大小
int main()
{
int input, n;
int count = 0;
int* numbers = NULL;
int* more_numbers = NULL;
do {
printf("输入整数值(0结束): ");
scanf("%d", &input);
count++;
more_numbers = (int*)realloc(numbers, count * sizeof(int));
if (more_numbers != NULL)
{
numbers = more_numbers;
numbers[count - 1] = input;
}
else
{
free(numbers);
puts("错误(重新)分配内存");
exit(1);
}
} while (input != 0);
printf("输入的数字: ");
for (n = 0; n < count; n++)
{
printf("%d ", numbers[n]);
}
free(numbers);
return 0;
}
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
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;//当i是10的时候越界访问
}
free(p);
}
请问下面代码中,运行Test 函数会有什么样的结果?
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
int main()
{
Test();
return 0;
}
在我们的猜想中,返还的应该是hello world,结果却是程序崩溃
在这里*p指向的并不是我们认为的str的地址,str是指针变量,传递的是空指针
*p指向的还是NULL,所以malloc函数开辟的空间给了NULL,程序对NULL解引用操作,从而导致程序崩溃
博主过了两天发现上面的讲解是有问题的,程序崩溃的问题并不是如上述所说的那样,我们再来看一次代码
我们在把 str 传给GetMemory这个函数,并用字符指针 p 来接收,p指向的也是NULL,然后接下来往后看,malloc开辟一块内存空间,p来接收,这时p指向的是malloc开辟空间的这个地址,而不是NULL
这时我们返回到test函数,malloc这块空间并没有被释放,p出了GetMemory这个函数,进入到test函数时就已经被销毁了,这时我们已经找不到malloc所开辟空间的地址
再到strcpy这里,str还是一个空指针,而要对str塞入内容,就要对NULL解引用操作,所以才导致程序崩溃。
void GetMemory(char** p)
{
*p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str);
strcpy(str, "hello world");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
想要扩大str的地址,只需要把str的地址传参过去,也就是&str,用二级指针 **p 来接收 str 的地址,相对的扩大的自然是str的内存空间