26

Beej の 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_storageこのくっついている、sockaddr_in、およびを回避する最善の方法はありsockaddr_in6ますか? これはネットワーキングの分野でよく使われているように思えますが、ベスト プラクティスの良い例が見つかりません。

また、エイリアシングの問題がどこで発生するかを正確に説明できる人がいれば、非常にありがたいです。

4

4 に答える 4

20

私は、共用体で明示的に許可されているtype-punningで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;

于 2009-09-16T13:28:04.243 に答える
1

私は、ユニオンで明示的に許可されているタイプパニングでGCCに正しいことをさせるためにこれを行う傾向があります

このユニオンの(誤)使用は、GCCでは機能しない(または偶然にのみ)と確信しています:

short type_pun2 (int i, int *pi, short *ps) {
    *pi = i;
    *ps を返します。
}

ユニオンU {
    int i;
    短いs;
};

short type_pun (int i) {
    うう;
    return type_pun2 (i, &u.i, &u.s);
}

それを行う正しい方法は、memcpyではなくを使用することunionです。

于 2011-10-03T15:40:51.280 に答える
-1

この問題は、関数の呼び出しとは関係ありません。むしろ、一緒((struct sockaddr_in*)sa)->sin_addrです。問題は、saある型のポインターですが、それを別の型のポインターにキャストしてから逆参照していることです。これは、「厳密なエイリアシング」と呼ばれる、異なる型の変数は決してエイリアシングできないというルールを破っています。あなたの場合、別のタイプへのエイリアスはまさにあなたがやりたいことです。

簡単な解決策は、この最適化をオフにすることです。これにより、このようなエイリアシングが可能になります。GCC では、フラグは-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 に警告を表示させることは実際にはできないので、これで何かが得られるかどうかはわかりません。

于 2010-03-30T01:02:30.640 に答える