質問
このコードでは、次のようなものを使用していました。
*(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'はメモリをビットパターン0に設定しますが、これは必ずしも同じではありません。 IEEE浮動小数点数を使用しないシステムを想像してください。異常ですが、Cでサポートされています。0.0の表現は「すべてのビットがゼロ」を意味する必要はなく、プロセッサにとって便利なものであれば何でもかまいません。
解決
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値を持つ古風なアーキテクチャをターゲットにしたい場合の唯一のオプションは、特定のメンバーに対して手動で初期化を行うことです。
Thomas Padron-McCarthyに感謝します!今日は何か新しいことを学びました:)
他のヒント
memsetを使用したくない場合は、常に構造体の静的コピーを宣言し、memcpyを使用すると、同様のパフォーマンスが得られます。これにより、プログラムに4メガが追加されますが、おそらく個々の要素を設定するよりも優れています。
とはいえ、GCCがmemsetを使用していて、以前は十分だったなら、今は十分に良いことをお勧めします。
他の人が言ったように、memsetは行くべき方法です。ただし、C ++オブジェクト、特に仮想メソッドを使用するオブジェクトではmemsetを使用しないでください 。 sizeof(foo)
には仮想関数ポインターのテーブルが含まれます。その上でmemsetを実行すると、深刻な悲嘆が生じます。
memsetだけでは問題が解決しない場合は、memsetを実行し、ゼロ以外のメンバー(つまり、IEEE以外の浮動小数点値)を初期化します。
>
プライベート初期化関数はいものではなく、オブジェクト(構造体)を初期化する適切なオブジェクト指向の方法です。私はあなたの構造が4MBのポインターではないと想定しているので、解決策は次のようにすべきだと思います:
void init_big_struct(struct bigstruct *s)
{
memset(s, 0, sizeof(struct bigstruct));
s->some_pointer = NULL; // Multiply this as needed
}
一方で、私たちのコードは20を超える組み込みオペレーティングシステムと多数の異なるハードウェアで実行されており、構造体のmemsetだけで問題に遭遇することはありません。
hmm-最初にinit関数を作成し、各メンバーを明示的に設定することは正しいことです-オブジェクト指向言語のコンストラクターの動作方法です。
2番目-IEEE以外の浮動小数点数を実装するハードウェアを知っている人はいますか? -おそらくコモドール64または何か;-)