我有一个简单的函数
#define IP_AS_V6(storage, f) ((struct sockaddr_in6 *)&(storage))->sin6_##f
static inline int ip_check_equal_v6
(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
{ return ((uint64_t *)IP_AS_V6(a, addr).s6_addr)[0] == ((uint64_t *)IP_AS_V6(b, addr).s6_addr)[0] &&
((uint64_t *)IP_AS_V6(a, addr).s6_addr)[1] == ((uint64_t *)IP_AS_V6(b, addr).s6_addr)[1]; }
在切换到GCC-12后,由于,它不再编译
error: array subscript 2 is outside array bounds of 'const struct sockaddr_storage[0]' [-Werror=array-bounds]
5 | ((uint64_t *)IP_AS_V6(a, addr).s6_addr)[1] == ((uint64_t *)IP_AS_V6(b, addr).s6_addr)[1]; }
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
IPv6地址是128位,因此当将其视为64位int数组时,索引1应该非常好。这段代码是否由于其他原因而非法/未定义,或者这确实是gcc-12中的一个错误?
让我们看看您的宏实际在做什么。鉴于
const struct sockaddr_storage *a;
表达式CCD_ 1扩展为…
(uint64_t *)((struct sockaddr_in6 *)&(a))->sin6_addr.s6_addr)[0]
首先,您的间接级别搞砸了,至少如果a
实际上指向struct sockaddr_in6
。&(a)
是指向结构指针的指针,它对您的目的毫无用处。你的意思大概只是(a)
,而不是它的地址。这可能是编译器抱怨的根源。
其次,(struct sockaddr_in6 *)(a))->sin6_addr.s6_addr
的类型为unsigned char[16]
。它会自动转换为unsigned char *
,您可以安全地将其转换为类型uint64_t *
,但也可能无法。如果(uint64_t *)IP_AS_V6(a, addr).s6_addr)[0]
0的结果指针没有正确对齐,那么会产生未定义的行为,我不清楚是否有什么可以确保对齐成功。
第三,即使对齐成功,通过类型为uint64_t
的左值访问unsigned char[]
的部分也违反了严格的混叠规则,从而产生未定义的行为。
该函数还丢弃了const
ness,这是一种糟糕的形式,应该会引发警告,即使它没有尝试修改任何一个对象。
我想我会这样写函数:
static inline int ip_check_equal_v6(const struct sockaddr_storage *a,
const struct sockaddr_storage *b) {
const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) a;
const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *) b;
return !memcmp(a6->sin6_addr.s6_addr, a6->sin6_addr.s6_addr,
sizeof(a6->sin6_addr.s6_addr));
}
如果第一个地址中的地址字节与第二个地址相同(两者都被解释为IPV6地址(,则返回1;如果其中任何一个地址不同,则返回0,这似乎是您的版本想要做的。请注意,比原来的读起来容易得多,这使得它更容易维护,甚至更容易用眼睛评估。您的编译器甚至可能比原来的编译器更好地优化它,尽管我对此没有任何承诺。