我使用Beej指南网络和跨走样的问题就来了。 他提出了一个函数返回IPv4或特定结构的IPv6地址:
1 void *get_in_addr( struct sockaddr *sa )
2 {
3 if (sa->sa_family == AF_INET)
4 return &(((struct sockaddr_in*)sa)->sin_addr);
5 else
6 return &(((struct sockaddr_in6*)sa)->sin6_addr);
7 }
这将导致GCC吐出了上线3 SA严格的混叠误差据我了解,那是因为我调用这个函数像这样:
struct sockaddr_storage their_addr;
...
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
connection_name,
sizeof connection_name);
我猜别名有一个事实,即做their_addr
变量的类型的sockaddr_storage
和相同的内存以不同类型分另一个指针。
就是要解决这个症结的最佳方式sockaddr_storage
, sockaddr_in
和sockaddr_in6
组成工会? 看起来这应该是网络陈旧的领土,我只是无法找到最佳实践任何很好的例子。
此外,如果任何人都可以解释究竟在何处混淆问题发生,我会非常感激。
我倾向于这样做是为了让GCC做正确的事类型双关 ,这是明确允许与工会:
/*! Multi-family socket end-point address. */ typedef union address { struct sockaddr sa; struct sockaddr_in sa_in; struct sockaddr_in6 sa_in6; struct sockaddr_storage sa_stor; } address_t;
我倾向于这样做是为了让GCC做正确的事与类型双关,这是明确允许与工会
我敢肯定,这(错误)使用工会不会 (或仅是偶然)与海湾合作委员会的工作:
short type_pun2 (int i, int *pi, short *ps) {
*pi = i;
return *ps;
}
union U {
int i;
short s;
};
short type_pun (int i) {
U u;
return type_pun2 (i, &u.i, &u.s);
}
要做到这一点,正确的方法是用memcpy
,不是union
。
试图编写代码来获取本机的MAC地址时,我最近有HPUX系统上的类似别名警告
的&(((struct sockaddr_in *)addr)->sin_addr)
抱怨严格走样规则
这在某些情况下的代码
char ip[INET6_ADDRSTRLEN] = {0};
strucut sockaddr *addr
...
get addr from ioctl(socket,SOCGIFCONF...) call
...
inet_ntop(AF_INET, &(((struct sockaddr_in *)addr)->sin_addr),ip,sizeof ip);
我通过执行以下操作克服了混淆警告
struct sockaddr_in sin;
memcpy(&sin,addr,sizeof(struct sockaddr));
inet_ntop(AF_INET, &sin.sin_addr,ip,sizeof ip);
而虽然这是潜在的危险我才加入以下行
static_assert(sizeof(sockaddr)==sizeof(sockaddr_in));
我不知道这是什么会被认为是不好的做法,但它的工作,是跨平台的,以其他* NIX口味和编译器
这个问题无关与呼叫的功能。 相反,它与((struct sockaddr_in*)sa)->sin_addr
。 问题是, sa
是一个类型的指针,但你它强制转换为不同类型的指针,然后解引用它。 这打破了所谓的“严格走样”的规则,它说,不同的永远不能别名类型的变量。 在你的情况下,混叠不同类型的正是你想做的事。
简单的解决方案是关闭这种优化,其允许以这种方式混叠。 在海湾合作委员会,该标志为-fno-strict-aliasing
。
更好的解决方案是使用一个联盟,由尼古拉提及。
void *get_in_addr(struct sockaddr *sa)
{
union {
struct sockaddr *sa;
struct sockaddr_in *sa_in;
struct sockaddr_in6 *sa_in6;
} u;
u.sa = sa;
if (sa->sa_family == AF_INET)
return &(u.sa_in->sin_addr);
else
return &(u.sa_in6->sin6_addr);
}
这就是说,我不能真正得到GCC使用原密码的时候给我一个警告,所以我不知道这是否买你的东西。