C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: C++におけるC互換構造体のゼロクリア


(過去ログ 119 を表示中)

[トピック内 4 記事 (1 - 4 表示)]  << 0 >>

■69729 / inTopicNo.1)  C++におけるC互換構造体のゼロクリア
  
□投稿者/ キム (5回)-(2014/01/24(Fri) 18:09:42)

分類:[C/C++] 

環境:Visual C++ 2008

C互換構造体ゼロ初期化に関する解釈についてご教示ください。
下記の(A)のような記述をしたら、先輩に怒られてしまいました。
なぜだめなのか理解できません。

// C互換構造体
struct Hoge
{
    int i;
    double d;
    void* p;
};

int _tmain(int argc, _TCHAR* argv[])
{
    Hoge a = {};

    // ここで Hoge a を書き換えるような処理を実行
    a.i = 1;
    a.d = 2.345;
    a.p = &_tmain;
    std::cout << a.i << std::endl << a.d << std::endl << a.p << std::endl;

    // (A)ここで Hoge a をゼロクリア
    a = Hoge();
    std::cout << a.i << std::endl << a.d << std::endl << a.p << std::endl;

    return 0;
}

ゼロクリアされるのは、()による値初期化で非静的メンバーが各々ゼロクリアされるからという解釈で正しいですか?

JIS X3014:2003 の 8.5 初期化子 を読むと、
7 に《初期化子》の括弧の中が空(すなわち、())のオブジェクトには値初期化を行わなければならない。
とあり、
5 に T型のオブジェクトの値初期化とは、次のいずれかを意味する。
- Tが利用者定義コンストラクタを持たないクラス型の場合、Tの各非静的データメンバ及び各規定クラス成分を値初期化する。
- Tが配列型の場合、各要素に対し値初期化を行う。
- そうでない場合、そのオブジェクトをゼロ初期化する。

とあるので、利用者定義コンストラクタを持たないクラス型であるC互換構造体型にはこれが適用されると解釈しています。
また、その結果、
・各メンバが各々ゼロ初期化される。
・各メンバはスカラ型なので0を型変換した値が設定される。
  (よってポインタや浮動少数の内部表現は関係ない)
・パディング領域がゼロクリアされるかどうかは実装依存。
ということになると考えています。
なので、まったく問題ないと考えているのですが。

この解釈で正しいですか?

引用返信 編集キー/
■69731 / inTopicNo.2)  Re[1]: C++におけるC互換構造体のゼロクリア
□投稿者/ 774RR (131回)-(2014/01/25(Sat) 08:15:47)
なぜ怒ったのかを訊ねてみないと、相手側の勘違いとか理解不足とかありえるわけで・・・
「値初期化」についてよく知らない人もいるし。

> この解釈で正しいですか?
正しいと思う。

規格マニアとして指摘しておくと、ここの文言は
ISO/IEC 14882:1998 と ISO/IEC 14882:2003 で変更があるところで、
使おうとしているコンパイラの採択している規格書の版は確認しておく必要がある。
ISO/IEC 14882:2011 は未入手につき調査していない。
(2011 では POD の定義が変更になっているのは知っている) 。
# visual C++ 2008 がどうだかは調査していない。

・コンパイラの採択している版
・コンパイラの規格合致の程度
・コンパイラのバグの有無
あたりで、同一ソースを別の(古い)コンパイラで使うと困る可能性は0ではない。

ちなみにa = Hoge(); は
1.値初期化で一時オブジェクトを作って
2.その一時オブジェクトを a に複写して
3.一時オブジェクトをデストラクトしている
わけで、一時オブジェクトの生成破棄が発生する。
最適化を考えないとき「a のメンバごとの代入」より遅いかもしれない。
# 最適化の結果として同じ「ゼロクリア」コードが生成されたとしても不思議はないが。

一方で、クラス実装が変化しうるとき「メンバごとの代入」では
書き直す必要がある
書き直す際に抜けが出るかもしれない
という点で、提示コードのほうが安全方向であることは間違いない。

問題ない安全なコードに対して相手がダメだしした理由を、俺も知りたいね。
引用返信 編集キー/
■69753 / inTopicNo.3)  Re[2]: C++におけるC互換構造体のゼロクリア
□投稿者/ キム (6回)-(2014/01/27(Mon) 19:13:55)
No69731 (774RR さん) に返信

お返事ありがとうございます。

> 問題ない安全なコードに対して相手がダメだしした理由を、俺も知りたいね。

相手に確認しました。
問題はパディング領域が不定となる可能性にあるとのことでした。
- 今回の構造体はC言語からも呼び出される部分での受け渡しに利用されていること。
- C言語開発陣の間では構造体の比較にmemcmpを使用する習慣があり、今更ダメ出しできない。
- なので、ZeroMemoryを使ってほしい。

memcmpやZeroMemoryなど、非常に不安なのですが、現在の実行環境では問題く動く実績があるので、実績優先の決定とのことです。
仕様制定時にはそのような話は微塵も無かったのですが、たぶん私の突込みが足りなかったのだと反省しています。

> 規格マニアとして指摘しておくと、ここの文言は
> ISO/IEC 14882:1998 と ISO/IEC 14882:2003 で変更があるところで、
> 使おうとしているコンパイラの採択している規格書の版は確認しておく必要がある。

値初期化って2003版からなのですね。
1998版は手元にないのですが、何とか調べて見ます。
今回のことがきっかけで非常に勉強になり、感謝しています。
解決済み
引用返信 編集キー/
■69754 / inTopicNo.4)  Re[3]: C++におけるC互換構造体のゼロクリア
□投稿者/ 774RR (132回)-(2014/01/27(Mon) 20:10:50)
> 問題はパディング領域が不定となる可能性にあるとのことでした。
なるほど。というか、それ以外に不安材料がないよね。

> - C言語開発陣の間では構造体の比較にmemcmpを使用する習慣があり、今更ダメ出しできない。
習慣があってもダメなもんはダメ。
・C の構造体代入で padding が複写される保証はない。だから、
 暗黙の複写が行われるが padding をゼロクリアする機会がないようなケース、たとえば
 「関数の引数」での構造体の受け渡しの際に
 渡す元のバイナリイメージと渡された後のバイナリイメージが同一である保証はない。
 (特定処理系の特定コンパイルオプション採用時に padding を含む複写がされる、というのと別の話)
・メンバに char [] を置いて文字列を保持するだけで破綻する
 (有効文字数が増減するとき '\0' 以後は操作しないで放置プレイなことが多いため)

> 値初期化って2003版からなのですね。
「値初期化 (value-initialize) という文言は 2003 で追加されている。
1998 には zero-initialize と default-initialize の2つしかなかった。

よそんちの様子が垣間見えて、こっちもとても参考になったですよ。
ありがとうございました。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -