C 克服别名限制( union ?)

标签 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

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

我试过这样的事情(想象一下这些宏的格式正确,每行末尾都有反斜杠转义的换行符)

#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
})

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

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

(ma​​y_alias 不算,它只是让你绕过严格的别名规则)

编辑:使用 memcmp 来执行此操作。我被别名规则搞糊涂了,没想到。

最佳答案

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

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

如果您的 128 位整数是大端或小端(即不是混合端),您还可以使用 memcmp()(直接或在取反返回值之后)或者自己做一个字节比较:通过字符类型的指针访问是别名规则的一个异常(exception)。

关于C 克服别名限制( union ?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6486807/

相关文章:

c - 如何使 GCC 输出到标准输出?

c - Ubuntu 上的 MPI 代码只使用了一个处理器

c - 在不违反严格的别名规则的情况下处理数据序列化

c - 结构体和结构体第一个成员之间的指针别名

c - 奇怪的双重自由行为

C 进程间通信

c - 取消引用类型双关指针将打破严格的别名规则

c - C 代码中带有 union 的意外输出

c - 结构变量别名

c++ - C++中将不同类型的变量存储在一个实体中