常见的动态内存错误
对null指针的解引用操作
int main()//错误1 因为没有判断
{
int* p = (int*)malloc(10000);
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
return 0;
}
对动态开辟空间的越界访问
int main()
{
int* p = malloc(10 * sizeof(int));
if (p == NULL)
{
return 1;
}
int i = 0;
for (i = 0; i < 40; i++)
{
*(p+i) = i;
}
free(p);
p = NULL;
return 0;
}
开辟10个却要访问40个
使用free释放非动态开辟的空间
int main()
{
int arr[10] = { 0 };//栈区
int* p = arr;
//使用
free(p);//使用free释放非动态开辟的空间
p = NULL;
return 0;
}
使用free释放动态内存中的一部分
int main()
{
int* p = malloc(10 * sizeof(int));
if (p == NULL)
{
return 1;
}
int i = 0;
for (i = 0; i < 5; i++)
{
*p++ = i;
}
free(p);
p = NULL;
return 0;
}
p释放的时候释放的是后面的一部分,p++一直在走没人记得这块空间的起始位置
对同一块动态开辟的空间,多次释放
就是使用两次或多次free 释放完后传空指针 下次在释放free(null)什么也不会发生
动态开辟的空间忘记释放- 内存泄漏 - 比较严重的
void test()
{
int* p = (int*)malloc(100);
if (p == NULL)
{
return;
}
//使用
}//没有置null p出了这块空间会销毁 但是malloc不会。malloc只能主动释放 或者程序结束
int main()
{
test();
//....
return 0;
}
题
#include <string.h>
//
//str传给GetMemory函数的时候是值传递,所以GetMemory函数的形参p是str的一份临时拷贝。
//在GetMemory函数内部动态申请空间的地址,存放在p中,不会影响外边str,所以当GetMemory函数返回之后,str依然是NULL。所以strcpy会失败。
//
//当GetMemory函数返回之后,形参p销毁,使得动态开辟的100个字节存在内存泄漏。无法释放。
void GetMemory(char* p)//传过来后p也变空指针
{
p = (char*)malloc(100);//p来维护开辟的100.调用完出去p销毁(p是临时变量)。开辟空间找不到导致内存泄漏
}
void Test(void)
{
char* str = NULL;
GetMemory(str);//值传递 没有传递地址
strcpy(str, "hello world");
printf(str);
}
int main()
{
Test();
return 0;
}//运行不出结果
进行修改后让代码运行
//改:1
char* GetMemory(char* p)
{
p = (char*)malloc(100);
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory(str);
strcpy(str, "hello world");
printf(str);//?
//printf("hello world");//char *p = "hello world";
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
改2
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;
}
题2
//GetMemory 函数内部创建的数组是在栈区上创建的
//出了函数,p数组的空间就还给了操作系统
//返回的地址是没有实际的意义,如果通过返回的地址,去访问内存就是非法访问内存的
//
char* GetMemory(void)
{
char p[] = "hello world"; //hello world\0
return p;//临时变量的创建出函数销毁。但是也会返回返回的是地址编号 出栈销毁 但是堆不会
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}//运行结果出错
题3
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
//free(str);
//str = NULL;少了指针释放
}
int main()
{
Test();
return 0;
}
题4
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);//非法访问内存空间,str指向的动态内存空间已经被释放了
//str = NULL; 记得加上加上这句
if (str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
c/c++程序的内存开辟
临时数据用完就回收的放在栈区上
柔性数组
struct S
{
int n;
int arr[];//大小是未知
};
struct S
{
int n;//4
int arr[0];//大小是未知
}; 以上两种都可以
柔性数组的特点
struct S
{
int n;//4
int arr[0];//大小是未知
};
int main()
{
struct S s = {0};
printf("%d\n", sizeof(s));//?
return 0;
}//结果为4
3
int main()
{
//期望arr的大小是10个整形
struct S*ps = (struct S*)malloc(sizeof(struct S)+10*sizeof(int));//sizeof(struct S) 计算出柔性数组前的大小 后面加的是柔性数组的大小
ps->n = 10;
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
//增加
struct S* ptr = (struct S*)realloc(ps, sizeof(struct S)+20*sizeof(int));
if (ptr != NULL)
{
ps = ptr;
}
//使用
//释放
free(ps);
ps = NULL;
//同上相比这一样可以实现 但是效率不高 并且容易出错 这就是柔性数组的优势
struct S
{
int n;
int* arr;
};
int main()
{
struct S* ps = (struct S*)malloc(sizeof(struct S));
if (ps == NULL)
return 1;
ps->n = 10;
ps->arr = (int*)malloc(10*sizeof(int));
if (ps->arr == NULL)
return 1;
int i = 0;
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
//增加
int*ptr = realloc(ps->arr, 20 * sizeof(int));
if (ptr != NULL)
{
ps->arr = ptr;
}
//使用
//释放
free(ps->arr);
ps->arr = NULL;
free(ps);
ps = NULL;
return 0;
}