Tod*_*eed 7 c strict-aliasing unions
假设我有一个示例源文件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
Run Code Online (Sandbox Code Playgroud)
让我们也说我接受限制,如果你传入两个重叠的指针,你得到未定义的结果.
我尝试过这样的事情(假设这些宏在每行末尾都使用反斜杠转义的换行符进行了正确的格式化)
#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]
})
Run Code Online (Sandbox Code Playgroud)
但是当我在宏中取消引用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
})
Run Code Online (Sandbox Code Playgroud)
除了我从编译器得到与严格别名规则完全相同的错误.
所以:有没有办法在不破坏严格别名的情况下做到这一点,实际上是不是要复制内存?
(may_alias不算,它只允许你绕过严格别名规则)
编辑:使用memcmp来做到这一点.我陷入了别名规则,并没有想到它.
编译器是正确的,因为别名规则由您正在访问的对象(即内存位置)的所谓"有效类型"确定,而不管任何指针魔法.在这种情况下,使用union对指针进行类型化处理与显式转换没有什么不同 - 使用强制转换实际上是可取的,因为标准不保证仲裁指针类型具有兼容的表示,即您不必要地依赖于实现定义行为.
如果要符合标准,则需要在声明原始变量期间将数据复制到新变量或使用union .
如果你的128位整数是big-endian或little-endian(即不是mix-endian),你也可以使用memcmp()(直接或在否定返回值之后)或者自己进行逐字节比较:通过指针访问字符类型是别名规则的例外.