2

考虑以下 C 代码:

int main(){  
    int* c;  
    c = (int*)malloc(sizeof(int));  
    c = 0xdeadbeef;  
    free(c);  
    return 0;  
}

这将出现段错误,因为您正在尝试释放 c,这不是以前 malloc'ed 的东西。我的问题是我刚刚分配的块会发生什么?显然 c 不再指向它,所以它不能被使用,但它仍然被认为是“空闲”列表的一部分,或者这是一个显式的内存泄漏?

4

7 回答 7

12

这是一个泄漏。当您的程序终止时,它会被操作系统回收。

于 2009-04-10T21:00:07.200 回答
10

内存仍然被分配,导致内存泄漏。你想要其他方式吗?机器/编译器确实没有办法知道您分配的内存应该被回收。如果这不是正确的行为,您的代码将按概率运行:您永远无法真正信任该代码。

您可以在将来的某个时间重新指向该内存块,因此自动释放它会将地毯从您身下拉出来。

于 2009-04-10T21:02:50.627 回答
1

c = 7;假设您更改为,该代码不会出现段错误*c = 7;

于 2009-04-10T21:01:59.193 回答
1

好吧,imo,此时出现的段错误不是因为您尝试释放以前未分配的内存,而是因为您尝试引用操作系统未授予您权限的内存地址而发生段错误(这是分段错误的定义)。

一些实验,比如你会在 valgrind 中运行你的示例代码,你会得到这个作为输出:

    ==6945== 无效的 free() / delete / delete[]
    ==6945== 在 0x402265C: 空闲 (vg_replace_malloc.c:323)
    ==6945== by 0x80483D5: main (bla.c:6)
    ==6945== 地址 0x7 没有被堆栈、malloc 或(最近)释放
    ==6945==
    ==6945== 错误摘要:来自 1 个上下文的 1 个错误(抑制:来自 1 个的 11 个)
    ==6945== malloc/free:退出时使用:1 个块中的 4 个字节。
    ==6945== malloc/free:1 个分配,1 个释放,4 个字节分配。

所以这是一个内存泄漏'pur sang'。现在假设您将更改代码,以便您尝试释放的指针“靠近”您分配的指针(因此操作系统仍然知道您可以访问它,操作系统不会授予字节边界上的内存访问权限)。假设我们像这样修改代码:

int main(){  
    int* c;  
    c = (int*)malloc(sizeof(int));  
    c++;
    free(c);  
    return 0;  
}

运行此应用程序时,您不会再收到分段错误(由内核发出),而是来自 glibc( malloc() 和 free() 的所有者)的警告

    edb@Flying-Spaghetti-Monster:/tmp$ ./a.out
    *** 检测到 glibc *** ./a.out: free(): 无效指针:0x0804a00c ***
    ...接着是一个痕迹

所以你试图释放一些内核知道它属于你但glibc不记得把它交给你的内存。如果您在 valgrind 中运行它(通过替换 libc 中的 free()、malloc()、realloc()、... 函数并自行执行记帐),您将得到以下输出:

    ==6955== 无效的 free() / delete / delete[]
    ==6955== 在 0x402265C: 空闲 (vg_replace_malloc.c:323)
    ==6955== by 0x80483D2: main (bla.c:5)
    ==6955== 地址 0x418a02c 在大小为 4 的块分配后为 0 字节
    ==6955== 在 0x4022AB8: malloc (vg_replace_malloc.c:207)
    ==6955== by 0x80483C0: main (bla.c:3)
    ==6955==
    ==6955== 错误摘要:来自 1 个上下文的 1 个错误(抑制:来自 1 个的 11 个)
    ==6955== malloc/free:退出时使用:1 个块中的 4 个字节。
    ==6955== malloc/free:1 个分配,1 个释放,4 个字节分配。
于 2009-04-11T09:23:13.623 回答
1

在 malloc() 之后,c是一个保存内存地址的变量。在这种情况下,c具有您分配的第一个字节的内存地址的值。

当您说时c = 7,您是在说“c现在指向内存地址 '7'”。因为,在这种情况下,内存地址“7”没有分配给你的进程,你不能 free() 它。从理论上讲,内存地址“7”(或 0xa73c930bf,或您设置的任何内容)确实可能已分配给您的进程,在这种情况下 free() 将成功。

但是,在更改 c 的值之后,内存仍然被分配。您的进程仍然拥有内存及其数据 - 您刚刚丢失了指针。你不知道那段记忆从哪里开始。

这是一个好的情况。在 C 中,您可能有不同的变量指向该内存地址。您甚至可能有一个“int”,而不是一个“int*”来存储该内存地址。C 不会尝试跟踪您是否存储了那个特定的内存地址——这实际上只是一个数字。这样做将是一项不可能完成的任务,因为任何保持跟踪的尝试都需要失去 C 指针提供的一些灵活性。

因此,回答您的问题:由于您无法跟踪 c 的值,因此该程序存在内存泄漏。我的意思是你的内存不可能被你的程序使用,因此浪费了资源。

但是,当程序退出时,分配给您的进程的所有内存都将被释放并可供其他程序使用。

于 2009-04-10T21:17:22.743 回答
1

请不要喷我,但我不清楚你的问题的重点是什么。很明显,你正在做的事情与语言打算让你做的事情直接冲突。这相当于说“如果我用灭火器液填充汽车的油箱会发生什么,只是因为灭火器的喷嘴恰好适合油箱孔”。
我不是想成为一个混蛋,我只是不清楚为什么这个特殊问题?我可以想出无穷无尽的问题,这些问题利用了指针的力量,想知道有多少种方法会错误地使用它们,从而导致应用程序失败。如果您能找到正确的方法,您是否正在尝试完成您的代码*会做的事情?或者您想知道是否有某种内部机制可以跟踪您的指针并在您不小心丢失该内存时帮助您回收该内存?(如果是这样,上面已经回答了这个问题)。

于 2009-04-10T21:28:19.787 回答
0

请注意,返回的值malloc()不需要强制转换,因为转换void*->int*是自动的。

您还可以像这样重写调用:

c = malloc(sizeof *c);

所以现在如果你改变c's 的类型,你根本不需要重写分配。

[编辑]

检查分配是否成功(即c != NULL)也是一个好主意。

于 2009-04-10T21:08:29.100 回答