需要明确的是:我知道mallocfree是在C库中实现的,C库通常从操作系统分配内存块,并自行管理将较小的内存分配给应用程序,并跟踪分配的字节数.这个问题不是How does free know how much to free.

更确切地说,我想知道为什么free最初是这样制作的.作为一种低级语言,我认为要求C程序员不仅要跟踪分配了什么内存,还要跟踪分配了多少内存是完全合理的(事实上,我通常发现我最终还是要跟踪malloced的字节数).我还想到,显式地将字节数指定为free可能会允许一些性能优化,例如,对于不同的分配大小,具有不同池的分配器将能够通过查看输入参数来确定要释放的池,并且总体上会减少空间开销.

那么,简而言之,为什么要创建mallocfree,以便它们需要在内部跟踪分配的字节数?这只是一个历史性的意外吗?

小编辑:

推荐答案

与前面两个参数mfree(void *, size_t)相比,一个参数free(void *)(在Unix V7中引入)还有另一个主要优势,我在这里没有提到这两个参数:一个参数free大大简化了与堆内存一起工作的每个other API.例如,如果free需要内存块的大小,那么strdup将不得不以某种方式返回两个值(指针+大小),而不是一个值(指针),而C使多值返回比单值返回更麻烦.我们得写char *strdup(char *, size_t *)struct CharPWithSize { char *val; size_t size}; CharPWithSize strdup(char *),而不是char *strdup(char *).(如今,第二种 Select 看起来很诱人,因为我们知道以NUL结尾的字符串是"most catastrophic design bug in the history of computing",但这是事后诸葛亮的说法.早在70年代,C作为简单的char *处理字符串的能力实际上被认为是defining advantage over competitors like Pascal and 算法l.)此外,不仅strdup会受到这个问题的影响——它还会影响分配堆内存的每个系统或用户定义的函数.

早期的Unix设计者都是非常聪明的人,freemfree好有很多原因,所以基本上我认为问题的答案是,他们注意到了这一点,并据此设计了自己的系统.我怀疑你能在他们做出决定的那一刻,在他们的头脑中找到任何直接的记录.但我们可以想象.

假设您正在用C语言编写应用程序,以便在带有两个参数mfree的V6Unix上运行.到目前为止,您处理得还不错,但是跟踪这些指针大小正变得越来越麻烦,因为您的程序become more ambitious并且需要越来越多地使用堆分配的变量.但是,您有了一个绝妙的 idea :您可以只编写一些实用函数,将大小直接存储在分配的内存中,而不是一直复制这size_t:

void *my_alloc(size_t size) {
    void *block = malloc(sizeof(size) + size);
    *(size_t *)block = size;
    return (void *) ((size_t *)block + 1);
}
void my_free(void *block) {
    block = (size_t *)block - 1;
    mfree(block, *(size_t *)block);
}

使用这些新函数编写的代码越多,它们看起来就越棒.它们不仅让你的代码更容易编写,还让你的代码变成了faster——这两件事通常不会同时发生!在你到处传递这些size_t之前,这增加了复制的CPU开销,意味着你不得不更频繁地溢出寄存器(尤其是对于额外的函数参数),浪费内存(因为嵌套函数调用通常会导致size_t的多个副本存储在不同的堆栈帧中).在您的新系统中,您仍然需要花费内存来存储size_t,但只需一次,而且它永远不会被复制到任何地方.这些可能看起来效率很低,但请记住,我们谈论的是256kib内存的高端机器.

这让你很开心!所以你和那些正在为下一个Unix版本工作的留着胡子的人分享你的酷招,但这并没有让他们高兴,反而让他们难过.您看,他们正在添加一系列新的实用函数,比如strdup,他们意识到使用您的酷把戏的人将无法使用他们的新函数,因为他们的新函数都使用笨重的指针+大小API.这也会让你很难过,因为你会意识到你必须在你写的每个程序中自己重写Good strdup(char *)函数,而不是能够使用系统版本.

但是等等!这是1977年,向后兼容性将在future 5年内发明!除此之外,没有一个严肃的人真的用这个不起眼的名字来形容这个晦涩难懂的"Unix"东西.《K&;R现在正在go 出版商的路上,但这没有问题——它在第一页上说,"C不提供直接处理字符串等复合对象的操作……没有堆……".在历史的这一点上,string.hmalloc是供应商扩展(!).因此,留胡子的男人#1建议,我们可以随心所欲地改变它们;我们为什么不宣布你的复杂分配器为official分配器呢?

几天后,大胡子男人2看到了新的API,说嘿,等等,这比以前好了,但它仍然在每一次分配中花费整整一个字来存储大小.他认为这是下一件亵渎神明的事.其他人都看着他,好像他疯了一样,因为你还能做什么?那天晚上,他熬夜并发明了一个新的分配器,它根本不存储大小,而是通过对指针值执行黑魔法位移来动态推断大小,并在保持新API的同时交换它.新的API意味着没有人注意到switch ,但他们确实注意到第二天早上编译器使用的RAM减少了10%.

现在每个人都很高兴:你的代码写得更容易,速度更快,大胡子男人#1可以写一个很好的简单的strdup,人们会实际使用,而大胡子男人#2——相信他已经赚了一点钱——回到了messing around with quines.运走!

至少,事情就是这样发生的.

C++相关问答推荐

strftime函数中%s的历史意义是什么?为什么没有记录?

Mise()在虚拟内存中做什么?

堆栈在作用域阻塞后会被释放吗?

C:二进制搜索和二进制插入

为什么内核使用扩展到前后相同的宏定义?

在C++中使用函数指针的正确语法

SSH会话出现意外状态

如何确保在C程序中将包含uft8字符的字符串正确写入MySQL?

为什么双精度d=flt_max+flt_max;在c语言中得到inf的结果

==284==错误:AddressSaniizer:堆栈缓冲区下溢

将回调/基于事件的C API转换为非回调API

如何在c中使用具有不同变量类型的内存分配?

为什么我无法访问C语言中的文件

如何修复我的qsort()算法?它每次都给出不同的结果

*S=0;正在优化中.可能是GCC 13号虫?或者是一些不明确的行为?

'printf("%s", user_input)' 危险吗?

函数的typedef是标准 C 语法吗?它与函数指针的typedef有何不同?

memcmp 是否保证按顺序比较字节?

为什么创建局部变量的指针需要过程在堆栈上分配空间?

我怎样才能用c语言正常运行这两个进程?