ある行で変数を宣言し、次の行でそれを割り当てるのはなぜですか?


101

CおよびC ++コードでは、次の規則がよく見られます。

some_type val;
val = something;

some_type *ptr = NULL;
ptr = &something_else;

の代わりに

some_type val = something;
some_type *ptr = &something_else;

最初は、これはスコープの最上部ですべてのローカル変数を宣言しなければならなかった時代から残された習慣だと思っていました。しかし、私はベテラン開発者の習慣をすぐに却下しないことを学びました。だから、1行で宣言し、その後に割り当てる理由はありますか?


12
「ベテラン開発者の習慣をすぐに却下しないことを学びました。」の+1 それは学ぶべき賢い教訓です。
ワイルドカード

回答:


92

C

C89では、すべての宣言スコープの先頭({ ... })である必要がありましたが、この要件はすぐに削除されました(最初はコンパイラー拡張機能で、後に標準で)。

C ++

これらの例は同じではありません。some_type val = something;コピーコンストラクターをval = something;呼び出し、デフォルトコンストラクターを呼び出してからoperator=関数を呼び出します。多くの場合、この違いは重要です。

習慣

ある場所では宣言を、別の場所では定義を使用して後でコードを再フォーマットする場合、最初に変数を宣言し、後で定義することを好む人もいます。

ポインターについて、一部の人々は、そのポインターで何をしても、NULLまたはへのすべてのポインターを初期化するだけの習慣をnullptr持っています。


1
C ++の大きな違い、ありがとう。プレーンCではどうですか?
ジョナサンスターリング

13
MSVCは、Cモードでコンパイルしているブロックの先頭を除いて宣言をまだサポートしていないという事実は、私にとって無限の苛立ちの原因です。
マイケルバー

5
@Michael Burr:これは、MSVCがC99をまったくサポートしていないためです。
-orlp

3
「some_type val = something;コピーコンストラクターを呼び出す」:コピーコンストラクターを呼び出すことできますが、標準では、コンパイラーは、temporaryのdefault-construction、valのcopy構築、temporaryの破棄を削除して、val 唯一の引数としてsome_type取るコンストラクタsomething。これは、C ++の非常に興味深い異常なエッジケースです。つまり、これらの操作のセマンティックな意味について推定があることを意味します。

2
@Aerovistae:組み込み型については同じですが、ユーザー定義型については常に同じとは言えません。
orlp

27

質問CとC ++に同時にタグを付けましたが、これらの言語では答えが大きく異なります。

まず、質問のタイトルの文言が間違っています(または、より正確には、質問自体とは無関係です)。どちらの例でも、変数は1行で同時に宣言および定義されます。例の違いは、最初の変数では変数が初期化されていないか、ダミー値で初期化されてから、後で意味のある値が割り当てられることです。2番目の例では、変数はすぐに初期化されます。

第二に、C ++言語では、@ nightcrackerが答えで指摘したように、これら2つの構造は意味的に異なります。最初のものは初期化に依存し、2番目のものは割り当てに依存します。C ++では、これらの操作はオーバーロード可能であるため、結果が異なる可能性があります(ただし、初期化と割り当ての非等価なオーバーロードを作成することはお勧めできません)。

元の標準C言語(C89 / 90)では、ブロックの中央で変数を宣言することは違法です。そのため、ブロックの先頭で初期化されていない(またはダミー値で初期化された)それらの意味のある値が利用可能になった後の値。

C99言語では、ブロックの中央で変数を宣言しても構いません(C ++の場合と同様)。つまり、最初のアプローチは、宣言の時点で初期化子が不明な特定の状況でのみ必要です。(これはC ++にも当てはまります)。


2
@ジョナサンスターリング:私はあなたの例を読んだ。おそらく、CおよびC ++言語の標準用語をブラッシュアップする必要があります。具体的には、これらの言語で特定の意味を持つ宣言定義という用語について。繰り返しますが、どちらの例でも、変数は1行で宣言および定義されています。C / C ++では、行はsome_type val;すぐに変数を宣言し定義しますval。これが私の答えの意味です。

1
そこにあなたの意味がわかります。宣言定義については、私が使用した方法ではなく、無意味であることは間違いありません。貧弱な言葉遣いと、よく考えられていないコメントに対する謝罪を受け入れてください。
ジョナサンスターリング

1
そのため、「宣言」が間違った言葉であるというコンセンサスがある場合、私よりも標準に関する知識のある人がWikibooksページを編集することをお勧めします。
ジョナサンスターリング

2
他のコンテキストでは、declareは正しい単語ですが、declareは明確に定義された概念であるため、結果として、CおよびC ++では、他のコンテキストでできるほどゆるく使用できません。
-orlp

2
@ybungalobill:あなたは間違っています。C / C ++の宣言定義は、相互に排他的な概念ではありません。実際、定義宣言の特定の形式にすぎません。すべての定義は同時に宣言です(いくつかの例外を除きます)。定義宣言(つまり定義)と非定義宣言があります。さらに、通常、サーム宣言は(定義であっても)常に使用されます。ただし、2つの区別が重要な場合のコンテキストを除きます。

13

「ローカル宣言」の時代から残った古い習慣だと思います。したがって、あなたの質問への答えとして:いいえ、私には正当な理由はないと思います。私は自分でやることはありません。


4

私はHelium3による質問への私の答えでそれについて何か言いました

基本的に、何が変更されたかを簡単に確認できるのは視覚的な補助だと言います。

if (a == 0) {
    struct whatever *myobject = 0;
    /* did `myobject` (the pointer) get assigned?
    ** or was it `*myobject` (the struct)? */
}

そして

if (a == 0) {
    struct whatever *myobject;
    myobject = 0;
    /* `myobject` (the pointer) got assigned */
}

4

他の答えはかなり良いです。Cにはこれに関するいくつかの歴史があります。C++では、コンストラクターと代入演算子の間に違いがあります。

追加の点について誰も言及していないことに驚いています。変数の使用と宣言を分けておくと、読みやすくなることがあります。

視覚的に言えば、コードを読むとき、変数のタイプや名前などのよりありふれたアーティファクトは、あなたに飛び付くものではありません。それはあなたが通常最も興味を持ち、ほとんどの時間を凝視することに費やしている文なので、残りを一gする傾向があります。

同じ狭いスペースでいくつかのタイプ、名前、および割り当てがすべて行われている場合、それは少しの情報の過負荷です。さらに、それは私が通常一見するスペースで何か重要なことが起こっていることを意味します。

少し直感に反するように思えるかもしれませんが、これは、ソースをより垂直方向のスペースを大きくすることで改善できる1つの例です。私はこれを、密集したポインターの計算と割り当てを狭い垂直スペースで行うジャムパックされた行を書くべきではない理由に似ています-言語があなたがそのようなことを逃れることができるという理由だけでそれはいつも。:-)


2

Cでは、変数は関数の最初に宣言する必要があったため、これは標準的な方法でした。C++とは異なり、変数は関数本体のどこでも宣言してから使用できます。ポインタがゴミを指し示していないことを確認しただけなので、ポインタは0またはNULLに設定されました。そうでなければ、私が考えることができる大きな利点はありません。


2

変数定義のローカライズとそれらの意味のある初期化の長所:

  • 変数がコードに最初に現れるときに習慣的に意味のある値が割り当てられている場合(同じことに関する別の観点:意味のある値が利用可能になるまで外観を遅らせます)、意味のない値または初期化されていない値で誤って使用される可能性はありません(条件文、短絡評価、例外などのために、初期化が誤ってバイパスされる場合があります)

  • より効率的にすることができます

    • 初期値を設定するオーバーヘッドを回避します(デフォルトの構成またはNULLなどのセンチネル値への初期化)
    • operator= 時には効率が低下し、一時オブジェクトが必要になる場合があります
    • 時々(特にインライン関数の場合)オプティマイザーは非効率性の一部/すべてを取り除くことができます

  • 変数のスコープを最小化すると、スコープ内の変数の平均数が同時に最小化されます:これ

    • それは作るより簡単に精神的に追跡するために、スコープ内の変数、それらの変数に影響を与える可能性があり、実行の流れと文、およびその値のインポートを
    • 少なくとも一部の複雑で不透明なオブジェクトでは、これによりプログラムのリソース使用量(ヒープ、スレッド、共有メモリ、記述子)が削減されます。
  • 定義で変数名を繰り返していないため、最初の意味のある割り当てでより簡潔である場合があります

  • 参照などの特定のタイプに必要であり、オブジェクトを const

変数定義をグループ化するための引数:

  • 時には、多くの変数の型を除外するのが便利かつ/または簡潔です:

    the_same_type v1, v2, v3;

    (理由が型名が長すぎるか複雑であるというだけである場合、typedef時々より良いことができます)

  • ある操作に関係する変数(および型)のセットを強調するために、変数の使用法とは無関係に変数をグループ化することが望ましい場合があります。

    type v1;
    type v2; type v3;

    これにより、タイプの共通性が強調され、変更が少し簡単になりますが、行ごとの変数に固定されているため、コピー、貼り付け、//コメントなどが容易になります。

プログラミングの場合によくあることですが、ほとんどの状況で1つのプラクティスに明確な経験的利点がある場合がありますが、いくつかのケースでは他のプラクティスが圧倒的に優れている場合があります。


新しい変数は同じ名前を使用できますが(つまり、後のステートメントが同じ変数を使用した場合でも、同じ変数を使用した場合でも、別のもの]、コードが複数の場所で書き込み可能でなければならない変数を作成するものから。どちらのユースケースも同じ方法で実行されますが、変数をいつ変更できるかを知ることは、バグを追跡するときに非常に役立ちます。
supercat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.