释放后内存泄漏?

memory leak after free?

提问人:DravStart 提问时间:8/29/2023 最后编辑:Adrian MoleDravStart 更新时间:8/30/2023 访问量:101

问:

我注意到我一直在测试的程序在两个不同的点泄漏内存,无论我是否调用。我不明白为什么会发生这种情况以及如何修复它。free()

我正在使用泄漏来测试内存泄漏:我的机器没有可用的 Valgrind。

报告如下:

leaks Report Version: 4.0, multi-line stacks
Process 21334: 226 nodes malloced for 18 KB
Process 21334: 2 leaks for 544 total leaked bytes.

STACK OF 1 INSTANCE OF 'ROOT LEAK: <malloc in tokenizer>':
3   libsystem_pthread.dylib               0x18d51eda0 thread_start + 8
2   libsystem_pthread.dylib               0x18d523fa8 _pthread_start + 148
1   a.out                                 0x102c13c10 tokenizer + 36
0   libsystem_malloc.dylib                0x18d364d88 _malloc_zone_malloc_instrumented_or_legacy + 128 
====
    1 (272 bytes) ROOT LEAK: <malloc in tokenizer 0x120004080> [272]

STACK OF 1 INSTANCE OF 'ROOT LEAK: <calloc in printwords>':
3   libsystem_pthread.dylib               0x18d51eda0 thread_start + 8
2   libsystem_pthread.dylib               0x18d523fa8 _pthread_start + 148
1   a.out                                 0x102c13d4c printwords + 40
0   libsystem_malloc.dylib                0x18d364eb0 _malloc_zone_calloc_instrumented_or_legacy + 92 
====
    1 (272 bytes) ROOT LEAK: <calloc in printwords 0x11ef041d0> [272]

它是一个多线程程序,读取文本文件,分离单词,然后打印它们。这个版本不打印单词,但内存泄漏完全相同(我已经测试过了)。

法典:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <pthread.h>
#include <string.h>

#include "functs.h"


pthread_t *thpool;
Deque_t* deque_12;
Deque_t* deque_23;

void* readfile(void* arg);
void* tokenizer();
void* printwords();
char rd;
char tk;


void init(){
   deque_12 = new_deque();
   deque_23 = new_deque();
   rd = 0; //lines read
   tk = 0; //all lines tokenized
}

int main(int argc, char *argv[]){
   
   char* filename = "loremIpsum.txt";
   init();
   thpool = malloc(sizeof(pthread_t)*3);
   
   thread_create(&thpool[0],/*attrs*/ NULL, readfile, (void*)filename);
   thread_create(&thpool[1],/*attrs*/ NULL, tokenizer, /*args*/NULL);
   thread_create(&thpool[2],/*attrs*/ NULL, printwords, /*args*/NULL);

   
   for(int i=0; i<3; i++)
      thread_join(thpool[i], NULL);
   
   free_deque(deque_12,1);
   free_deque(deque_23,0);
   free(thpool);
}

void* readfile(void* arg){
   FILE* fp= fopen((char*)arg, "r");
   
   char* line= calloc(1,sizeof(char)*BUFSIZE);
   while(fgets(line, BUFSIZE, fp)!=NULL){
      push_tail(deque_12, (void*)line);
      line= calloc(1,sizeof(char)*BUFSIZE);
   }
   rd=1;
   printf("1: File read\n");
   fclose(fp);
   free(line);
   return NULL; 
}

void* tokenizer(){
   struct timespec t = {0,1000000};
   while(isEmpty(deque_12))
      nanosleep(&t, NULL);
   char* bfr = malloc(sizeof(char)*BUFSIZE); //memory leak
   while(1){
      pop_head(deque_12, (void*)&bfr,1);
      if(bfr==NULL && rd) break;
      else if(bfr==NULL){
         nanosleep(&t, NULL);
         continue;
      }
      char* state;
      char* token = strtok_r(bfr, " ",&state);
      while(token!=NULL){
         push_tail(deque_23, (void*)token);
         token = strtok_r(NULL, " ",&state);
      }
   }
   free(bfr);
   tk=1;
   puts("2: Tokenized");
   return NULL;
}
void* printwords(){
   struct timespec t = {0,1000000};
   char* word= calloc(1, sizeof(char)*BUFSIZE); //memory leak
   while(1){
      pop_head(deque_23, (void*)&word,0);
      if(word==NULL && tk) break;
      else if(word==NULL){
         nanosleep(&t, NULL);
         continue;
      }
      printf(">%s\n",word);
      fflush(stdout);
   }
   puts("3: all words printed");
   free(word);
   return NULL;
}

functs.h:

#ifndef _FUNCTS_H
#define _FUNCTS_H

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <errno.h>

#ifndef BUFSIZE
#define BUFSIZE 256
#endif

void thread_create(pthread_t *tid,const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg){
    int err;
    if((err = pthread_create(tid, attr, start_routine, arg)) != 0){
        perror("pthread_create");
        errno = err;
        pthread_exit(&errno);
    }
}
void thread_join(pthread_t tid, void **retval){
    int err;
    if((err = pthread_join(tid, retval)) != 0){
        fprintf(stderr,"FATAL_ERROR: thread_join");
        errno = err;
        pthread_exit(&errno);
    }
}
void mtx_lock(pthread_mutex_t *mtx_ptr){
    int err;
    if((err = pthread_mutex_lock(mtx_ptr)) != 0){
        puts("mutex_lock");
        errno = err;
        pthread_exit(&errno);
    }
}
void mtx_unlock(pthread_mutex_t *mtx_ptr){
    int err;
    if((err = pthread_mutex_unlock(mtx_ptr)) != 0){
        perror("mutex_unlock");
        errno = err;
        pthread_exit(&errno);
    }       
}


typedef struct node{
   void* data;
    struct node *next;
    struct node *prev;
}Node_t;

typedef struct deque{
   Node_t *head;
   Node_t *tail;
   pthread_mutex_t mtx;
}Deque_t;

Deque_t* new_deque(){
   Deque_t* d = malloc(sizeof(Deque_t));
   d->head = NULL;
   d->tail = NULL;
   pthread_mutex_init(&(d->mtx), NULL);
   return d;
}

int isEmpty(Deque_t *d){
   return (d->head == NULL);
}
void push_tail(Deque_t* d, void* data){
   Node_t *newNode = malloc(sizeof(Node_t));
   newNode->data = data;
   newNode->next = NULL;
   mtx_lock(&(d->mtx));

   if(isEmpty(d)){
      newNode->prev = NULL;
      d->head = newNode;
   }
   else{
      newNode->prev = d->tail;
      d->tail->next = newNode;
   }
   d->tail = newNode;
   mtx_unlock(&(d->mtx));
}
void pop_head(Deque_t* d, void** val, int ismallocd){
   mtx_lock(&(d->mtx));
   if(isEmpty(d)){
      mtx_unlock(&(d->mtx));
      *val = NULL;
      return;
   }
   Node_t *tmp = d->head;
   *val = d->head->data;
   if(d->head == d->tail){
      d->head = NULL;
      d->tail = NULL;
   }
   else{
      d->head = d->head->next;
      d->head->prev = NULL;
   }
   mtx_unlock(&(d->mtx));
   if(ismallocd) free(tmp->data);
   free(tmp);
} 
void free_deque(Deque_t* d, int ismallocd){
   Node_t* tmp;
   while(d->head != NULL){
      tmp = d->head;
      d->head = d->head->next;
      if(ismallocd) free(tmp->data);
      free(tmp);
    }
    free(d);
}
#endif
C 内存泄漏 malloc free

评论

0赞 DravStart 8/29/2023
@Mat是的。 退出循环。程序正确结束。if(bfr==NULL && rd) break

答:

0赞 Adrian Mole 8/30/2023 #1

在函数中,按以下行分配内存:printwords

char* word= calloc(1, sizeof(char)*BUFSIZE);

尝试在函数末尾释放该内存,并执行以下操作:

free(word);

但是,到那时(即当循环被破坏时),指针必须是 ,因为离开该循环的唯一方法就是在行中。请记住,使用参数调用函数不是错误,并且具有明确定义的(根据 C 标准)行为:它什么都不做。while(1)wordNULLif(word==NULL && tk) break;freeNULL

我不完全确定您为什么要进行这种分配(可能,以便您可以将非空指针传递给函数);但是,如果要这样做,则应将其分配给其他变量,然后将其复制到循环中使用的指针。pop_headword

以下是执行此操作的一种方法:

void* printwords()
{
   struct timespec t = {0,1000000};
   char* temp = calloc(1, sizeof(char)*BUFSIZE);
   char* word = temp; // We can use that local allocation but we MUST remember it!
   while(1) {
      pop_head(deque_23, (void*)&word,0);
      if(word==NULL && tk) break;
      else if(word==NULL){
         nanosleep(&t, NULL);
         continue;
      }
      printf(">%s\n",word);
      fflush(stdout);
   }
   puts("3: all words printed");
// free(word); // WRONG! At this point, "word" is NULL
   free(temp); // Should work!
   return NULL;
}

或者,您可以在函数中处理该分配的缓冲区(例如,在设置和返回之前),但这可能会干扰列表删除过程的正常工作。free()charpop_headfree(*val)*val = NULL;

请注意,您的缓冲区在函数中遇到了完全相同的问题,并且补救措施是相同的。bfrtokenizer

评论

0赞 DravStart 8/30/2023
没有想到我可以在没有分配lmao的情况下声明指针,这真的是一个被忽视的错误。如果我需要分配它,我现在知道我应该事先创建一个临时变量。多谢!