是什么让glibc malloc能够比较来自不同"对象的指针"?

时间:2016-11-25 16:53:43

标签: c malloc language-lawyer glibc

指针与关系运算符(例如<<=>=>)之间的比较仅在指针指向内部时由C标准定义相同的聚合对象(struct,array或union)。这在实践中意味着比较

的形状
if (start_object <= my_pointer && my_pointer < end_object+1) {

可以变成

if (1) {

通过优化编译器。尽管如此,在K&amp; R,第8.7节&#34;示例-A存储分配器&#34;中,作者进行了与上述类似的比较。他们通过说

来原谅这一点
  

然而,仍有一个假设是,可以有意义地比较sbrk返回的不同块的指针。标准不保证这一点,它只允许在数组中进行指针比较。因此,malloc的这个版本只能在一般指针比较有意义的机器中移植。

此外,似乎the implementation of malloc used in glibc做了同样的事情!

更糟糕的是 - 我偶然发现这一点的原因是 - 对于学校作业,我应该实施一个基本的malloc类似的功能,以及作业的说明要求我们使用K&amp; R代码,但我们必须通过调用sbrk替换mmap来电!

虽然比较来自不同sbrk调用的指针可能是未定义的,但它也只是略微可疑,因为你有某种心理直觉,返回的指针应该来自同一个内存区域。根据我的理解,不同mmap次呼叫返回的指针 no 保证甚至彼此远程相似,并且跨mmap调用整合/合并内存块应该是非常非法的(并且看起来glibc避免了这种情况,仅仅合并sbrkmmap页面内部而不是整个页面内部返回的内存,但是分配需要这样做。

问题:可能有人对此有所启发

  1. 是否可以优化将来自sbrk的不同来电的指针进行比较,
  2. 如果是这样,glibc会让他们侥幸逃脱。

3 个答案:

答案 0 :(得分:3)

答案很简单。根据官方规范,C库实现是根据(或可能期望)C编译器如何处理具有未定义行为的某些代码的一些知识编写的。

我可以举出很多例子;但是这些指针实际上是指过程中的一个地址&#39;地址空间可以自由比较是由C库实现(至少由Glibc)以及许多&#34;现实世界&#34;程式。虽然严格符合程序的标准不能保证,但绝大多数真实世界的架构/编译器都是如此。另请注意脚注67,关于将指针转换为整数和后退:

  

用于将指针转换为整数或。的映射函数   整数到指针的目的是与寻址一致   执行环境的结构。

虽然这并没有严格授予比较任意指针的许可,但它有助于理解规则应该如何工作:作为一组特定行为,一定要在所有平台上保持一致,而不是作为一个限制指针表示完全已知和理解时允许的内容。

你已经说过:

if (start_object <= my_pointer && my_pointer < end_object+1) {

可以变成:

if (1) {

假设(你没有说明)my_pointer以某种方式从start_object的值或它划分的对象的地址派生 - 那么这是严格的是的,但它并不是编译器在实践中所做的优化,除非是静态/自动存储持续时间对象(即编译器知道没有动态分配的对象)。

答案 1 :(得分:2)

考虑到sbrk的调用被定义为递增或递减某些区域(堆)中分配的字节数,对于某些进程,由给定的incr参数根据某些{ {1}}地址。这实际上只是brk的包装器,它允许您调整堆的当前顶部。当你调用brk时,你告诉内核从brk(addr)的底部一直为你的进程分配空间(或者可能是当前前一个更高地址之间的空闲空间)堆积到新地址)。如果addrsbrk(incr)将完全等效。这样回答你的问题:

  1. 因为incr == new_top - original_top只是调整堆的大小(即一些连续的内存区域)sbrk个字节数,所以比较incr的值只是一个比较在一些连续的记忆区域中的点数。这完全等同于比较数组中的点,因此它是根据C标准定义良好的操作。因此,可以优化sbrk周围的指针比较调用。

  2. sbrk没有做任何特别的事情来“逃避它” - 他们只是假设上面提到的假设是正确的(它确实如此)。实际上,如果他们正在检查分配了glibc的内存的块状态,那么explicitly verifies内存mmap'内存超出了{{1}分配的范围}}

  3. 编辑:我想让我的答案更清楚:这里的关键是没有未定义的行为! mmap被定义为在某个连续的内存区域中分配字节,这本身就是C标准指定的“对象”。因此,比较“对象”中的指针是一个完全理智且定义明确的操作。这里的假设不是glibc利用未定义的指针比较,而是假设sbrk在某个连续区域中增长/缩小内存。

答案 2 :(得分:0)

C标准的作者认识到存在一些分段存储器硬件平台,其中尝试在不同段中的对象之间执行关系比较可能表现奇怪。而不是说这些平台无法有效地容纳高效的C实现,标准的作者允许这样的实现做任何他们认为合适的事情,如果尝试将指针与可能在不同段中的对象进行比较。

对于标准的作者来说,不相交的对象之间的比较应该只在这样的分段记忆系统上表现出奇怪的行为,而这种行为不能有效地产生一致的行为,这被认为暗示这样的系统不如任意指针之间的关系比较将产生一致排名的平台,标准的作者不遗余力地避免这种影响。相反,他们认为,由于没有理由针对普通平台实现针对这种比较做任何奇怪的事情,这样的实现会合理地处理它们是否标准强制要求它们。

不幸的是,有些人对制作符合标准的编译器比制作符合标准的编译器更感兴趣的人决定使用任何编写的代码来适应硬件的限制。几十年来已经过时的应该被考虑&#34;破坏&#34;。他们声称他们的优化&#34;允许程序比其他方式更有效,但在许多情况下,效率&#34;只有在编译器省略实际需要的代码的情况下,增益才有意义。如果程序员解决了编译器的限制,那么生成的代码可能最终效率低于编译器没有使用&#34;优化&#34;首先。