c语言_Day37_08_09
1、动态内存分配
内存空间分布:
- 栈:局部变量、函数形参
- 堆:动态内存分配
- 静态区:静态变量、全局变量
内存使用方式:
- 创建变量
- 创建数组
1、动态内存函数
1. malloc和free
malloc函数:用于开辟内存空间
参数:
size_t size 开辟内存的字节数
返回值:
void* 开辟内存空间的起始地址(成功)
空指针(失败)
int main()
{
int num = 10;
// 向内存申请一块存放10个整形的空间
int* p = (int*)malloc(num * sizeof(int));
// 开辟失败,打印错误信息
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
// 开辟成功,正常使用
else
{
for (int i = 0; i < num; i++)
{
*(p + i) = i;
printf("%d\n", *(p + i));
}
}
return 0;
}
free函数:释放动态内存空间
参数:
_Block 待释放的空间的指针
注:free函数仅用于释放归还空间,但是指针仍存在,故需要手动将指针赋值为空指针
int main()
{
int num = 0;
scanf("%d", &num);
int* p = (int*)malloc(num * sizeof(int));
system("cls");
if (p)
{
for (int i = 0; i < num; i++)
{
*(p + i) = i;
printf("%d\n", *(p + i));
}
}
else
{
printf("%s\n", strerror(errno));
}
free(p);
p = NULL;
return 0;
}
2. calloc
calloc函数:内存中创建一个数组并初始化为0
参数:
size_t num 数组元素个数
size_t size 每个元素的字节大小
返回值:
数组的首元素地址
int main()
{
int num = 20;
int* arr = calloc(num, sizeof(int));
if (arr)
{
for (int i = 0; i < num; i++)
{
printf("%d ", arr[i]);
}
}
else
{
printf("%s", strerror(errno));
}
printf("\n");
// 释放空间
free(arr);
arr = NULL;
return 0;
}
3. realloc
realloc:调整动态内存的大小
参数:
void* block 待修改的动态内存的指针
size_t size 新空间的大小
返回值:
void* 返回指向重新
void
分配的 (的指针,并且可能已移动) 内存块
realloc的风险:
realloc返回值的重新分配存在两种情况:
- 若新内存空间足够,则返回指向原内存空间的指针
- 若新内存空间不够(造成非法访问),则重新开辟新的空间并返回指向新空间的指针
- 若新内存超过全部内存大小,则返回空指针
int main()
{
int num = 40;
char* p = (char*)malloc(num * sizeof(char));
if (p)
{
for (int i = 0; i < num; i++)
{
*(p + i) = i;
printf("%d\n", *(p + i));
}
}
else
{
printf("%s\n", strerror(errno));
}
printf("=======================================\n");
// 修改内存空间大小
int new_num = 100;
char* p2 = (char*)realloc(p, new_num);
if (p2)
{
p = p2;
for (int i = 0; i < new_num; i++)
{
*(p + i) = i;
printf("%d\n", *(p + i));
}
}
else
{
printf("%s\n", strerror(errno));
}
free(p);
p = NULL;
return 0;
}
注:若返回新的地址,则realloc函数内部自动释放旧地址
若realloc的第一个参数传入空指针,则其功能等同于malloc
2、常见的动态内存错误
- 空指针操作
- 动态内存越界访问
- 使用free函数释放非动态内存的空间
- 使用free函数释放动态内存的一部分
int* p = (int*)malloc(10 * sizeof(int));
for (int i = 0; i < 10; i++)
{
*p++ = i;
}
free(p);
p = NULL;
上述代码中,p的指向已发生变化(不再指向动态内存空间的起始地址),故使用free函数释放内存后程序会崩溃
- 对同一块动态内存多次释放
注:为防止上述情况发生,一定要保证free释放内存后再将指针赋值为空指针
- 忘记释放动态内存空间(内存泄漏)
3、笔试题
- 问题代码
void GetMemory(char* p)
{
p = (char*)malloc(40);
}
void Test()
{
char* str = NULL;
GetMemory(str);
strcpy(str, "Hello, World!");
printf("%s\n", str);
}
int main()
{
Test();
}
问题:
- 值传递问题:动态内存的地址仅由p保存,函数结束后p销毁,动态内存地址丢失
- 动态内存未释放,导致内存泄漏
修改:
void GetMemory(char** p)
{
*p = (char*)malloc(20 * sizeof(char));
}
void Test()
{
char* str = NULL;
GetMemory(&str);
strcpy(str, "Hello, World!");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
- 问题代码
char* GetMemory()
{
char arr[] = "Hello";
return arr;
}
void Test()
{
char* str = NULL;
str = GetMemory();
printf("%s\n", str);
}
int main()
{
Test();
return 0;
}
str虽然指向返回值arr,但arr在函数调用结束后销毁,打印str会造成非法访问内存
注:返回栈空间的地址存在风险
修改:
char* GetMemory()
{
char* p = "Hello";
return p;
}
void Test()
{
char* str = NULL;
str = GetMemory();
printf("%s\n", str);
}
int main()
{
Test();
return 0;
}
返回栈空间时,函数调用结束栈空间释放;返回堆内存或静态区内存可避免上述问题
- 问题代码
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;
}
未释放内存,内存泄漏问题
- 问题代码
void Test()
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
释放动态内存后并未将指针指向空指针,导致非法访问内存
4、内存空间解析
- 栈:存放局部变量
int a;
int numArr[5];
char* p;
// ...
- 数据段(静态区):存放static修饰的静态变量以及全局变量
int global_var = 20;
void Test()
{
static int num = 10;
}
// ...
- 堆:存放动态内存
malloc(10 * sizeof(int));
calloc(10, sizeof(int));
realloc(NULL, 10 * sizeof(int));
// ...
- 代码段:存放如常量字符串等只读数据
"Hello, World!";
// ...
各内存空间的回收机制:
- 栈:函数执行结束后自动释放
- 堆:一般由程序员手动释放,若程序员不释放则在程序结束后由操作系统释放
- 数据段:程序结束后由操作系统释放