为什么`free`用C不采取的字节数被释放? [英] Why does `free` in C not take the number of bytes to be freed?

查看:158
本文介绍了为什么`free`用C不采取的字节数被释放?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

只是要清楚:我知道,的malloc 免费在C库中,这通常被实现从OS分配的存储器大块,并没有其自身的管理,以包裹出小批量的存储器的应用和跟踪分配的字节的数目。这个问题是不是如何免费知道多少自由

Just to be clear: I do know that malloc and free are implemented in the C library, which usually allocates chunks of memory from the OS and does its own management to parcel out smaller lots of memory to the application and keeps track of the number of bytes allocated. This question is not How does free know how much to free.

相反,我想知道为什么免费作出这样摆在首位。作为一个低层次的语言,我觉得这是完全合理的要求C程序员跟踪不仅被分配什么样的内存,但有多少(其实,我经常发现,我最终保留的字节数的轨道反正malloced)。这也发生在我明确给予的字节数为免费可能会允许一些性能优化技术,例如具有单独的池为不同分配大小分配器将能够确定从释放该池仅通过查看输入参数,并且将有整体空间开销更少。

Rather, I want to know why free was made this way in the first place. Being a low-level language, I think it would be perfectly reasonable to ask a C programmer to keep track not only of what memory was allocated but how much (in fact, I commonly find that I end up keeping track of the number of bytes malloced anyway). It also occurs to me that explicitly giving the number of bytes to free might allow for some performance optimisations, e.g. an allocator that has separate pools for different allocation sizes would be able to determine which pool to free from just by looking at the input arguments, and there would be less space overhead overall.

因此​​,简而言之,为什么是的malloc 免费创建,使得他们必须在内部跟踪字节数的分配?难道仅仅是历史的偶然?

So, in short, why were malloc and free created such that they're required to internally keep track of the number of bytes allocated? Is it just a historical accident?

一个小编辑:
有几个人提供点类似如果你释放比你分配什么不同数量的什么。我的想象的API可以简单地需要一个准确地释放分配的字节数;释放或多或少可能仅仅是UB或实现定义。我不想阻碍对其他可能性的讨论,虽然。

A small edit: A few people have provided points like "what if you free a different amount than what you allocated". My imagined API could simply require one to free exactly the number of bytes allocated; freeing more or less could simply be UB or implementation defined. I don't want to discourage discussion about other possibilities, though.

推荐答案

一参数免费(无效*)(在UNIX中V7中引入)拥有的另一大优势早期的两个参数 mfree(无效*,为size_t)我还没有看到这里提到的:一个参数免费显着简化了与堆内存的每一个工作的其他的API。例如,如果免费所需的内存块的大小,那么的strdup 会以某种方式不得不返回两个值(指针+)的大小,而不是一(指针),和C使得多值的回报远远超过单值回报较为繁琐。取而代之的的char *的strdup(字符*)我们不得不写的char *的strdup(字符*,为size_t *)或者结构CharPWithSize {字符* VAL;为size_t大小}; CharPWithSize的strdup(字符*)。 (时下第二个选项看起来pretty诱人​​的,因为我们知道,NUL结尾的字符串是的最具灾难性的设计缺陷在计算史,但是这事后说,早在上世纪70年代,C的处理字符串作为一个简单的的char *能力实际上认为是定义过像Pascal和陵竞争对手的优势。)另外,它不仅仅是的strdup 从这个问题的困扰 - 它影响到每一个系统或用户定义的函数分配堆内存

One-argument free(void *) (introduced in Unix V7) has another major advantage over the earlier two-argument mfree(void *, size_t) which I haven't seen mentioned here: one argument free dramatically simplifies every other API that works with heap memory. For example, if free needed the size of the memory block, then strdup would somehow have to return two values (pointer + size) instead of one (pointer), and C makes multiple-value returns much more cumbersome than single-value returns. Instead of char *strdup(char *) we'd have to write char *strdup(char *, size_t *) or else struct CharPWithSize { char *val; size_t size}; CharPWithSize strdup(char *). (Nowadays that second option looks pretty tempting, because we know that NUL-terminated strings are the "most catastrophic design bug in the history of computing", but that's hindsight speaking. Back in the 70's, C's ability to handle strings as a simple char * was actually considered a defining advantage over competitors like Pascal and Algol.) Plus, it isn't just strdup that suffers from this problem -- it affects every system- or user-defined function which allocates heap memory.

在早期的Unix的设计师是非常聪明的人,有很多原因免费 mfree 更好,所以基本上我认为这个问题的答案是,他们注意到了这一点,并相应设计他们的系统。我怀疑你会发现什么是在他们作出这一决定的那一刻头脑里想的任何直接记录。但我们可以想像的。

The early Unix designers were very clever people, and there are many reasons why free is better than mfree so basically I think the answer to the question is that they noticed this and designed their system accordingly. I doubt you'll find any direct record of what was going on inside their heads at the moment they made that decision. But we can imagine.

这是你用C编写的应用程序在V6 Unix上运行,与它的两个参数pretend mfree 。你没事管理,到目前为止,但这些指针大小的跟踪正在成为一个麻烦,越来越多的为你的程序变得​​更雄心勃勃并需要越来越多地使用堆分配变量。但你有一个绝妙的主意:不是围绕着这些复制为size_t S中的时候,你可以写一些实用功能,这直接藏匿的大小分配的内存里面:

Pretend that you're writing applications in C to run on V6 Unix, with its two-argument mfree. You've managed okay so far, but keeping track of these pointer sizes is becoming more and more of a hassle as your programs become more ambitious and require more and more use of heap allocated variables. But then you have a brilliant idea: instead of copying around these size_ts all the time, you can just write some utility functions, which stash the size directly inside the allocated memory:

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);
}

和你写的使用这些新功能的更多code,更真棒他们似乎。他们不仅让你的code更容易编写,它们的使你的code的更快的 - 两件事情不经常一起去!你路过这些为size_t s左右所有的地方,它增加了CPU的开销进行复制,并意味着之前,你不得不更经常溢出寄存器(尤指的额外功能参数),和浪费的存储器(因为嵌套函数调用将通常导致多拷贝的为size_t 被存储在不同的堆栈帧)。在新的系统,你还是得花内存来存储为size_t ,但只有一次,而且它永远不会在任何地方复制。这些看似小的效率,但要以我们正在谈论的RAM 256 KIB高端​​机的想法。

And the more code you write using these new functions, the more awesome they seem. Not only do they make your code easier to write, they also make your code faster -- two things which don't often go together! Before you were passing these size_ts around all over the place, which added CPU overhead for the copying, and meant you had to spill registers more often (esp. for the extra function arguments), and wasted memory (since nested function calls will often result in multiple copies of the size_t being stored in different stack frames). In your new system, you still have to spend the memory to store the size_t, but only once, and it never gets copied anywhere. These may seem like small efficiencies, but keep in mind that we're talking about high-end machines with 256 KiB of RAM.

这让你开心!所以,你与谁是下一个版本的Unix工作的大胡子男人分享您的酷技巧,但它并不能让他们高兴,这让他们伤心。你看,他们只是在添加一些新的实用功能如的strdup 的过程中,他们认识到,使用酷技巧的人将无法使用他们的新的功能,因为他们的新功能都使用繁琐的指针+大小API。然后让你伤心过,因为你意识到你必须重写好的strdup(字符*)函数自己在每次你写的程序,而不是能够使用的系统版本。

This makes you happy! So you share your cool trick with the bearded men who are working on the next Unix release, but it doesn't make them happy, it makes them sad. You see, they were just in the process of adding a bunch of new utility functions like strdup, and they realize that people using your cool trick won't be able to use their new functions, because their new functions all use the cumbersome pointer+size API. And then that makes you sad too, because you realize you'll have to rewrite the good strdup(char *) function yourself in every program you write, instead of being able to use the system version.

别急!这是1977年,和向后兼容性将不会被发明了另外5年!除此之外,没有人真正严重的使用的这种模糊的UNIX的东西与它的断色的名称。 K&放大器的第一版; R是在它的途中到现在为止发行,但是这没有问题 - 它说正确的第一页是C不提供任何操作直接复合对象,如字符串处理上...有没有堆......。在这一历史时刻,文件string.h 的malloc 是供应商扩展(!)。因此,建议大胡子男人#1,我们可以但我们喜欢改变他们;为什么我们不只是声明你的棘手分配器是的官方的分配?

But wait! This is 1977, and backwards compatibility won't be invented for another 5 years! And besides, no-one serious actually uses this obscure "Unix" thing with its off-color name. The first edition of K&R is on its way to the publisher now, but that's no problem -- it says right on the first page that "C provides no operations to deal directly with composite objects such as character strings... there is no heap...". At this point in history, string.h and malloc are vendor extensions (!). So, suggests Bearded Man #1, we can change them however we like; why don't we just declare your tricky allocator to be the official allocator?

几天后,大胡子男人#2看到了新的API,并说嘿,等等,这是比以前好多了,但它仍然花费每个分配一个完整的字存储大小。他认为,这是接下来要做的亵渎。其他人看着他,好像他是疯了,因为你还能做什么?那天晚上,他留晚,发明了不存储大小在所有新的分配,而是推断它通过对指针值执行魔法bitshifts飞,并交换它同时使新的API到位。新的API意味着,没有人注意到了开关,但他们注意到,第二天早上,编译器将使用10%以下的RAM。

A few days later, Bearded Man #2 sees the new API and says hey, wait, this is better than before, but it's still spending an entire word per allocation storing the size. He views this as the next thing to blasphemy. Everyone else looks at him like he's crazy, because what else can you do? That night he stays late and invents a new allocator that doesn't store the size at all, but instead infers it on the fly by performing black magic bitshifts on the pointer value, and swaps it in while keeping the new API in place. The new API means that no-one notices the switch, but they do notice that the next morning the compiler uses 10% less RAM.

而现在每个人的快乐:你得到你更易写入和更快的code,大胡子男人#1获取写一个不错的简单的strdup ,人们会实际使用,以及大胡子男人#2 - 相信他赢得了他保持了位 - 要追溯到与基内斯瞎搞。船舶它!

And now everyone's happy: You get your easier-to-write and faster code, Bearded Man #1 gets to write a nice simple strdup that people will actually use, and Bearded Man #2 -- confident that he's earned his keep for a bit -- goes back to messing around with quines. Ship it!

或者至少,这是怎么回事的可能的发生。

Or at least, that's how it could have happened.

这篇关于为什么`free`用C不采取的字节数被释放?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆