
🌈这里是say-fall分享,感兴趣欢迎三连与评论区留言 🔥专栏:《C语言入门知识点》、《C语言底层》、《精通C语言》、《C语言编程实战》 💪格言:做好你自己,你才能吸引更多人,并与他们共赢,这才是你最好的成长方式。
上一篇是关于动态内存管理的四个函数的文章,那这一篇我们就来了解一下柔性数组和一些常见的动态内存管理时候的错误,帮助大家减少这些错误的出现,同时也给大家几个小练习,看看大家能不能找到这些练习中的瑕疵吧~
错误案例:
void test1()
{
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;//这里如果malloc返回的是NULL,就会导致错误
free(p);
}改正:
void test1()
{
int* p = (int*)malloc(INT_MAX / 4);
if (p != NULL)
{
*p = 20;
}
free(p);
p = NULL;
}错误案例:
void test2()
{
int* p = (int*)malloc(10 * sizeof(int));
if (NULL == p)
{
exit(EXIT_FAILURE);
}
int i = 0;
for (i = 0;i < 11;i++)//注意这里会导致超出开辟的空间,即越界访问
{
*(p + i) = 1;
}
free(p);
p = NULL;
}改正:
void test2()
{
int* p = (int*)malloc(10 * sizeof(int));
if (NULL == p)
{
exit(EXIT_FAILURE);
}
int i = 0;
for (i = 0;i < 10;i++)
{
*(p + i) = 1;
}
free(p);//这里能直接返回p吗?这个改正修改对了吗
p = NULL;
}这里提示一下:可以用另一个指针
p接收malloc返回的动态内存指针ptr,之后对p执行free操作是合法且正确的,只要保证p指向的是malloc(或calloc/realloc)分配的完整内存块。
#include <stdlib.h>
int main() {
int* ptr = (int*)malloc(10 * sizeof(int)); // 分配内存,ptr 指向该内存
int* p = ptr; // p 接收 ptr 的地址,此时 p 和 ptr 指向同一块内存
// 使用内存...
free(p); // 正确:释放 p 指向的内存(与 ptr 指向的是同一块)
p = NULL; // 好习惯:释放后置空,避免野指针
ptr = NULL; // 同理,ptr 也需置空(此时它已指向被释放的内存)
return 0;
}错误案例:
void test3()
{
int a = 0;
int* pa = &a;
free(pa);
}改正:
除了直接去掉,这里是无法修正的,使用free时要确保是对指向动态空间的指针使用的错误案例:
void test4()
{
int* p = (int*)malloc(40);
int i = 0;
for (i = 0;i < 10;i++)
{
p++;
}
free(p);
}改正:
void test4()
{
int* p = (int*)malloc(40);
int i = 0;
for (i = 0;i < 10;i++)
{
p++;
}
p = p - 10;
free(p);
p = NULL;
}//这些案例操作有些是无意义的,只是为了表现错误的特性
//注意,上面 1.2 修改完是不是犯了这个错误呢?错误案例:
void test5()
{
int *p = (int*)malloc(40);
int* ptr = p;
free(p);
free(ptr);
}改正:
void test5()
{
int *p = (int*)malloc(40);
int* ptr = p;
free(p);
p= NULL;//p置空的话释放ptr时候就不会出现问题了,不过一般只需要释放一次即可
free(ptr);
ptr = NULL;//虽然释放一次即可,但是注意要置空两次才行
}所谓的内存泄漏会导致什么呢?其实就是内存一点点被吃掉,尤其是在一些循环的地方。 错误案例:
void test6()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
}
int main()
{
while (1)
{
test();
}
}//这里的test6中就有空间不释放,如果这个代码运行起来,循环一次就吃掉一点空间,这是很致命的改正:
void test6()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
free(p);
p = NULL;//在动态内存的使用中,最重要的就是释放内存和置空操作
}
int main()
{
while (1)
{
test();
}
}void GetMemory(char* p)
{
p = (char*)malloc(100);
}
void Test1(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
int main()
{
Test1();
return 0;
}char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}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;
}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;
}记得我们之前了解过的变长数组吗?是的,这个柔性数组和那个不一样。
int main() {
int n;
printf("请输入数组长度:");
scanf("%d", &n);
int arr[n]; // 变长数组:长度由变量 n 决定(运行时确定)
// 使用变长数组
return 0;
}
//但是有的编译器是不能使用变长数组的,例如vs2022n)malloc申请的空间在堆区)int arr[n] = {0}; 是错误的struct S
{
int n;
int arr[];//柔性数组成员
}s;大小:
struct S
{
int n;
int arr[];
}s;
int main()
{
printf("%zd\n", sizeof(s));
return 0;
}//输出结果是4也就是说:柔性数组成员是不占空间大小的,那要怎么使用呢?
先看一下柔性数组的特点:
malloc()进⾏内存的动态分配,并且分配的内存应该⼤于结构的⼤⼩,以适应柔性数组的预期⼤⼩。使用:
typedef struct S
{
int i;
int arr[];
}s;
int main()
{
printf("%zd\n", sizeof(s));
s* p = (s *)malloc(4 + 10 * sizeof(int));//拓展空间
//使用柔性数组
p->n = 10;
for (i = 0;i < 10;i++)
{
p->arr[i] = i + 1;
}
//内存释放
free(p);
p = NULL;
return 0;
}能看得出来,柔性数组的使用是要先开辟空间的,然后就可以正常使用了,也可以搭配
realloc去进行长度的变化 等等,数组名是指针!换个形式的话,也行?
typedef struct S
{
int i;
int * ps;
}s;
int main()
{
printf("%zd\n", sizeof(s));
s* p = (s*)malloc(sizeof(s));//动态分配结构体内存
p -> i = 100;//i = 100
int* ps = (int*)malloc(p->i * sizeof(int));//为指针指的地址分配对应个空间
//使用柔性数组
for(i=0; i<100; i++)
{
p->ps[i] = i;
}
//内存释放
free(p -> ps);//先要释放后申请的空间
p -> ps = NULL;
free(p);//再释放前面申请的空间
return 0;
}这两种方法都可以达到相同的效果,但是第一种方法相比第二种方法有一些优点:
