c语言编程常见问题解析(资源泄漏)

1内存泄漏
1.1.1 示例
(1)问题描述
通过 malloc 等函数动态申请的内存在使用后必须相应地调用 free 等函数释放,否 则这块内存就不能被再次使用,出现内存泄漏,示例如下:

示例一
在这里插入代码片` 
int my_example(int c) 
{  
    void *p = malloc(10);  
    if(c)  
    { 
           return -1;   /*"p" 指向的内存被泄漏*/ 
    }                              
    free(p); 
    return 0;
 }

示例二:
int wrong_check() 
{  
   void *p = malloc(10);  
   void *q = malloc(20); 
   if((NULL == p) || (NULL == q)) 
   { 
          return -1;
           /*"p" or "q" 其中之一为 NULL 时,另外一个指向的内存被泄漏*/ 
   }   
  free(q);
  free(p);
  return 0;
} 
 
示例三:
int test(int i) 
{
    void *p = malloc(10);
    void *q = malloc(4);
    if(i > 0)
    { 
          p = q;      /* “p”被重新赋值,指向新的地址,之前指向的内存被泄漏*/
    } else
    {
          free(q);
    } 
  free(p);
  return 0; 
}

(2)防范措施

  1. 每次申请内存前必须明确由谁负责释放,何时释放,在何处释放;
  2. 在异常分支中,保持清醒,一定要在 return 语句前考虑是否要释放内存;
  3. 内存申请后立即检查是否申请成功,不要多个指针用同一个 if 语句判断; 4) 申请内存成功后,禁止对指向给内存地址的指针重新赋值。

2, 句柄泄漏
2.2.1 示例
1)问题描述
通过 open 等函数获得的文件句柄在使用完后必须通过 close 等函数释放,否则 就会 造成句柄泄漏(handle leak),此时没有关闭句柄 fd,就会出现句柄泄漏,示例如下。

示例一:
int leak_example(int c) 
{ 
    int fd = open(“my_file”, MY_OPEN_OPTIONS);
    if(c)
    { 
       return -1;   // 文件句柄“fd”,发生泄漏
    }  
  close(fd);    
  return 0;
}
示例二:
void gpio_read(int pin_no, unsigned char * val)
{  
   int fd = -1;  
  fd = open(GPIO_DEV, O_RDWR);    
  if (fd == -1)
  {  
       perror("GPIO Driver open"); 
       printf("# Make sure GPIO Module Loaded\n"); 
       return -1;  
   }  
  struct io_param gpio_io_param;
  memset((char *)&gpio_io_param,'\0',sizeof(gpio_io_param)); 
  gpio_io_param.cmd = GPIO_DRV_READ; 
  gpio_io_param.pin_no = pin_no; 
  if (ioctl(fd,gpio_io_param.cmd,&gpio_io_param) == -1) 
 {      
    perror("GPIO ioctl");
    return ;
 } 
 close(fd); 
 *val = gpio_io_param.value;
  return ;
}
 

2) 影响
这是一个典型的异常分支处理不当引起的资源泄漏问题,在正常情况下可能不会出现问题,但是一个非常危险的隐患,一旦走入异常分支,就会出现句柄泄漏,而且此函数是一个读取 GPIO 的公用函数,很可能会反复大量的调用,最终会因资源耗尽而导致系统崩溃,
3)防范措施
只要获得了句柄,无论是正常分支还是异常处理分支,一定要在 return 语句前释放句柄

3,其他泄露
上述提到的两类问题是最常见的资源泄漏问题,除此之外,还有文件指针泄漏(fopen等函数打开文件,在使用完后必须通过 close 等函数关闭导致的资泄漏),信号量的泄漏(即创建信号使用完之后没有销毁导致的资源泄漏)等多种形式。

示例一:
 void test(int c)
{   
  FILE *p = fopen("foo.c", "rb");   
  if(c) 
  { 
      return;       // 文件指针“p”没有关闭,发生泄漏
  } 
  fclose(p);
} 

如下的代码块所示,是一个创建线程的函数,为了保证下一个线程之前,上次创建的线程的某些动作必须执行完成。在线程创建前创建一个信号量,然后 wait,在创建的线程中完成相应动作之后,执行 sem_post 操作。此时程序可以继续往下执行了。 其中 init_sync 为局部变量,函数执行完成之后,该信号量已经用完了,之后未进行信号量的删除操作,进而导致每创建一个线程,就会引起一个信号量的泄漏.

示例二:
int pal_create_task(char *name, int priority,  int stack,  void *func_ptr) 
{ 
    Ipcom_sem     init_sync = NULL; //信号量的指针 
    f (!init_sync)
  {
        ipcom_sem_create(&init_sync, 0);//创建互斥信号量
   } 
    /* Create and start the shell process. */
  ipcom_proc_attr_init(&attr);     
  attr.stacksize = stack;     
  attr.priority = priority;     
  attr.arg = init_sync;  //将信号量作为入参,传入要创建的线程中,进行 sem_post 
  if (ipcom_proc_acreate(name, (Ipcom_proc_func)func_ptr, &attr, &pid))
  {
           goto exit;     
   }     
 ipcom_sem_wait(init_sync);     //等待线程创建之后,执行 sem_pos 操作 
exit:     return RESULT_OK; } 

猜你喜欢

转载自blog.csdn.net/qq_40008325/article/details/88880294
今日推荐