glibc检测到:双重释放或损坏

3

我将解释我所做的简要编码步骤以及我面临问题的领域。

main.cpp

int main()
{
    int cnt_map,i=1,value;

   /* My question is about this char pointer "key" */ 
    char *key =(char*)malloc(sizeof(char) * 25);

   if(key!=NULL)
   {
     printf("Key value is not NULL,its value is:%x\n",key) ;
     cout<< "Enter the number of elements required in container map"<<endl;
     cin >> cnt_map;
     for (i=1;i<=cnt_map;i++)
     {
       cout << "Enter the key : ";
       cin >>key;
       cout << "Enter the key value:" ;
       cin >>value;
       printf("value pointed by ptr key: %s, value in ptr: %x\n", key,key);
       c -> add_map1(key,value); //Function inserts value to map container
       key+=sizeof(key);
     }
     c -> size_map1();           //Function displays size of map container
     c -> display_map1();        //Function displays contents of map container
  if(key)
  {
    printf("FINALLY:value pointed by ptr key: %s, value in ptr: %x,size:%d\n",key, key, sizeof(key));
    free(key);
  } 
 }
return 0;
}

当我尝试编译和运行上述代码时,我能够成功地编译代码,但在尝试运行应用程序时出现了“glibc detected: double free or corruption”错误。
现在我的问题是,我创建了一个字符指针(char *key =(char*)malloc(sizeof(char) * 25);),并成功使用malloc为其分配了内存。在完成我的过程后,当我尝试释放该字符指针时,我会收到双重释放或破坏错误。我了解到,使用malloc/calloc分配内存的任何变量都应最终被释放。请告诉我为什么我会遇到这个错误,为什么我不应该这样做?如果可能的话,请告诉我char* key上的内存操作(以图形方式)。
注意:上面呈现的代码不是完整的代码,我只是解释了我遇到问题的地方,如果我不释放指针变量,我的应用程序将成功运行。

你可以随时使用 valgrind 来调试所有这些内存错误。它几乎不会丢失任何信息,输出结果也很易读。 - cgledezma
4个回答

7
通过这样做:
key+=sizeof(key);

你的key变量不再指向你分配的内存开头。你必须传递原始指针给free()。你需要将原始指针存储在另一个变量中,以便你可以在最后正确地free()它。
(你可能可以简单地删除那行代码 - 我不确定它的作用,因为sizeof(key)要么是4,要么是8。我怀疑这是多余的。)

1
似乎没有任何理由首先增加“key”。 - Barmar
那么,对于使用key+=sizeof(key);分配的所有内存位置怎么办呢?如何释放所有这些位置?如果我们不释放这些位置会发生什么?为什么我没有使用free()时我的应用程序仍然能够成功执行? - ybc
@RichieHindle 没错!这就是发生的事情。当我重新运行应用程序时,我发现内存开始在相同的先前位置分配。感谢您的良好分析。无论如何,在我的情况下,我需要很少的迭代,但是还有一个问题,就是在什么样的情况下我们需要释放分配的内存...我猜想与您提供的解释形成对比(如果可能,给出一个用例)。 - ybc
1
@ybc:我想不出有哪种情况下不调用“free”会导致代码失败,除非是由于内存耗尽。但这是极其糟糕的做法,没有人会推荐这样做。(在您的示例中,您可以使用普通数组“char key [25];”,这样就根本不需要调用“free”,就像您不为“cnt_map”或“i”调用“free”一样。 - RichieHindle
如果add_map1没有复制键,那么你需要在每次循环中调用malloc(),并且不应该调用free(),因为映射仍然引用该字符串。但是它确实应该复制,以避免这个问题,并且也可以使用自动字符串作为键。 - Barmar
显示剩余3条评论

5
那是因为这一行代码:key+=sizeof(key);key的地址与malloc返回的地址不相同。
例如: char *key = (char*)malloc(sizeof(char) * 25); 假设malloc返回地址20000(完全无意义的地址,只是用于示例)。
现在你执行key+=sizeof(key);,所以key=20000 + 4 = 20004。问题在于你试图释放指向地址20004而不是20000的key
为了解决这个问题,请尝试以下操作:
int main()
{
    int cnt_map,i=1,value;
    char *key_save;

   /* My question is about this char pointer "key" */ 
    char *key =(char*)malloc(sizeof(char) * 25);

    key_save = key;
   if(key!=NULL)
   {
     printf("Key value is not NULL,its value is:%x\n",key) ;
     cout<< "Enter the number of elements required in container map"<<endl;
     cin >> cnt_map;
     for (i=1;i<=cnt_map;i++)
     {
       cout << "Enter the key : ";
       cin >>key;
       cout << "Enter the key value:" ;
       cin >>value;
       printf("value pointed by ptr key: %s, value in ptr: %x\n", key,key);
       c -> add_map1(key,value); //Function inserts value to map container
       key+=sizeof(key);
     }
     c -> size_map1();           //Function displays size of map container
     c -> display_map1();        //Function displays contents of map container
  if(key)
  {
    printf("FINALLY:value pointed by ptr key: %s, value in ptr: %x,size:%d\n",key, key, sizeof(key));
    free(key_save);
  } 
 }
return 0;
}

如果你使用正确的地址free(key),那么所有分配给key的内存空间都将被释放(包括地址“20004”上的字节)。 - nouney
1
“fix”是错误的,因为增加键是错误的...请参见此处的其他答案和评论。 - Jim Balter
2
@JimBalter 这不是我的问题。问题是:为什么会出现内存损坏?我只回答这一点,我不在这里审查所有的代码。 - nouney

1

只需要删除这一行:

key+=sizeof(key);
key 不是指向字符串数组的指针,而是指向单个字符串的指针。每次递增它时,都会减少字符串中可用的空间。第一次读取键时,有25个字节可用。下一次,您将递增 key 4或8个字节,但分配的空间结尾没有改变,因此现在只有21或17个字节可用。第三次仅有17或9个字节,以此类推。几次迭代后,您将递增 key 超过您分配的内存块的末尾,并开始写入未分配的内存(或分配给其他数据结构的内存)。这是未定义的行为,很可能会导致程序中出现不可预测的故障。

由于您正在使用C ++,应该使用std::string代替char []表示字符串,并使用std::vector代替普通数组。这些数据结构会自动扩展,因此可以避免像这样的缓冲区溢出问题。


-1

这并不考虑你的代码,但我在读写问题(操作系统)http://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem中遇到了同样的问题。

这是由于文件指针是全局的,因此每当任何一个读者尝试读取并在另一个读者读取并关闭文件指针之间时,当另一个读者尚未完成阅读并尝试在阅读后关闭文件指针时,文件指针已经关闭,它没有指向任何文件。 我使用的解决方案是: 不要将文件指针声明为全局变量,而是将其声明为读取函数的本地变量,否则您可以检查文件指针是否为NULL,如果为NULL,则不要关闭文件指针。

#include<stdio.h>
#include<semaphore.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
sem_t x,wsem;
int rc=0;
char ch;
char str[20];
void *reader(void *);
void *writer(void *);


int main()
{
 int nw,nr,i=0,j=0;
 pthread_t w[10],r[10];
 sem_init(&x,0,1);
 sem_init(&wsem,0,1);
 rc=0;
 printf("Enter the no of readers:");
 scanf("%d",&nr);
 printf("Enter the no of writers");
 scanf("%d",&nw);
 while(i<nw || j<nr) 
 {

  if(i<nw)
  {
pthread_create(&w[i],NULL,writer,(void *)i);

i++;
  }
  if(j<nr)
  {
pthread_create(&r[j],NULL,reader,(void *)j);

    j++;
  }
 }
 for(i=0;i<nw;i++)
 {
pthread_join(w[i],NULL); 
 }
 for(j=0;j<nr;j++)
 {
pthread_join(r[j],NULL);
 }

 return 0;
}

void *reader(void *arg)
{
 FILE *fptr;
 sem_wait(&x);
 rc++;
 if(rc==1)
  sem_wait(&wsem);
 sem_post(&x);

 printf("\nreader %d:",arg);
 fptr=fopen("temp.txt","r+");
 while(fgets(str,10,fptr)!=NULL)
 {
  printf("%s",str);
 }
 printf("\n");
 fclose(fptr);
 sem_wait(&x);
 rc--;
 if(rc==0)
    sem_post(&wsem);
 sem_post(&x);
}


void *writer(void *arg)
{
 FILE *fptr1;
 sem_wait(&wsem);  
 printf("\nwriter-%d:\n",arg);
 fptr1=fopen("temp.txt","a+");
 printf("enter the string:");
 scanf("%s",str);
 fputs(str,fptr1);
 fclose(fptr1);  
 sem_post(&wsem);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接