我意识到下面的代码示例是你永远不应该做的事情。我的问题只是一个有趣的问题。如果你分配一块内存,然后移动指针(一个禁忌),当你释放内存时,释放的块的大小是多少,内存在哪里?这是人为的代码片段:
#include <stdio.h>
#include <string.h>
int main(void) {
char* s = malloc(1024);
strcpy(s, "Some string");
// Advance the pointer...
s += 5;
// Prints "string"
printf("%s\n", s);
/*
* What exactly are the beginning and end points of the memory
* block now being deallocated?
*/
free(s);
return 0;
}
这就是我认为我发生的事情。被释放的内存块以保存字符串“s”的字节开头。持有“Some”的5个字节现在丢失了。
我想知道的是:在内存中的位置紧跟在原始1024字节的末尾之后的5个字节是否已经解除分配,或者它们是否只是单独存在?
任何人都知道编译器的作用是什么?这是不确定的?
感谢。
答案 0 :(得分:18)
您无法将未从malloc
,calloc
或realloc
获取的指针传递给free
(NULL
除外)。
Question 7.19与您的问题相关。
答案 1 :(得分:7)
这是标准中未定义的行为,因此您不能依赖任何内容。
请记住,块是人为分隔的内存区域,并不是自动的 出现。有必要跟踪块,以释放所有必要的东西,仅此而已。没有可能的终止,比如C字符串,因为没有值或值的组合可以保证不在块内。
最后我看,有两种基本的实施方法。
一种方法是保留已分配块的单独记录以及分配的地址。 free()函数查找块以查看要释放的内容。在这种情况下,它可能根本找不到它,并且很可能什么都不做。内存泄漏。但是,没有任何保证。
一种方法是在分配地址之前将块信息保存在内存的一部分中。在这种情况下,free()使用块的一部分作为块描述符,并且根据存储的内容(可能是任何内容),它将释放一些东西。它可能是一个太小的区域,也可能是一个太大的区域。堆腐败的可能性很大。
所以,我希望内存泄漏(没有任何内容被释放)或堆损坏(太多被标记为空闲,然后重新分配)。
答案 2 :(得分:5)
是的,这是未定义的行为。你基本上是在释放一个没有malloc
的指针。
答案 3 :(得分:4)
您无法将未从malloc
(或calloc
或realloc
...)获得的指针传递给free
。这包括您从malloc
获得的块的偏移量。违反此规则可能会导致发生任何。通常情况下,这最终可能是在最糟糕的时刻出现的最糟糕的可能性。
进一步说明,如果你想截断这个块,有一个合法的方法可以做到这一点:
#include <stdio.h>
#include <string.h>
int main() {
char *new_s;
char *s = malloc(1024);
strcpy(s, "Some string");
new_s = realloc(s, 5);
if (!new_s) {
printf("Out of memory! How did this happen when we were freeing memory? What a cruel world!\n");
abort();
}
s = new_s;
s[4] = 0; // put the null terminator back on
printf("%s\n", s); // prints Some
free(s);
return 0;
}
realloc
可用于放大和缩小内存块,但可能(或可能不)移动内存来执行此操作。
答案 4 :(得分:2)
它不是编译器,它是标准库。行为未定义。图书馆知道它已将原始s
分配给您。 s+5
未分配给库已知的任何内存块,即使它恰好位于已知块内。所以,它不会起作用。
答案 5 :(得分:2)
我想知道的是:在内存中的位置紧跟在原始1024字节的末尾之后的5个字节是否已经解除分配,或者它们是否只是单独存在?
两者。结果是未定义的,因此编译器可以自由地执行其中任何一个,或者他们真正喜欢的任何其他内容。当然(对于特定平台和编译器的所有“未定义行为”的情况),都有一个特定的答案,但任何依赖这种行为的代码都是一个坏主意。
答案 6 :(得分:2)
对未被malloc或其兄弟分配的ptr调用free()是未定义的。
malloc的大多数实现在ptr返回之前立即分配一个小的(通常是4byte)头区域。这意味着当你分配1024个字节时,malloc实际上保留了1028个字节。当调用free(ptr)时,如果ptr不为0,它将检查ptr-sizeof(header)中的数据。一些分配器实现了一个健全性检查,以确保它是一个有效的头,并且可能检测到一个坏的ptr,并断言或退出。如果没有进行健全性检查,或者错误地通过,则自由例程将对标题中出现的任何数据起作用。
答案 7 :(得分:1)
添加更正式的答案:我将这个问题的机制与在图书馆(malloc)中拿书的人进行比较,然后与封面一起撕下几十页(推进指针),然后尝试归还(免费)。
您可能会找到一本图书管理员(malloc /免费图书馆实施),这本书可以拿回这本书,但在很多情况下,我希望您会因疏忽处理而支付罚金。
在C99的选秀中(我没有最终的C99在我面前),在这个主题上有一些话要说:
free函数会导致
ptr
指向的空格被释放, 也就是说,可供进一步分配。如果ptr
是空指针,则不执行任何操作 发生。否则,如果参数与先前返回的指针不匹配 通过calloc
,malloc
或realloc
函数,或者空格如何 通过调用free
或realloc
取消分配,行为未定义。
根据我的经验,双重免费或没有通过malloc
返回的“指针”将导致内存损坏和/或崩溃,具体取决于您的malloc
实施。围栏两侧的安全人员不再使用此行为,至少在广泛使用的Doug Lea的malloc
包的早期版本中various interesting things。
答案 8 :(得分:0)
库实现可能会在返回给你的指针之前放置一些数据结构。然后在free()
中,它递减指针以获取数据结构,告诉它如何将内存放回到空闲池中。因此,字符串“Some”开头的5个字节被解释为struct
算法使用的malloc()
的结尾。也许是32位值的结束,如分配的内存大小,或链表中的链接。这取决于实施。无论细节如何,它都会让你的程序崩溃。正如思南指出的那样,如果你很幸运!
答案 9 :(得分:0)
让我们在这里变得聪明...... free()
不是黑洞。至少,你有CRT源代码。除此之外,您还需要内核源代码。
当然,行为未定义,因为由CRT / OS来决定做什么。但这并不妨碍你找出你的平台实际上做了什么。
快速浏览Windows CRT会显示free()
使用CRT特定堆直接HeapFree()
。 Beoyond你进入RtlHeapFree()
然后进入系统空间(NTOSKRN.EXE)与内存管理器Mm*()
。
在所有这些代码路径中都有一些检查。但是对内存执行不同的操作会导致不同的代码路径。因此,未定义的真正定义。
快速浏览一下,我可以看到分配的内存块最后有一个标记。释放存储器时,每个字节都用不同的字节写入。运行时可以检查是否覆盖了块标记的结尾,如果是,则引发异常。
在你的内存中释放几个字节(或覆盖你分配的大小)的情况下,这是一种可行性。当然你可以欺骗它并在正确的位置自己编写块标记的结尾。这将使您通过CRT检查,但随着代码路径的进一步发展,会出现更多未定义的行为。可能会发生以下三种情况:1)绝对无害,2)CRT堆内存损坏,或3)任何内存管理函数抛出异常。
答案 10 :(得分:-2)
简短版本:这是未定义的行为。
长版本:我检查了CWE site并发现,虽然这是一个坏主意,但没有人似乎有一个坚实的答案。可能是因为它未定义。
我的猜测是,大多数实现,假设它们不会崩溃,将释放1019个字节(在您的示例中),或者释放1024并在后五个中获得双倍或类似的。从理论上说现在,它取决于malloc例程的内部存储表是否包含地址和长度,或者起始地址和结束地址。
无论如何,这显然不是一个好主意。 : - )