23

私たちのコードでは、次のようなものを使用していました。

   *(controller->bigstruct) = ( struct bigstruct ){ 0 };

これは以前はうまく機能していましたが、GCC のバージョンをアップグレードしたところ、突然スタック オーバーフローが発生し始めました。アセンブリを見ると、古い GCC コード (2.x) は基本的にこれを行っていました。

memset(controller->bigstruct, 0, sizeof(struct bigstruct));

新しい GCC (3.4.x) はこれを行っていました

   struct bigstruct temp = { 0 };
   controller->bigstruct = temp;

C99 の仕様を確認したところ、その理由がわかりました。C99 では、基本的に匿名構造体がスタック上に存在する必要があります。これは良いコンセプトですが、この構造体は 4 メガバイトの大きさであり、ヒープ上にのみ存在することを意図していました!

メンバーを明示的に設定する独自の「初期化」関数を作成することにしましたが、これは見苦しく、メンテナンスの頭痛の種です。私は memset を適切な解決策とは考えていません。なぜなら、0 のビット値が型の適切なゼロ値であることを知ることができないからです (ニトピッキング、私は知っていますが、そこにいます;私はそれを気にしませんコンパイラは知っているので、それを行います)

このような大きな構造を初期化する「正しい」、または少なくとも最善の方法は何ですか?

memset が解決策ではないと思う理由をさらに明確にするために、明示的に初期化されていないメンバーの初期化の規則は、静的初期化と同じであり、次のとおりです。 - ポインター型の場合は、null ポインターに初期化されます。- 算術型の場合、(正または符号なし) ゼロに初期化されます。...

「memset」はメモリをビット パターン ゼロに設定しますが、これは必ずしも同じではありません。IEEE 浮動小数点数を使用しないシステムを想像してみてください。珍しいですが、C でサポートされています。0.0 の表現は、「全ビット 0」を意味する必要はなく、プロセッサにとって都合のよいものであれば何でもかまいません。

4

6 に答える 6

21

memsetは行く方法です。多くの選択肢はありません。

次のようにします。

#define InitStruct(var, type) type var; memset(&var, 0, sizeof(type))

そのため、次のことだけを行う必要があります。

InitStruct(st, BigStruct);

そして、通常どおり st を使用します...

「0」が構造体の有効な「0」タイプではない理由がわかりません。構造体を「一括初期化」する唯一の方法は、すべてのメモリを値に設定することです。そうしないと、メンバーごとに特定のビット パターンを使用するように指示する追加のロジックを作成する必要があります。使用する最適な「一般的な」ビット パターンは 0 です。

また、これは、実行時に使用したのと同じロジックです

*(controller->bigstruct) = *( struct bigstruct ){ 0 };

したがって、私はあなたがそれを使用することを躊躇しません:)

この投稿への最初のコメントは、私が彼とばかを呼ぶ前に、私にいくつかの調査をさせました。そして、私はこれを見つけました:

http://www.lysator.liu.se/c/c-faq/c-1.html

とても興味深い; コメントに投票できたら、私はそうします:)

そうは言っても、0 以外の null 値を持つ古風なアーキテクチャをターゲットにしたい場合の唯一のオプションは、特定のメンバーを手動で初期化することです。

ありがとう、トーマス・パドロン・マッカーシー!今日は新しいことを学びました:)

于 2008-10-07T06:38:52.607 に答える
5

memset を使用したくない場合は、いつでも構造体の静的コピーを宣言して memcpy を使用できます。これにより、同様のパフォーマンスが得られます。これにより、プログラムに 4 MB が追加されますが、個々の要素を設定するよりもおそらく優れています。

とはいえ、GCC が memset を使用していて、以前はそれで十分だったのであれば、今でも十分に優れていることをお勧めします。

于 2008-10-07T06:38:39.753 に答える
4

プライベートな初期化関数は醜いものではなく、オブジェクト (構造体) を初期化する良いオブジェクト指向の方法です。あなたの構造は4MBのポインターではないと仮定するので、解決策は次のようになると思います:

void init_big_struct(struct bigstruct *s)  
{  
    memset(s, 0, sizeof(struct bigstruct));  
    s->some_pointer = NULL; // Multiply this as needed  
}

一方、私たちのコードは 20 を超える組み込みオペレーティング システムと多数の異なるハードウェアで実行されていますが、構造体の memset だけで問題が発生することはありません。

于 2008-10-07T07:08:42.733 に答える
4

他の人が言ったように、 memset は行くべき道です。ただし、C++ オブジェクト、特に仮想メソッドを持つオブジェクトでは memset を使用しないでください。には仮想関数ポインターのsizeof( foo )テーブルが含まれており、これに対して memset を実行すると深刻な問題が発生します。

memset だけで問題が解決しない場合は、単に memset を実行してから、ゼロ以外のメンバー (つまり、IEEE 以外の浮動小数点値) を初期化します

于 2008-10-07T10:54:57.880 に答える
-2

うーん - まず最初に init 関数を作成し、各メンバーを明示的に設定することは正しいことです - それはオブジェクト指向言語のコンストラクタが機能する方法です。

第二に、IEEE 以外の浮動小数点数を実装するハードウェアを知っている人はいますか? - おそらくコモドール64か何か;-)

于 2008-10-07T06:57:21.073 に答える