26

我正在使用 Beej's Guide to Networking 并遇到了别名问题。他提出了一个函数来返回特定结构的 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_in什么sockaddr_in6?看起来这应该是网络中的老旧领域,我只是找不到任何具有最佳实践的好例子。

另外,如果有人能准确解释混叠问题发生的位置,我将不胜感激。

4

4 回答 4

20

我倾向于这样做是为了让 GCC 使用type-punning做正确的事情,这在 union 中是明确允许的:


/*! 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;

于 2009-09-16T13:28:04.243 回答
1

我倾向于这样做是为了让 GCC 使用类型双关语做正确的事情,这是工会明确允许的

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

短 type_pun2 (int i, int *pi, short *ps) {
    *pi = 我;
    返回 *ps;
}

联合 U {
    诠释我;
    短裤;
};

短类型双关(int i){
    你;
    返回 type_pun2 (i, &u.i, &u.s);
}

正确的做法是使用memcpy,而不是union

于 2011-10-03T15:40:51.280 回答
0

我最近在尝试编写代码以获取机器的 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 风格和编译器是跨平台的

于 2017-08-23T07:49:25.433 回答
-1

该问题与对函数的调用无关。相反,它与((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 给我一个警告,所以我不确定这是否能给您带来任何好处。

于 2010-03-30T01:02:30.640 回答