7

假设我有一个示例源文件 test.c,我正在编译它:

$ gcc -03 -墙

test.c 看起来像这样..

/// CMP128(x, y)
//
// arguments
//  x - any pointer to an 128-bit int
//  y - any pointer to an 128-bit int
//
// returns -1, 0, or 1 if x is less than, equal to, or greater than y
//
#define CMP128(x, y) // magic goes here

// example usages

uint8_t  A[16];
uint16_t B[8];
uint32_t C[4];
uint64_t D[2];
struct in6_addr E;
uint8_t* F;

// use CMP128 on any combination of pointers to 128-bit ints, i.e.

CMP128(A, B);
CMP128(&C[0], &D[0]);
CMP128(&E, F);

// and so on

假设我接受这样的限制,如果你传入两个重叠的指针,你会得到未定义的结果。

我已经尝试过这样的事情(想象这些宏在每行末尾用反斜杠转义的换行符正确格式化)

#define CMP128(x, y) ({
  uint64_t* a = (void*)x;
    uint64_t* b = (void*)y;

  // compare a[0] with b[0], a[1] with b[1]
})

但是当我在宏 (a[0] < b[0]) 中取消引用 a 时,我从 gcc 收到“取消引用破坏严格别名规则”错误

我原以为你应该使用联合以两种不同的方式正确引用内存中的一个地方,所以接下来我尝试了类似的东西

#define CMP128(x, y) ({
    union {
        typeof(x) a;
        typeof(y) b;
        uint64_t* c;
    }   d = { .a = (x) }
        , e = { .b = (y) };

    // compare d.c[0] with e.c[0], etc
})

除了我从编译器那里得到关于严格别名规则的完全相同的错误。

那么:有没有办法在不破坏严格混叠的情况下做到这一点,而不是实际复制内存?

may_alias不算,它只是让你绕过严格的别名规则)

编辑:使用 memcmp 来做到这一点。我被混叠规则赶上了,并没有想到它。

4

1 回答 1

5

编译器是正确的,因为别名规则是由您正在访问的对象(即内存位置)的所谓“有效类型”决定的,而不管任何指针魔术。在这种情况下,使用联合对指针进行类型双关与显式转换没有什么不同 - 使用转换实际上是更可取的,因为标准不保证任意指针类型具有兼容的表示,即您不必要地依赖于实现定义行为。

如果要符合标准,则需要将数据复制到新变量中,或者在原始变量的声明过程中使用联合。

如果您的 128 位整数是大端或小端(即不是混合端),您也可以使用memcmp()(直接或在否定返回值之后)或自己进行逐字节比较:通过指针访问字符类型是别名规则的一个例外。

于 2011-06-26T21:44:57.800 回答