1.为什么存在动态内存分配
首先我们已经掌握的内存开辟方式有:定义变量、数组等。其空间都是在栈上,栈上的空间有以下几个特征:
- 开辟的空间大小是固定的
- 数组在声明的时候,必须指定数组的大小,这会影响内存分配给数组的空间
当我们学习实用动态内存之后,就可以不考虑上面的情况。
2.动态内存函数介绍
2.1 malloc
这是 C 语言提供的一个动态内存开辟的函数,其函数声明为:
void* malloc (size_t size);
这个函数向内存申请一块连续并且可用的空间,并返回此空间的地址(指针)。
那么 malloc 函数有以下几个规则:
- 如果开辟成功,则返回一个开辟好的空间的指针(无类型指针)
- 如果开辟失败,则返回一个空指针
- 返回值(指针)的类型是 void* ,所以使用 malloc 开辟空间时要配合强制类型转换
- 如果参数 size 为 0 ,此时的 malloc 的行为标准是未定义的,具体取决于编译器
接下来我们来使用 malloc 函数:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int* arr = NULL;
arr = (int*)malloc(40);//arr 指针指向一块 40 字节的空间地址
if (arr == NULL)//如果是返回值是空指针
{
printf("%s\n", strerror(errno));//搭配 string 类函数
return 1;
}
for (int i = 0; i < 10; i++)
{
arr[i] = i;
printf("%d ", arr[i]);
}
return 0;
}
2.2 calloc
此函数也是 C 语言提供的动态内存函数,原型如下:
void* calloc (size_t num,size_t size);
参数的意义为:开辟 num 个大小为 size 的空间。
此函数与 malloc 的区别在于,会在返回空间地址之前,把空间的每个字节都初始化为 0 。
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int* arr = NULL;
arr = (int*)calloc(10, sizeof(int));
if (arr == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
2.3 realloc
realloc 函数让动态内存管理更加灵活。有时候我们会觉得使用 malloc 或 calloc 申请的空间小了或者大了,那么 realloc 就能对申请的空间进行重新调整。其函数原型如下:
void* realloc (void* ptr,size_t size);
其参数以及返回值的意义为:
- ptr 是要调整的内存地址
- size 是调整之后的空间大小(注意是调整之后的空间大小)
- 返回值为调整之后的空间地址
realloc 对重新开辟的空间有两种做法:
?
- 原有空间之后有足够大的空间,那么扩展的空间会直接追加上去
- 原有空间之后没有足够大的空间,那么 realloc 就会在堆区上重新找一块合适的连续空间
那么对于 realloc 的使用也非常简单:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int* arr = NULL;
arr = (int*)calloc(5, sizeof(int));
if (arr == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
int* tmp = (int*)realloc(arr, 10*sizeof(int));//现在要调整为 10 个字节的大小
if (tmp == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
else
arr = tmp;
return 0;
}
2.4 free
其实,在没有介绍 free 之前,上面的代码都是"错误"的。free 也是 C 语言提供的函数,是专门用来做动态内存的释放和回收的。其函数原型如下:
void free(void* ptr);
free 函数有以下几个规则:
- 如果参数 prt 指向的空间不是动态开辟的,那 free 函数的行为是未定义的
- 如果参数 ptr 是空指针,那么函数什么都不做
我们补全上面的代码:
malloc:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int* arr = NULL;
arr = (int*)malloc(40);//arr 指针指向一块 40 字节的空间地址
if (arr == NULL)//如果是返回值是空指针
{
printf("%s\n", strerror(errno));//搭配 string 类函数
return 1;
}
for (int i = 0; i < 10; i++)
{
arr[i] = i;
printf("%d ", arr[i]);
}
free(arr);
arr = NULL;
return 0;
}
realloc:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int* arr = NULL;
arr = (int*)calloc(5, sizeof(int));
if (arr == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
int* tmp = (int*)realloc(arr, 10*sizeof(int));//现在要调整为 10 个字节的大小
if (tmp == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
else
arr = tmp;
free(arr);
arr = NULL;
return 0;
}
calloc:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main()
{
int* arr = NULL;
arr = (int*)calloc(10, sizeof(int));
if (arr == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
free(arr);
arr = NULL;
return 0;
}
?3. 常见的动态内存错误
3.1 对空指针的解引用操作
#include <stdlib.h>
void test()
{
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
int main()
{
test();
return 0;
}
?
?3.2 对动态开辟空间的越界访问
#include <stdlib.h>
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);
}
int main()
{
test();
return 0;
}
这种情况编译器是不会报错的,因为我们在使用数组时经常存在这个问题。但是在运行时会发生错误。
?
3.3 对非动态开辟的空间使用 free 释放
?
#include <stdlib.h>
void test()
{
int a = 10;
int* p = &a;
free(p);//ok?
}
int main()
{
test();
return 0;
}
?
这种情况也会出现运行错误。
?
3.4? 使用 free 释放动态内存的一部分
#include <stdlib.h>
void test()
{
int* p = (int*)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
int main()
{
test();
return 0;
}
?3.5 对用一块内存多次释放
?
#include <stdlib.h>
void test()
{
int* p = (int*)malloc(100);
free(p);
free(p);//重复释放
}
int main()
{
test();
return 0;
}
?
3.6??动态开辟内存忘记释放
这种情况就像我们上面未介绍 free 之前时一样。
#include <stdlib.h>
void test()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while (1);
}
虽然编译器不会报错,运行也不会报错,但是不释放内存是不可取的坏习惯。
?4. 经典笔试题
4.1 题目一
#include <stdio.h>
#include <stdlib.h>
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;
}
?
4.2 题目二
#include <stdio.h>
#include <stdlib.h>
char* GetMemory(void) {
char p[] = "hello world";
return p;
}
void Test(void) {
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
?4.3 题目三
#include <stdio.h>
#include <stdlib.h>
void GetMemory(char** p, int num) {
*p = (char*)malloc(num);
}
void Test(void) {
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
int main()
{
Test();
return 0;
}
?
4.4 题目四
#include <stdio.h>
#include <stdlib.h>
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
?
?
?
|