动态内存管理
1、动态内存所开辟的空间都是在堆上开辟的;
malloc函数
1、可以向内存申请一块连续可用的空间,并返回这块空间的指针;
2、开辟成功,返回指向空间的指针;
3、开辟失败,返回NULL;
4、malloc申请的空间释放:1、free释放(主动);2、程序退出,操作系统回收(被动释放);
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 开辟一块malloc空间
int* p = (int*)malloc(10 * (sizeof(int)));
// 判断malloc是否为空
if (p == NULL)
{
// 打印出错误原因
perror("malloc");
return 1;
}
// 使用malloc开辟的空间
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i)); // 未初始化
}
return 0;
}
free函数
1、用来释放动态开辟的内存;
2、free的参数的空间不是动态开辟的空间,那free的行为是未定义的;
3、free的参数是NULL指针,函数什么都都不做;
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 开辟一块malloc空间
int* p = (int*)malloc(10 * (sizeof(int)));
// 判断malloc是否为空
if (p == NULL)
{
// 打印出错误原因
perror("malloc");
return 1;
}
// 使用malloc开辟的空间
for (int i = 0; i < 10; i++)
{
*(p + i) = i;
printf("%d ", *(p + i));
}
// free释放空间
free(p);
p = NULL; // 最后将指针制为空指针
return 0;
}
calloc函数
1、calloc也是开辟空间的;
2、calloc会将开辟好的空间全部初始化为0;
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 开辟一块calloc空间
int* q = (int*)calloc(10, sizeof(int));
if (q == NULL)
{
perror("calloc");
return 1;
}
for (int i = 0; i < 10; i++)
{
printf("%d ", *(q + i)); // 全都初始化为了0
}
free(q);
q = NULL;
return 0;
}
realloc函数
1、realloc函数使用来追加空间的;
2、realloc的返回值不能用追加到指定空间接收;(因为如果追加空间失败,则会导致以前开辟的空间也置为空);
3、如果后续空间被占用,不能直接使用;realloc则会找一块新的空,一次性开辟够;并将就空间中的数据拷贝到新空间中,还会将就得空间释放掉,返回新空间的地址;
4、realloc追加的空间内存放的是随机值;
5、realloc函数的第一个参数如果是一个空指针,那么就等价于malloc函数;
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 开辟一块calloc空间
int* q = (int*)calloc(10, sizeof(int));
if (q == NULL)
{
perror("calloc");
return 1;
}
// 使用realloc追加空间
// q = (int*)realloc(q, 10 * sizeof(int));
// 这样写是会出问题的,最好不用自己接收;
int* q1 = (int*)realloc(q, 10 * (sizeof(int)));
if (q1 == NULL)
{
perror("realloc");
return 1;
}
for (int i = 0; i < 20; i++)
{
printf("%d ", *(q + i)); // realloc追加的空间值是随机值
}
free(q);
free(q1);
q = NULL;
q1 = NULL;
return 0;
}
动态内存常见错误
1、对NULL指针的解引用操作;
- 不对动态内存开辟空间的返回值进行判断,就对该指针进行解引用操作,就会出现错误;
2、对动态内存的越界访问;
3、对非动态的内存free释放;
4、使用free释放动态内存的一部分;
- 使用动态内存保证指针的起始位置不变;
5、对同一块动态内存空间多次释放;
- 所以要保证在free完之后将指针置为空指针,这样多次释放就不会报错了;
6、忘记释放动态内存空间(内存泄漏);
笔试题
题目1:未free、解引用NULL指针
源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void GetMemory(char* p)
{
p = (char*)malloc(100); // 没有内存释放,并且形参不会改变实参;
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world"); // 对NULL指针解引用;
printf(str);
}
int main()
{
Test();
return 0;
}
修改后的代码1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void GetMemory(char** p)
{
*p = (char*)malloc(100); // 没有内存释放,并且形参不会改变实参;
// 这里通过解引用找到首元素地址
}
void Test(void)
{
char* str = NULL;
GetMemory(&str); // 所以这里传地址过去,通过形参改变实参;
strcpy(str, "hello world"); // 对NULL指针解引用;
printf(str);
// 使用完后,对动态内存空间进行释放
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
修改后的代码2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void* GetMemory()
{
char* p;
return p = (char*)malloc(100); // 没有内存释放,并且形参不会改变实参;
// 将地址传回去
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
strcpy(str, "hello world"); // 对NULL指针解引用;
printf(str);
// 最后释放
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
题目2:野指针
注意:
局部变量出了当前函数就会销毁,千万千万不能返回变量地址,也就是不能返回栈空间地址,那么所接收的指针就成为了野指针;地址是带回来了,但是空间却销毁了;
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory(); // str成了野指针了
printf(str);
}
题目3:未free(存在内存泄漏)
这个代码和题目1改造后的代码很像, 只是少了释放动态内存空间,存在内存泄漏;
#include <stdio.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:free后未置为NULL
#include <stdio.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;
}
代码错误注释:
#include <stdio.h>
void Test(void)
{
char* str = (char*)malloc(100);
// 未判断NULL
strcpy(str, "hello");
free(str);
// 应该在后面将str置为NULL
if (str != NULL)
{
strcpy(str, "world"); // 先释放了空间,所以这里的str成了野指针
printf(str);
}
}
int main()
{
Test();
return 0;
}
C/C++的内存开辟
还未写完