为什么我在使用以下代码时会出现“double free or corruption”错误?

3

我正在编写一个简单的程序,以确保我在学习哈佛大学的CS50课程时充分理解C语言中指针的工作原理。以下是代码:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int *a = malloc(sizeof(int));
    *a = 5;
    printf("the address of pointer a is: %p\n", &a);
    printf("the address that a is pointing to is: %p\n", a);
    printf("the contents of a are: %i\n", *a);

    printf("please enter a new value for a: \n");
    scanf("%i", a);
    printf("the address of pointer a is: %p\n", &a);
    printf("the address that a is pointing to is: %p\n", a);
    printf("the contents of a are: %i\n", *a);

    int *b = malloc(sizeof(int));
    *b = 7;
    printf("the address of pointer b is: %p\n", &b);
    printf("the address that b is pointing to is: %p\n", b);
    printf("the contents of b are: %i\n", *b);

    a = b;
    printf("setting a = b\n");
    printf("the address of pointer a is: %p\n", &a);
    printf("the address that a is pointing to is: %p\n", a);
    printf("the contents of a are: %i\n", *a);

    free(a);
    free(b);
}

代码编译没有问题,但执行时出现以下错误:"* Error in `./address': double free or corruption (fasttop): 0x00000000018b7030 * Aborted"

如果我删除free(a)或free(b)语句之一,则此问题消失,但valgrind显示存在内存泄漏: "==9626== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==9626== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==9626== by 0x4206AD: main (address.c:6)"

我查看了其他问题和提到double free corruption的其他网站,但仍然找不出问题所在... 我感觉答案很简单,问题可能在于"a = b"语句,但我真的不明白为什么一个指针不能指向另一个指针所指向的内存位置,然后释放两个指针占用的内存...


1
也许你想要的不是赋值语句 a = b;,而是 *a = *b; — 这两者有很大的区别,而第二种写法(带有 *)不会引起问题。它将 b 指向的值分配给 a 指向的内存。 - Jonathan Leffler
关于:“它可以编译通过”这是不正确的。在编译时,始终启用警告,然后修复这些警告。(对于gcc,至少使用:-Wall -Wextra -pedantic -Wconversion -std=gnu17)请注意,其他编译器具有执行相同功能的不同选项。 - user3629249
它能干净地编译吗?不行。编译器对每3个printf()调用中的2个输出警告。这(可能)不会阻止代码正常工作,但它肯定不是没有问题的代码。 - user3629249
在调用任何堆分配函数(malloccallocrealloc)时,始终检查返回的值是否为(!=NULL),以确保操作成功。 - user3629249
建议将 -std=gnu17 更改为 -std=gnu11,因为 C17 标准尚未在所有地方(包括现在)得到支持。 - user3629249
显示剩余3条评论
3个回答

6
你正在做这件事:
 a = b; // a now points to the same thing as b does

...

free(a);
free(b);

...它在语义上等同于

free(b);
free(b);

这是双重释放 - b被释放两次 - 在C语言中是不允许的。


你的内存泄漏问题:

当你设置 a = b 时,你失去了a的原始值。 a是指向用int *a = malloc(sizeof(int));分配的内存的指针,现在已经丢失了。 如果你想valgrind停止投诉泄漏,你需要保存该地址并在退出之前传递给free()

对于每个由malloc()返回的地址(除了零... ^ _ ^),你应该使用相同的地址作为参数调用free()


1
有道理,谢谢!创建一个临时变量来存储a的原始值,然后释放它可以解决内存泄漏问题。 - Matvey
@Matvey 不客气 :) 如果你想给我一些信用,请随意接受我的答案。 - Morten Jensen

4

让我们把您的代码精简到最少:

#include <stdlib.h>

int main(void)
{
    int *a = malloc(sizeof(int));
    int *b = malloc(sizeof(int));

    a = b; // after the assignment the pointer value previously held by a is gone.
           // both pointers point to the same memory. The memory previously pointed
           // to by a can no longer be free()d since its address is gone.

    free(a); // free the memory pointed to by b (a is a copy of b) once
    free(b); // free the memory pointed to by b twice --> boom.
}

1
当你做这个时:

当你这样做:

 a = b;

你实际上是让 ab 指向相同的地址(也就是说,它们是同一个指针)。这就是导致错误的原因:
free(a); // Frees the location where b points to
free(b);

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