假设我有一个示例源文件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(a [0]< b [0])时,我从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执行此操作。我陷入了别名规则,并没有想到它。
答案 0 :(得分:5)
编译器是正确的,因为别名规则由所谓的决定 无论指针魔法如何,您正在访问的对象(即内存位置)的“有效类型”。在这种情况下,使用union对指针进行类型化处理与显式转换没有什么不同 - 使用强制转换实际上是可取的,因为标准不保证仲裁指针类型具有兼容的表示,即您不必要地依赖于实现定义行为。
如果要符合标准,则需要在声明原始变量期间将数据复制到新变量或使用union 。
如果你的128位整数是big-endian或little-endian(即不是mix-endian),你也可以使用memcmp()
(直接或在否定返回值之后)或者做一个字节 - 明智的比较你自己:通过字符类型的指针访问是别名规则的一个例外。