当前位置: 首页 > 知识库问答 >
问题:

是什么使glibc malloc能够比较来自不同“对象”的指针?

诸葛绍元
2023-03-14

将指针与关系运算符(例如<<==)进行比较,只有当指针都指向同一个聚合对象(结构、数组或联合)时,C标准才定义。在实践中,这意味着

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

可以变成

if (1) {

通过优化编译器。尽管如此,在K&R第8.7节“示例-一个存储分配器”中,作者做了与上面类似的比较。他们借口说

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

此外,glibc中使用的malloc的实现也做了同样的事情!

更糟糕的是--我一开始偶然发现这个的原因是--对于一个学校作业,我应该实现一个类似于malloc的基本函数,而作业的说明要求我们使用K&R代码,但是我们必须用对mmap的调用替换sbrk的调用!

虽然比较来自不同SBRK调用的指针可能是未定义的,但这也只是有点可疑,因为您有某种心理直觉,返回的指针应该来自某种相同的内存区域。据我所知,由不同的mmap调用返回的指针甚至不能保证彼此远程相似,并且跨mmap调用合并/合并内存块应该是非常非法的(glibc似乎避免了这一点,只需要合并由sbrk或在mmap页内部返回的内存,而不是跨页),但是分配需要这样做。

问题:有人能照点光吗

  1. 比较来自不同调用的指向SBRK的指针是否可以被优化,以及
  2. 如果是这样,glibc做了什么让他们逍遥法外。

共有1个答案

裘光启
2023-03-14

律师回答的语言(我相信)可以在C99标准§6.5.8.5中找到(或者更准确地说,来自ISO/IEC 9899:TC3委员会草案-2007年9月7日WG14/N1256,该草案几乎相同,但我手头没有原件),其中关于关系运算符(即<<=>=):

当比较两个指针时,结果取决于所指向的对象在地址空间中的相对位置。如果指向对象或不完整类型的两个指针都指向同一个对象,或者都指向同一个数组对象的最后一个元素,则它们比较相等。如果指向的对象是同一聚合对象的成员,则指向稍后声明的结构成员的指针比指向结构中先前声明的成员的指针大,指向下标值较大的数组元素的指针比指向下标值较低的同一数组元素的指针大。指向同一union对象成员的所有指针比较相等。如果表达式p指向数组对象的元素,而表达式q指向同一数组对象的最后一个元素,则指针表达式q+1的比较值大于p。在所有其他情况下,行为是未定义的。

(C11文本相同或接近相同)

这在一开始似乎没有帮助,或者至少表明每个实现都利用了未定义的行为。然而,我认为,你要么可以将这种行为合理化,要么可以使用一种方法。

指定的C指针要么是null,要么是从使用&获取对象地址派生的,要么是通过指针算术,或者是通过某个函数的结果派生的。在这种情况下,它们由SBRKmmap系统调用的结果派生。这些系统调用真正返回什么?在寄存器级别,它们返回大小为UINTPTR_T(或INTPTR_T)的整数。实际上是系统调用接口将它们强制转换为指针。正如我们所知,指针与UINTPTR_T(或INTPTR_T)之间的强制转换是双射型的定义,我们知道我们可以将指针强制转换到UINTPTR_T(例如)并对它们进行比较,这将对指针施加良好的顺序关系。wikipedia链接提供了更多的信息,但这实际上将确保每个比较都有很好的定义,以及其他有用的属性,如A B 暗示 A 。(我也不能选择完全任意的顺序,因为它需要满足C99§6.5.8.5的其他要求,这使得我只能选择 intptr_tuintptr_t。)

我们可以利用这一点来编写(可以说更好):

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

这里有个尼特。您将注意到,我将其种姓为uintptr_t而不是intptr_t。为什么那是正确的选择?事实上,我为什么不选择一个比较奇怪的顺序,比如反转位和比较?这里的假设是,我选择了与内核相同的顺序,特别是我对<的定义(由顺序给出)是这样的,即任何分配的内存块的开始和结束总是start 。在我所知道的所有现代平台上,都没有“环绕”(例如,内核不会分配从 0xFFFF8000开始到 0x00007FFFF结束的32位内存)--不过请注意,类似的环绕过去也曾被利用过。

C标准规定指针比较在许多情况下会给出未定义的结果。但是,这里您是从系统调用返回的整数中构建自己的指针。因此,您可以比较整数,或者通过将指针转换回整数来比较指针(利用转换的双重性)。如果只是比较指针,则依赖于C编译器对指针比较的实现是正常的,这几乎可以肯定,但不能保证。

>

  • 是否可以优化从不同调用到sbrk的比较指针,以及

    如果是这样,glibc的所作所为让他们逍遥法外。

    在上面给出的公式中,start_object等实际上是void*,那么计算可能会被优化掉(例如,可能会做您想做的事情),但不能保证这样做,因为行为是未定义的。如果内核使用与强制转换所暗示的相同的井序,强制转换将保证它这样做。

  •  类似资料:
    • 问题内容: SSCCE: 以上输出: 问题答案: 异常类继承自其方法,并且不会覆盖它。每次都创建新的Exception实例,它们是内存中的不同对象。即使它们的堆栈跟踪相同,但它们在内存中的对象分配仍然不同,并且使用默认的equals()方法,它们并不相同。 但是,您可以定义自定义异常类并重写。

    • 问题内容: 我得到的错误就在这行 。 该怎么办?其他逻辑还可以吗? 我想做的是有一个A列表和一个B列表,其中一个属性与id相同;尽管变量名不同。即在和在B。现在我将两个列表都放在ListAll中,并在相同的变量id / bid上对它们进行排序。我有A和B实现可比性。 和我的listAll是对象类型? 我该怎么做?谢谢。 问题答案: 您可以添加一个通用基类并在那里进行比较,如下所示:

    • 两个 NavigableString 或 Tag 对象具有相同的HTML或XML结构时, Beautiful Soup就判断这两个对象相同. 这个例子中, 2个 <b> 标签在 BS 中是相同的, 尽管他们在文档树的不同位置, 但是具有相同的表象: “<b>pizza</b>” markup = "<p>I want <b>pizza</b> and more <b>pizza</b>!</p>"

    • 两个 NavigableString 或 Tag 对象具有相同的HTML或XML结构时, Beautiful Soup就判断这两个对象相同. 这个例子中, 2个 <b> 标签在 BS 中是相同的, 尽管他们在文档树的不同位置, 但是具有相同的表象: “<b>pizza</b>” markup = "<p>I want <b>pizza</b> and more <b>pizza</b>!</p>"

    • 问题内容: 我需要编写一个比较器,它采用类型A的对象A和类型B的对象B。这两个对象不是公共对象的扩展。它们的确不同,但是我需要通过其中的通用字段来比较这两个对象。我必须使用比较器接口,因为对象存储在Set中,并且在必须对CollectionUtils执行操作之后。我在Google上搜索了一下,发现了Comparator的解决方案,但只有相同的类型。 我试图朝这个方向实施思考,但是我不知道我是否在正

    • 我需要写一个比较器,取一个a类型的对象a和一个B类型的对象B。这两个对象不是一个公共对象的扩展。他们确实是不同的,但我需要比较这两个对象在它的共同领域。我必须使用比较器接口,因为对象存储在Set中,之后我必须使用CollectionUtils进行操作。我搜索了一点点,我用比较器找到了解决方案,但只有相同的类型。 TXS 附注:我在不同的集合中添加两个对象: 之后我会这样想:

    • 我创建了的两个实例,如下所示。一个是从创建的,另一个是相同的,但在开头加上了一些附加的数字。使用方法比较它们时,它返回: 我认为添加的数字被丢弃了,并且两者给出了相同的UUID字符串值。为什么会这样?

    • 问题内容: 好吧,我有两个StringBuilder对象,我需要在Java中对其进行比较。我知道我可以做的一种方法是 但这意味着我要创建两个String对象,还有没有更好的方法来比较StringBuilder对象。也许您不需要创建其他对象的地方? 问题答案: 如您所知,继承自,因此仅在将同一对象作为参数传递时才返回true。它并 没有 比较两个内容小号! 如果您查看源代码,您将得出结论,最有效的比