与前面两个参数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设计者都是非常聪明的人,free
比mfree
好有很多原因,所以基本上我认为问题的答案是,他们注意到了这一点,并据此设计了自己的系统.我怀疑你能在他们做出决定的那一刻,在他们的头脑中找到任何直接的记录.但我们可以想象.
假设您正在用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.h
和malloc
是供应商扩展(!).因此,留胡子的男人#1建议,我们可以随心所欲地改变它们;我们为什么不宣布你的复杂分配器为official分配器呢?
几天后,大胡子男人2看到了新的API,说嘿,等等,这比以前好了,但它仍然在每一次分配中花费整整一个字来存储大小.他认为这是下一件亵渎神明的事.其他人都看着他,好像他疯了一样,因为你还能做什么?那天晚上,他熬夜并发明了一个新的分配器,它根本不存储大小,而是通过对指针值执行黑魔法位移来动态推断大小,并在保持新API的同时交换它.新的API意味着没有人注意到switch ,但他们确实注意到第二天早上编译器使用的RAM减少了10%.
现在每个人都很高兴:你的代码写得更容易,速度更快,大胡子男人#1可以写一个很好的简单的strdup
,人们会实际使用,而大胡子男人#2——相信他已经赚了一点钱——回到了messing around with quines.运走!
至少,事情就是这样发生的.