假设我有一个示例源文件 test.c,我正在这样编译它:
$ gcc -03 -Wall
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 得到“取消引用打破严格别名规则”的错误
我原以为你应该使用 union 以两种不同的方式正确地引用内存中的一个地方,所以接下来我尝试了类似的东西
#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 来执行此操作。我被别名规则搞糊涂了,没想到。
最佳答案
编译器是正确的,因为别名规则是由所谓的 您正在访问的对象(即内存位置)的“有效类型”,无论任何指针魔法如何。在这种情况下,使用 union 对指针进行类型双关与显式强制转换没有什么不同——使用强制转换实际上是更可取的,因为标准不保证任意指针类型具有兼容的表示,即你不必要地依赖于实现定义行为。
如果要符合标准,则需要将数据复制到新变量或使用 union 在原始变量的声明中。
如果您的 128 位整数是大端或小端(即不是混合端),您还可以使用 memcmp()
(直接或在取反返回值之后)或者自己做一个字节比较:通过字符类型的指针访问是别名规则的一个异常(exception)。
关于C 克服别名限制( union ?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6486807/