常见的动态内存错误总结(二)

简介: 本文总结了两种常见的动态内存错误:内存泄漏和野指针。在示例代码中,内存泄漏发生于动态分配内存后未进行释放,导致程序结束时内存无法回收。野指针问题出现在函数返回栈上创建的变量地址,由于栈空间销毁,指针变成无效,访问时会导致错误。文章强调了使用`static`修饰局部变量可以延长其生命周期以避免野指针,以及释放内存后应将指针置`NULL`以防止后续误用。

常见的动态内存错误总结(一)+https://developer.aliyun.com/article/1519315?spm=a2c6h.13148508.setting.14.25634f0eqyvtS5


六、动态开辟的内存未释放(内存泄漏

代码一

void test()
{
     int *p = (int *)malloc(100);
     if(NULL != p)
     {
         *p = 20;
     }
}
 
int main()
{
     test();
     while(1);
}


如上代码中,在test函数中开辟了动态内存,然而却并没有在其中释放。整个程序中都没有释放动态内存,显然造成了内存泄漏。


代码二

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;语句,完成“善后”工作。


七、返回栈空间地址的问题与野指针陷阱

代码一

*运行test函数,会有什么结果?能正确输字符串"hello"吗?

//运行test函数,会产生什么结果?
 
char *Get()
{
  char p[] = "hello";
  return p;
}
 
void test()
{
  char* str = NULL;
  str = Get();
  printf(str);
}1.


答案是不能,该代码会运行出错,因为该程序中的str指针,实际上是一个野指针。


在Get()函数中,开辟了一个字符数组p[ ]。char p[ ] 是一个临时的数组空间,里面存放了hello\0这6个字符。它是开辟在栈上的,出了函数就被销毁。


因此,虽然str接收了返回值字符数组的首元素地址p,但函数调用完毕后,由于函数栈空间销毁,数组空间已不存在。


str就成了指向无访问权限空间的野指针,在printf访问时出错。




结论


从函数返回一个指向临时在栈空间开辟的指针,就会有野指针访问的问题。因为在函数栈帧中创建的变量,出了函数,栈帧空间就销毁。空间销毁,而空间的地址还在使用,就会出错。


而若将char p[ ] 用static修饰,将p定义为静态变量,程序就能正常运行:


//ok
char *Get()
{
  static char p[] = "hello";  //加了static
  return p;
}
 
void test()
{
  char* str = NULL;
  str = Get();
  printf(str);
}


因为在函数局部变量前用static修饰,变量就成了静态变量。


加了static的变量是在静态区开辟的,不是在栈里开辟的,因此p数组的的生命周期延长,在函数调用结束后,空间也未销毁。因此,str没有成为野指针,str中保存的指针p仍然有效。此时能够正常访问,并打印出字符串"hello"。  


代码二


*运行test函数,会有什么结果?能正确输字符串"hello"吗?


//运行test函数,结果如何?
 
char *Get()
{
  char *p = "hello";
  return p;
}
 
void test()
{
  char* str = NULL;
  str = Get();
  printf(str);
}


答案是可以。该代码能够正常运行并输出"hello"。


该代码与代码一的区别在于,char* p 指向的是一个常量字符串"hello"。常量字符串是存放在静态区而不是栈区的,因此出了Get函数内存空间也不会销毁。在test函数中仍然可以正常访问。


(注意,由于常量字符串不能被改变,因此它的首元素地址的数据类型是const char* 。p在接收时,最好也把数据类型写成const char*,否则编译器会报警告。)


代码三


*该代码正确吗?


int* test2()
{
  int a = 10;
  return &a;
}
 
int main()
{
  int*p  = test2();
  printf("%d\n", *p);
 
  return 0;
}


该代码自然是不正确。和上面的代码一一样,函数返回了局部变量的地址,造成了p的野指针问题。


代码四


*该代码正确吗?

int test2()
{
  int a = 10;
  return a;
}
 
int main()
{
  int p  = test2();
  printf("%d\n", p);
 
  return 0;
}


该代码正确。因为返回的并不是变量的地址,而是变量的值。通过寄存器,test2函数能将局部变量的返回值带入main函数的变量p中。


代码五


//运行Test函数,会有什么结果?
 
void Test(void) 
{
     char *str = (char *) malloc(100);
     strcpy(str, "hello");
     free(str);    //释放str
     if(str != NULL)
     {
         strcpy(str, "world");
         printf(str);
     }
}


在释放了str后,str成为野指针。str并未置NULL,进入 if 语句后,strcpy野指针非法访问,运行出错。


注意,free后必须先将指针置NULL。否则使用指针极易出现空指针访问错误!

相关文章
|
2月前
|
C++ 容器
常见的内存分配错误
【10月更文挑战第11天】
47 2
|
7月前
|
存储 安全 编译器
常见的动态内存错误总结(一)
该文讨论了C语言中与动态内存和指针相关的常见问题。首先,强调了对NULL指针解引用的危险性,示例展示了未检查动态内存分配结果导致的问题,并指出`free()`函数传入NULL是安全的。接着,通过代码解释了指针传值调用时的陷阱,说明了为何直接调用`GetMemory(str)`无法改变`Test`函数中`str`的值。文章还提到了动态内存的越界访问和非法释放,包括释放非动态内存、只释放内存的一部分以及重复释放同一块内存的错误情况。最后,建议在释放内存后将指针设为NULL以防止后续误用。
74 1
|
7月前
6个常见的动态内存的错误和动态内存经典笔试题
6个常见的动态内存的错误和动态内存经典笔试题
|
7月前
|
存储 程序员 Shell
【C/C++ 内存管理函数】C语言动态内存管理大揭秘:malloc、calloc、realloc与new的对比与差异
【C/C++ 内存管理函数】C语言动态内存管理大揭秘:malloc、calloc、realloc与new的对比与差异
246 0
|
C语言
【动态内存管理】动态内存函数
【动态内存管理】动态内存函数
动态内存分布——malloc,calloc,realloc,free的使用。以及关于动态内存的常见错误。
动态内存分布——malloc,calloc,realloc,free的使用。以及关于动态内存的常见错误。
|
编译器
C进阶:动态内存函数 malloc calloc realloc free及常见动态内存开辟错误(下)
C进阶:动态内存函数 malloc calloc realloc free及常见动态内存开辟错误(下)
68 1
|
编译器
动态内存函数和常见的动态内存错误
动态内存函数和常见的动态内存错误
|
编译器 C语言
常见的动态内存的错误 和 柔性数组
✅<1>主页:C语言的前男友 📃<2>知识讲解:常见的动态内存的错误,以及柔性数组(C99) 🔥<3>创作者:C语言的前男友 ☂️<4>开发环境:Visual Studio 2022 💬<5>前言:之前我们说过动态内存管理的知识,今天我们来见识一下,动态内存管理常见的错误。帮助大家对动态内存管理有更深入的理解。