如何投射sockaddr_storage并避免违反严格别名规则

我正在使用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_storagesockaddr_insockaddr_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使用类型惩罚做正确的事情,这是工会明确允许的

我很确定这种(误)使用联盟对GCC不起作用(或者只是偶然):

 short type_pun2(int i,int * pi,short * ps){
     * pi = i;
     return * ps;
 }

联盟U {
     int i;
    短片;
 };

 short type_pun(int i){
    你好
     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是一种类型的指针,但是你将它转换为不同类型的指针然后解除引用它。 这打破了一个名为“严格别名”的规则,该规则表明不同类型的变量永远不会是别名。 在您的情况下,别名到另一种类型正是您想要做的。

简单的解决方案是关闭此优化,这允许以这种方式混叠。 在GCC上,标志是-fno-strict-aliasing

更好的解决方案是使用Nikolai提到的工会。

 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给我一个警告,所以我不确定这是否会给你买任何东西。