目录
指针+结构体+动态内存管理=数据结构
动态内存相关代码:month_11/test_14/main.c · Hera_Yc/bit_C_学习 - 码云 - 开源中国
动态项目实践:Project_5 · Hera_Yc/Project - 码云 - 开源中国
栈区、堆区、静态区存储的数据:
开辟和释放动态内存
头文件声明 <stdlib.h>
动态内存指的就是堆区内存,malloc和free都是建立在堆区的基础上的。
malloc开辟空间
C语言提供的动态开辟内存的函数:
void *malloc( size_t size );
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 开辟成功,返回一个指向这片空间的指针。
- 开辟失败,返回一个空指针。
- 返回值的类型是 (void*) 型,所以malloc函数并不知道开辟空间的类型,需要使用者自己决定
- 如果size=0,malloc的行为是未定义的,取决于编译器。
int* p = (int*)malloc(40);
free释放空间
C语言提供的动态开辟回收的函数:
函数声明:
void free( void *ptr);
free释放动态开辟的内存。
- 如果参数ptr指向的空间不是动态开辟的(栈、静态),则free函数的行为是未知的。
- 如果参数ptr是NULL,则free不执行任何操作。
1、free的参数只能是一块动态内存的起始地址。
2、没有free释放空间,并不是说内存空间就不回收了。
3、当程序退出时,系统会自动回收内存空间。
clloc初始化开辟空间
函数声明:
void *calloc( size_t num, size_t size );
/*
num:Number of elements
size:Length in bytes of each element
*/
calloc开辟内存空间的同时初始化。
- 为num个大小为size的元素开辟一块空间,并把每块空间的每个字节初始化为0。
realloc修改动态内存
改变动态内存分配的空间。
函数声明:
void *realloc( void *ptr, size_t size );
//ptr:要修改的空间地址
//size:修改的空间大小
- realloc函数会找一块新的空间,将旧空间的拷贝到新空间,再将旧的空间释放掉。
- 返回新空间的地址。
realloc的应用:
int main()
{
int* p = (int*)malloc(40);
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 1;
}
//使用
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}
//扩容
int* ptr = (int *)realloc(p, 80);
if (ptr != NULL)
{
p = ptr;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);
p = NULL;
return 0;
}
常见的内存错误
1、对空指针的解引用操作
void test()
{
int* p = (int *)malloc(3);
*p = 20;//如果p的指针是NULL,就会报错
free(p);
}
改正:在使用malloc返回的指针前,对malloc进行一次检查。
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 1;
}
2、对动态开辟空间的越界访问
int main()
{
int* p = (int*)malloc(40);
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 1;
}
int i = 0;
for (i = 0; i <= 10; i++)
{
p[i] = i;//i=10,越界访问
}
free(p);
p = NULL;
}
3、对非动态开辟内存使用free释放
int main()
{
int a = 10;
int* p = &a;
free(p);
p = NULL;
return 0;
}
4、使用free释放动态内存的一部分
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
//使用
int i = 0;
for (i = 0; i < 5; i++)
{
*p = i;
p++;//初始化完成后,p不能找到动态内存的起始位置
}
//释放
free(p);
p = NULL;
return 0;
}
改正:把动态内存的起始地址存储起来
int* ptr = p;
free(ptr);
ptr = NULL;
5、对同一块空间的多次释放
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
free(p);
free(p);
}
改正:在释放完成后,立刻把p设为空指针,free(NULL)不执行任何操作。
free(p);
p = NULL;
6、动态开辟内存忘记释放
忘记释放动态内存容易造成内存泄漏。
内存泄漏:程序开辟的动态内存没有释放,开辟空间的起始地址又不能找到。
1、对该程序来说,这片内存既不能被利用,也不能被找到,对该程序而言,这块内存造成内存泄漏。
2、当程序终止运行时,操作系统会自动释放程序所占用的内存,这部分找不到的内存也会被释放掉。
void test()
{
int* p = (int*)malloc(40);
int flag = 0;
scanf("%d", &flag);
if (flag == 5);
return;
free(p);
p = NULL;
}
动态内存的经典题目
1、请问调用Test函数会产生怎样的结果?
void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "HelloWorld");
printf(str);
}
int main()
{
Test();
return 0;
}
对printf参数的补充:
printf("helloworld\n");
char* p = "helloworld\n";
等价于
printf(p);
2、这些程序的为问题的原因是?
int* f1(void)
{
int x = 10;
return (&x);
}//野指针
int* f1(void)
{
int* ptr;
*ptr = 10;
return ptr;
}//野指针
3、Test可以打印出来helloworld吗?
char* GetMemory()
{
char p[] = "hello world";
return p;
}
void Test()
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return;
}
4、 Test可以打印出来helloworld吗?
void GetMemory(char** p,int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str,100);
strcpy(str, "HelloWorld");
printf(str);
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
这里的Test可以打印出来,但是要记得释放掉开辟的内存空间。
4、 Test可以打印出来world吗?
Test()
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL);
{
strcpy(str, "world");
printf(str);
}
}