22

書籍、マニュアル ページ、Web サイトで実際のコードやソケット コードの例を見ると、ほとんどの場合、次のようなものが表示されます。

struct sockaddr_in foo;
memset(&foo, 0, sizeof foo); 
/* or bzero(), which POSIX marks as LEGACY, and is not in standard C */
foo.sin_port = htons(42);

それ以外の:

struct sockaddr_in foo = { 0 }; 
/* if at least one member is initialized, all others are set to
   zero (as though they had static storage duration) as per 
   ISO/IEC 9899:1999 6.7.8 Initialization */ 
foo.sin_port = htons(42);

また:

struct sockaddr_in foo = { .sin_port = htons(42) }; /* New in C99 */

また:

static struct sockaddr_in foo; 
/* static storage duration will also behave as if 
   all members are explicitly assigned 0 */
foo.sin_port = htons(42);

たとえば、getaddrinfo に渡す前に struct addrinfo ヒントをゼロに設定する場合も同じことがわかります。

どうしてこれなの?私が理解している限りでは、memset を使用しない例は、memset を使用する例と同等である可能性があります。違いがあることを認識しています:

  • memset はすべてのビットを 0 に設定しますが、これは必ずしも各メンバーを 0 に設定するための正しいビット表現ではありません。
  • memset は、パディング ビットもゼロに設定します。

これらの構造体をゼロに設定する場合、これらの違いのいずれかが関連するか、必要な動作であり、したがって代わりに初期化子を使用するのは間違っていますか? もしそうなら、それはなぜですか?また、どの標準または他の情報源がこれを検証していますか?

両方が正しい場合、初期化子の代わりに memset/bzero が表示される傾向があるのはなぜですか? スタイルだけの問題ですか?どちらがより良いスタイルであるかについて、主観的な答えは必要ないと思います。

通常、memset よりも初期化子を優先して使用するのが一般的です。これは、すべてのビットをゼロにすることは通常望ましくなく、その代わりに型のゼロを正しく表現する必要があるためです。これらのソケット関連の構造体については逆ですか?

私の調査では、POSIX では、http: //www.opengroup.org/onlinepubs/000095399/basedefs/netinet/in.h.html で sockaddr_in6 (sockaddr_in ではなく) をゼロにする必要があるだけであることがわかりましたが、その方法については言及していません。ゼロにする必要があります(memsetまたは初期化子?)。BSD ソケットは POSIX よりも前のものであり、それが唯一の標準ではないことを認識しています。レガシー システムまたは最新の非 POSIX システムに対する互換性の考慮事項はありますか?

個人的には、スタイル (およびおそらく良い慣行) の観点から、初期化子を使用して memset を完全に回避することを好みますが、次の理由で気が進まないのです。

  • UNIX Network Programmingのような他のソースコードと半正規のテキストは bzero を使用します (例えば、第 2 版の 101 ページと第 3 版の 124 ページ (私は両方を所有しています))。
  • 上記の理由により、それらが同一ではないことは十分承知しています。
4

5 に答える 5

15

部分的なイニシャライザ アプローチ (つまり ' { 0 }') の 1 つの問題は、GCC がイニシャライザが不完全であることを警告することです (警告レベルが十分に高い場合、私は通常 ' ' を使用-Wallし、しばしば ' ' を使用します-Wextra)。指定されたイニシャライザのアプローチでは、その警告は表示されませんが、C99 はまだ広く使用されていません。ただし、これらのパーツは、おそらく Microsoft の世界を除いて、かなり広く利用されています。

私はアプローチを好む傾向があります:

static const struct sockaddr_in zero_sockaddr_in;

に続く:

struct sockaddr_in foo = zero_sockaddr_in;

静的定数で初期化子を省略したということは、すべてがゼロであることを意味しますが、コンパイラはウィッターしません (ウィッターすべきではありません)。代入では、コンパイラの生来のメモリ コピーが使用されます。これは、コンパイラに深刻な欠陥がない限り、関数呼び出しよりも遅くはなりません。


GCCは時間の経過とともに変化しました

GCC バージョン 4.4.2 から 4.6.0 は、GCC 4.7.1 とは異なる警告を生成します。具体的には、GCC 4.7.1 は= { 0 }初期化子を「特殊なケース」として認識し、文句を言いませんが、GCC 4.6.0 などは文句を言いました。

ファイルを検討してくださいinit.c

struct xyz
{
    int x;
    int y;
    int z;
};

struct xyz xyz0;                // No explicit initializer; no warning
struct xyz xyz1 = { 0 };        // Shorthand, recognized by 4.7.1 but not 4.6.0
struct xyz xyz2 = { 0, 0 };     // Missing an initializer; always a warning
struct xyz xyz3 = { 0, 0, 0 };  // Fully initialized; no warning

GCC 4.4.2 (Mac OS X 上) でコンパイルすると、警告は次のようになります。

$ /usr/gcc/v4.4.2/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9: warning: missing initializer
init.c:9: warning: (near initialization for ‘xyz1.y’)
init.c:10: warning: missing initializer
init.c:10: warning: (near initialization for ‘xyz2.z’)
$

GCC 4.5.1 でコンパイルすると、警告は次のようになります。

$ /usr/gcc/v4.5.1/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:8: warning: missing initializer
init.c:9:8: warning: (near initialization for ‘xyz1.y’)
init.c:10:8: warning: missing initializer
init.c:10:8: warning: (near initialization for ‘xyz2.z’)
$

GCC 4.6.0 でコンパイルすると、警告は次のようになります。

$ /usr/gcc/v4.6.0/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:9:8: warning: (near initialization for ‘xyz1.y’) [-Wmissing-field-initializers]
init.c:10:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:10:8: warning: (near initialization for ‘xyz2.z’) [-Wmissing-field-initializers]
$

GCC 4.7.1 でコンパイルすると、警告は次のようになります。

$ /usr/gcc/v4.7.1/bin/gcc -O3 -g -std=c99 -Wall -Wextra  -c init.c
init.c:10:8: warning: missing initializer [-Wmissing-field-initializers]
init.c:10:8: warning: (near initialization for ‘xyz2.z’) [-Wmissing-field-initializers]
$

上記のコンパイラは私がコンパイルしたものです。Apple が提供するコンパイラは、名目上 GCC 4.2.1 と Clang です。

$ /usr/bin/clang -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9:23: warning: missing field 'y' initializer [-Wmissing-field-initializers]
struct xyz xyz1 = { 0 };
                      ^
init.c:10:26: warning: missing field 'z' initializer [-Wmissing-field-initializers]
struct xyz xyz2 = { 0, 0 };
                         ^
2 warnings generated.
$ clang --version
Apple clang version 4.1 (tags/Apple/clang-421.11.65) (based on LLVM 3.1svn)
Target: x86_64-apple-darwin11.4.2
Thread model: posix
$ /usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra -c init.c
init.c:9: warning: missing initializer
init.c:9: warning: (near initialization for ‘xyz1.y’)
init.c:10: warning: missing initializer
init.c:10: warning: (near initialization for ‘xyz2.z’)
$ /usr/bin/gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$

以下のコメントでSecurityMattが指摘しているように、memset()メモリから構造体をコピーすることの利点は、メモリからのコピーがよりコストがかかり、1 つではなく 2 つのメモリ位置 (ソースとコピー先) にアクセスする必要があることです。比較すると、値をゼロに設定すると、ソースのメモリにアクセスする必要がなくなり、最新のシステムではメモリがボトルネックになります。そのため、単純なイニシャライザ (同じ値 (通常はすべてゼロ バイト) がターゲット メモリに配置される) の場合、memset()コーディングはコピーよりも高速である必要があります。初期化子が値の複雑な組み合わせ (すべてがゼロバイトではない) である場合、表記のコンパクトさと信頼性のために、初期化子を優先してバランスを変更できます。

切り詰められた答えは 1 つもありません。私はまだイニシャライザを使用する傾向がありますが、memset()多くの場合有効な代替手段です。

于 2009-05-21T18:22:34.097 に答える
3

「struct sockaddr_in foo = { 0 };」"memset(&foo, 0, sizeof foo);" は初回のみ有効ですが、関数が実行されるたびにクリアされます。

于 2009-05-21T18:22:59.853 に答える
3

sockaddr_自分で何かの型のオブジェクトを作成してはならないので、どちらも正しくないと思います。代わりに、常にgetaddrinfo(または場合によってはgetsocknameまたはgetpeername) を使用してアドレスを取得します。

于 2011-06-07T02:52:30.207 に答える
1

どちらのアプローチでも問題はないはずです。パディングバイトの値は重要ではありません。memset()の使用は、構造体初期化子の導入よりも前か、より効率的だった可能性のあるBerkeley-ism bzero()の以前の使用に起因していると思われます。

于 2009-05-21T18:31:17.683 に答える
1

多くの人が指摘しているように、どちらも正しいです。さらに、ゼロ化されたメモリ ブロックを既に返すcallocを使用して、これらの構造体を割り当てることができます。

于 2009-05-21T18:22:32.833 に答える