用 < 比较两个指针如果它们都转换为整数类型,则未定义行为? [英] Is comparing two pointers with < undefined behavior if they are both cast to an integer type?

查看:28
本文介绍了用 < 比较两个指针如果它们都转换为整数类型,则未定义行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有这样一段代码,它根据位置以特定顺序将一个内存块复制到另一个内存块:

Let's say I have this code that copies one block of memory to another in a certain order based on their location:

void *my_memmove(void *dest, const void *src, size_t len)
{
    const unsigned char *s = (const unsigned char *)src;
    unsigned char *d = (unsigned char *)dest;

    if(dest < src)
    {
        /* copy s to d forwards */
    }
    else
    {
        /* copy s to d backwards */
    }

    return dest;
}

如果 srcdest 不指向同一数组的成员,这是未定义的行为(6.8.5p5).

This is undefined behavior if src and dest do not point to members of the same array(6.8.5p5).

但是,假设我将这两个指针强制转换为 uintptr_t 类型:

However, let's say I cast these two pointers to uintptr_t types:

#include <stdint.h>

void *my_memmove(void *dest, const void *src, size_t len)
{
    const unsigned char *s = (const unsigned char *)src;
    unsigned char *d = (unsigned char *)dest;

    if((uintptr_t)dest < (uintptr_t)src)
    {
        /* copy s to d forwards */
    }
    else
    {
        /* copy s to d backwards */
    }

    return dest;
}

如果它们不是同一数组的成员,这仍然是未定义的行为吗?如果是,我可以通过哪些方式合法地比较内存中的这两个位置?

Is this still undefined behavior if they're not members of the same array? If it is, what are some ways that I could compare these two locations in memory legally?

我见过 这个问题,但它只处理相等性,不处理其他比较运算符(<> 等).

I've seen this question, but it only deals with equality, not the other comparison operators (<, >, etc).

推荐答案

转换是合法的,但从技术上讲,没有为结果定义任何含义.相反,如果您将指针转换为 void *,然后转换为 uintptr_t,则定义了轻微的含义:执行反向操作将重现原始指针(或等效的东西).

The conversion is legal but there is, technically, no meaning defined for the result. If instead you convert the pointer to void * and then convert to uintptr_t, there is slight meaning defined: Performing the reverse operations will reproduce the original pointer (or something equivalent).

特别是,您不能依赖一个整数小于另一个整数来表示它在内存中更早或地址更低这一事实.

It particular, you cannot rely on the fact that one integer is less than another to mean it is earlier in memory or has a lower address.

uintptr_t 的规范 (C 2018 7.20.1.4 1) 说它具有任何有效的 void * 都可以转换为 uintptr_t 的特性>,然后转换回void *,结果会比较等于原指针.

The specification for uintptr_t (C 2018 7.20.1.4 1) says it has the property that any valid void * can be converted to uintptr_t, then converted back to void *, and the result will compare equal to the original pointer.

但是,当您将 unsigned char * 转换为 uintptr_t 时,您并没有将 void * 转换为 uintptr_t.所以 7.20.1.4 不适用.我们所拥有的只是 6.3.2.3 中指针转换的一般定义,其中第 5 段和第 6 段说:

However, when you convert an unsigned char * to uintptr_t, you are not converting a void * to uintptr_t. So 7.20.1.4 does not apply. All we have is the general definition of pointer conversions in 6.3.2.3, in which paragraphs 5 and 6 say:

整数可以转换为任何指针类型.除了前面指定的[涉及空指针的零],结果是实现定义的,可能没有正确对齐,可能没有指向引用类型的实体,并且可能是陷阱表示.

An integer may be converted to any pointer type. Except as previously specified [involving zero for null pointers], the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

任何指针类型都可以转换为整数类型.除了之前指定的[再次空指针],结果是实现定义的.如果结果不能以整数类型表示,则行为未定义.结果不必在任何整数类型的值范围内.

Any pointer type may be converted to an integer type. Except as previously specified [null pointers again], the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

所以这些段落没有帮助,只是它们告诉您实现文档应该告诉您转换是否有用.毫无疑问,它们存在于大多数 C 实现中.

So these paragraphs are no help except they tell you that the implementation documentation should tell you whether the conversions are useful. Undoubtedly they are in most C implementations.

在您的示例中,您实际上从参数中的 void * 开始并将其转换为 unsigned char *,然后转换为 uintptr_t.所以补救方法很简单:直接从 void * 转换为 uintptr_t.

In your example, you actually start with a void * from a parameter and convert it to unsigned char * and then to uintptr_t. So the remedy there is simple: Convert to uintptr_t directly from the void *.

对于我们有其他指针类型而不是 void * 的情况,那么 6.3.2.3 1 很有用:

For situations where we have some other pointer type, not void *, then 6.3.2.3 1 is useful:

指向 void 的指针可以与指向任何对象类型的指针相互转换.指向任何对象类型的指针都可以转换为指向 void 的指针,然后再返回;结果应与原始指针相等.

A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

所以,转换到和从 void * 被定义为保留原始指针,因此我们可以将它与从 void *uintptr_t<的转换结合起来/代码>:

So, converting to and from void * is defined to preserve the original pointer, so we can combine it with a conversion from void * to uintptr_t:

(uintptr_t) (void *) A < (uintptr_t) (void *) B

因为 (void *) A 必须能够在转换回来时产生原始的 A,并且 (uintptr_t) (void *) A> 必须能够产生它的 (void *) A,然后 (uintptr_t) (void *) A(uintptr_t) (void *) BAB 不同,code> 必须不同.

Since (void *) A must be able to produce the original A upon conversion back, and (uintptr_t) (void *) A must be able to produce its (void *) A, then (uintptr_t) (void *) A and (uintptr_t) (void *) B must be different if A and B are different.

这就是我们从 C 标准中可以说的关于比较的全部内容.从指针转换为整数可能会产生乱序的地址位或其他一些奇怪的东西.例如,它们可能会产生一个 32 位整数,其中包含一个 16 位段地址和一个 16 位偏移量.其中一些整数对于较低的地址可能具有较高的值,而其他整数对于较低的地址可能具有较低的值.更糟糕的是,同一个地址可能有两种表示形式,因此即使 AB 指的是同一个对象,比较可能会指示小于".

And that is all we can say from the C standard about the comparison. Converting from pointers to integers might produce the address bits out of order or some other oddities. For example, they might produce a 32-bit integer contain a 16-bit segment address and a 16-bit offset. Some of those integers might have higher values for lower addresses while others have lower values for lower addresses. Worse, the same address might have two representations, so the comparison might indicate "less than" even though A and B refer to the same object.

这篇关于用 &lt; 比较两个指针如果它们都转换为整数类型,则未定义行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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