C, Java の変数の定義位置についての質問です。


C では「オート変数はブロックの先頭で定義する」スタイルが多く、
たしか Java の coding convention でもそれが推奨されていたと思います。
このスタイルは、変数の定義と使用箇所が離れるため読みにくいと思うのですが、
なぜ推奨されているのでしょうか?

ちょっと考えると「そのブロックで使用されている変数が一覧できる」というメリットがあるような気がしますが、
このスタイルが守られない場合も多いため、実際には「一覧」にはならないとおもいます。
そのメリットを活かすためには「ブロックの最初以外で定義したら文法エラー」とすれば良いと思うのですが、
そうなってません。

* どういうメリットがあるのか?
* なぜ文法で強制しないのか?
について教えてください。

回答の条件
  • 1人5回まで
  • 登録:2008/03/23 13:28:38
  • 終了:2008/03/23 23:05:07

ベストアンサー

id:pyopyopyo No.1

pyopyopyo回答回数337ベストアンサー獲得回数792008/03/23 14:52:05

ポイント20pt

古いC言語では、オート変数はブロックの先頭でしか定義できませんでした。これは言語仕様で定められています。

そのため厳密なCコンパイラでは、現在でも「ブロックの最初以外で定義したら文法エラー」となります。

おそらくこのような歴史的経緯が背景にあるのだと思います。

そして「変数の定義と使用箇所が離れるため読みにくい」という理由から、

最近のC言語の仕様では、自動変数はどこでも宣言できるように仕様が拡張されています。

そしてコンパイラのオプションで、「ブロックの最初以外で定義したら文法エラー」とするか否かをプログラマが

選択できるようになっています。

ですので、

* どういうメリットがあるのか?

については、メリット、デメリットがあるが、それ以外に歴史的経緯もある。

* なぜ文法で強制しないのか?

C言語に限れば、コンパイラのオプションで選択できる

ということになります。

id:imsut

ありがとうございます。

「変数の定義と使用箇所が離れるため読みにくい」と感じていたのは私だけではないのですね。

なぜ Java がそのスタイルを推奨しているのか不思議です。

調べたところ C99 でブロック途中での定義が可能になったようですね。

2008/03/23 15:04:13

その他の回答(8件)

id:pyopyopyo No.1

pyopyopyo回答回数337ベストアンサー獲得回数792008/03/23 14:52:05ここでベストアンサー

ポイント20pt

古いC言語では、オート変数はブロックの先頭でしか定義できませんでした。これは言語仕様で定められています。

そのため厳密なCコンパイラでは、現在でも「ブロックの最初以外で定義したら文法エラー」となります。

おそらくこのような歴史的経緯が背景にあるのだと思います。

そして「変数の定義と使用箇所が離れるため読みにくい」という理由から、

最近のC言語の仕様では、自動変数はどこでも宣言できるように仕様が拡張されています。

そしてコンパイラのオプションで、「ブロックの最初以外で定義したら文法エラー」とするか否かをプログラマが

選択できるようになっています。

ですので、

* どういうメリットがあるのか?

については、メリット、デメリットがあるが、それ以外に歴史的経緯もある。

* なぜ文法で強制しないのか?

C言語に限れば、コンパイラのオプションで選択できる

ということになります。

id:imsut

ありがとうございます。

「変数の定義と使用箇所が離れるため読みにくい」と感じていたのは私だけではないのですね。

なぜ Java がそのスタイルを推奨しているのか不思議です。

調べたところ C99 でブロック途中での定義が可能になったようですね。

2008/03/23 15:04:13
id:syuzabu No.2

syuzabu回答回数155ベストアンサー獲得回数22008/03/23 14:54:11

ポイント15pt

こんにちは。

> このスタイルは、変数の定義と使用箇所が離れるため読みにくいと思うのですが

> ちょっと考えると「そのブロックで使用されている変数が一覧できる」というメリットがあるような気がしますが、

読みにくいと感じるのは imsut さんの主観ですよね。

僕は後者の変数が一覧出来た方が見やすいと思います。(コレも主観)


> このスタイルが守られない場合も多いため、実際には「一覧」にはならないとおもいます。

う~ん、ちゃんとした現場ではコーディング規約があるので

変数の宣言位置についてはそれに沿って守られるハズです。

・・・規約があるのに守らない人がいるのは組織として問題があります。(^-^;)


> * どういうメリットがあるのか?

例えば関数の中にA、Bと二つの分岐処理があったとします。

AとBでは全く異なる変数を使用するとします。


関数の初めで変数宣言を行うとその分のメモリを使用しますがAの分岐処理ではB用の変数、

Bの分岐処理ではA用の変数宣言分が不要で、無駄なメモリ消費となります。


分岐処理内で変数宣言を行うとA、Bは必要な分だけメモリを使用出来る形となります。


メモリ制限がある環境などでは後者の方が有利です。

メモリ制限がない環境では前者の方が一般的に見やすいとされてます。


・・・のように、ある程度自由度を持たせてあげないと

様々な分野のソフト開発には向かない言語となってしまうからではないでしょうか。


長文になりましたが、なんとなくこんな感じで。

id:imsut

> 関数の初めで変数宣言を行うとその分のメモリを使用しますがAの分岐処理ではB用の変数、

> Bの分岐処理ではA用の変数宣言分が不要で、無駄なメモリ消費となります。

> 分岐処理内で変数宣言を行うとA、Bは必要な分だけメモリを使用出来る形となります。

本当ですか?

その場合、その変数はヒープに確保されるのでしょうか?

スタックに確保されるなら、そのような挙動にはならないと思いますが・・・。

そういう動作をするコード例・環境が知りたいです。

2008/03/23 16:07:32
id:pahoo No.3

pahoo回答回数5960ベストアンサー獲得回数6332008/03/23 15:11:08

ポイント25pt

変数宣言の位置について明確なガイドラインがあるのかどうか分からなかったので、以下は個人的な意見になります。

どういうメリットがあるのか?

コンパイラとしては、ブロックの冒頭で宣言してくれると助かります。

20年以上前の話ですが、ミニコン用のCコンパイラを移植している際に苦労したのがregister変数の扱いです。文字通りCPUレジスタに変数を割り当てるので、当時の、ただでも貧弱なCPUのレジスタをフル回転させるため、register変数の定義の有無で、それ以降のコードのアルゴリズムを変更していました。

register変数は関数の途中でも宣言できるわけですが、シーケンシャルにコンパイルしていたものですから、途中にregister変数があらわれると、関数の冒頭までさかのぼるってコンパイルし直すことをしていました。

また、一般的なauto変数についてはスタック領域に確保していました。スタックは1本しかないため、関数の冒頭で一気に定義し、関数終了時に一気に解放した方が、安定性の高いコードを生成することができました。関数処理の途中で、ジャンプやループ制御のためにスタック領域を共有するためです。

今はこんな厄介なことは起きないのですが、コーディングの慣習は、こうした過去の経緯を引きずっているのではないでしょうか。

なぜ文法で強制しないのか?

プログラムの不具合を防ぐ無意味では、auto変数には必要十分なスコープを持たせる必要があります。たとえばループ制御変数なら、ループ文の直前に宣言すれば良いと思います。ブロック冒頭で定義することは、必ずしも必要ではないからです。

ところが、変数の宣言手段を用意している言語は多いのですが、明示的に廃棄する手段は定義されていません(言語によっては変数を廃棄するメソッドが用意されていますが)。当該ブロックが終了した時点で自動的に破棄されるというのが暗黙のルールになっていますね。

ブロック末尾で廃棄されるということは、プログラムの対称性を鑑みて、宣言する位置もブロック冒頭の方が良いという考えがあります。

いずれにしても、廃棄する方式が定義されない限り、宣言する位置も強制することはできないということではないでしょうか。

id:imsut

> 20年以上前の話

こういう話を聞くのは楽しいですね。

プロセッサ性能による制約というのはたしかにありそうです。今はないでしょうけど。

> 一般的なauto変数についてはスタック領域に確保していました。

今はそれが普通ですよね。

それにしても、関数内で使用するすべての変数のサイズの合計を求めて、

その分 SP をずらせば良いので、定義位置とは関係ない気がしますが・・・。

「ブロックの先頭だけ見れば「すべての変数」が分かるから簡単」ということですかね。

> 明示的に廃棄する手段は定義されていません

brace ({, }) で囲えば、意味的には廃棄されますね。領域は多分解放されませんが。

> ブロック末尾で廃棄されるということは、プログラムの対称性を鑑みて、宣言する位置もブロック冒頭の方が良いという考えがあります。

その観点はありませんでした。たしかに「美しさ」という意味ではそうかもしれません。

「美しさ」に対するデメリットが大きすぎるとは思いますが。


コンパイラの移植とはおもしろそうなお仕事ですね。

大変参考になりました。

2008/03/23 16:24:49
id:tarutatta No.4

tarutatta回答回数20ベストアンサー獲得回数02008/03/23 15:11:49

ポイント15pt

Cでのお話になりますが、昔は関数の先頭でオート変数を定義しなければエラーになっていたはずです。

たしか、1999年に策定され、C++ の機能の一部を取り込んだ ISO/IEC 9899:1999 - Programming Language C(C99)からどこで宣言してもよくなったはずです。

(これは私の推測ですが、)昔の非力なコンピュータでコンパイルするとき、コード解析に時間がかからないようにするためにそうなっているのではないかと思います。

おそらく、メリットはそれだけです。コンピュータの処理速度が向上した現在ではデメリットしか生まないような気がします。

関数の頭でカウンタ変数を宣言すると、いらないバグを生むことになりますし、使用部位と宣言部位が離れるとコーディングの効率が悪くなります。

id:imsut

> 関数の頭でカウンタ変数を宣言すると、いらないバグを生む

まったく同感です。この質問もこのバグに当たったことがきっかけです。

今のコンパイラ・プロセッサなら、

変数の宣言位置によってコンパイル時間が(体感できるほど)変わるとは思えないですし。

自分で書いといて何ですが「変数がブロックの先頭で定義されていると、

一覧できるから便利」というのは、具体的に何が便利なんですかね?

2008/03/23 16:12:26
id:Bookmarker No.5

しおり回答回数191ベストアンサー獲得回数342008/03/23 19:02:07

ポイント15pt

◆どういうメリットがあるのか?


□Java の場合

Java プログラミング言語に関するコード規約 - Declarations - Placement」には以下のように書かれていますが、よくわかりませんね。

Put declarations only at the beginning of blocks. (A block is any code surrounded by curly braces "{" and "}".) Don't wait to declare variables until their first use; it can confuse the unwary programmer and hamper code portability within the scope.

コード例を含めて超意訳すると、メリットはこんな感じでしょうか。

  • 内側のスコープの変数宣言が外側のスコープの同名の変数を隠すことがないので、不注意なプログラマーが混乱しない。
  • ブロックの先頭でしか宣言できない言語へ移植し易い。(Java の歴史は知らないので、Java 自身ブロックの先頭でしか宣言できない時代があったかは知りません。)

□C の場合

  • C99 に対応していないコンパイラーでもコンパイルできる。(古い環境へ移植し易い。)

◆なぜ文法で強制しないのか?


  • 定数の初期化は、代入と同時にしかできないから。

また Java の場合、上記のコード規約でループ用の変数の宣言は例外としています。

id:imsut

ありがとうございます。

でも、あまり説得力を感じられませんね・・・。

> 定数の初期化は、代入と同時にしかできないから。

ブロックの途中の計算結果を定数に入れたいという状況もよく分からないです。

2008/03/23 22:53:25
id:ken33jp No.6

ken33jp回答回数928ベストアンサー獲得回数132008/03/23 19:52:08

ポイント15pt

>このスタイルは、変数の定義と使用箇所が離れるため読みにくいと思うのですが、

>なぜ推奨されているのでしょうか?

読みにくいと感じるなら、プログラムの書き方が、細分化されて記述されてないから

という可能性もあります。

>ちょっと考えると「そのブロックで使用されている変数が一覧できる」というメリットが

>あるような気がしますが、

auto変数はそのブロックだけなので、あまり意味はないと考えます。

>このスタイルが守られない場合も多いため、実際には「一覧」にはならないとおもいます。

for(int i=0;i<10;i++)のように明らかに使い捨ての変数とかは直前に宣言して

書いたほうが読みやすいと思いますよ。

>そのメリットを活かすためには「ブロックの最初以外で定義したら文法エラー」とすれば良いと

>思うのですが、

>そうなってません。

Cも昔はそうでした。

>* どういうメリットがあるのか?

使い捨て変数は、変数名も気にせずその場で宣言してすてられます。

>* なぜ文法で強制しないのか?

直前に宣言したほうが分かりやすいことがあると分かったので

そういう方式を取り入られれたからです。

id:imsut

> 使い捨て変数は、変数名も気にせずその場で宣言してすてられます。

これは「変数定義をブロックの最初に限定しない」場合のメリットですよね。

「ブロックの最初に限定するメリットはない」ということですね?

2008/03/23 22:55:54
id:syuzabu No.7

syuzabu回答回数155ベストアンサー獲得回数22008/03/23 20:18:09

ポイント15pt

> スタックに確保されるなら、そのような挙動にはならないと思いますが・・・。

混乱させてしまい申し訳ありません。

変数のスコープとメモリ管理を混同して、適当なことを言いました。(妄想?)

動作確認した所、imsutさんの言われる通り、

関数内で変数を宣言した分スタックが確保されました。

以後、お答えする際には妄想は控えます。ではでは。(^-^;)

id:imsut

現在のコンパイラだとそうなると思います。

ただ、pahoo さんが書いているように、

auto 変数をスタックに取るのは C の仕様ではないので、

syuzabu さんのいうような挙動をする環境もある(あったの)のかもしれません。

2008/03/23 22:57:52
id:pahoo No.8

pahoo回答回数5960ベストアンサー獲得回数6332008/03/23 20:48:28

ポイント25pt

#3の補足です。

>それにしても、関数内で使用するすべての変数のサイズの合計を求めて、

>その分 SP をずらせば良いので、定義位置とは関係ない気がしますが・・・。

その通りなのですが、悪いコードへの対応が面倒で――

ループの途中でbreakされたり、さらにはgotoで脱出されたりと‥‥。

id:imsut

ありがとうございます。

コンパイラについては詳しくないのですが、

SP の設定・復帰は prologue, epilogue で行うのではないでしょうか?

ループ中の break や goto での脱出だと epilogue を通らないのでしょうか?

・・・ちょっと質問の趣旨とずれてしまったので、

この辺は自分で勉強します。

とてもおもしろいお話をありがとうございました。

2008/03/23 23:03:52
id:t_shiono No.9

t_shiono回答回数256ベストアンサー獲得回数222008/03/23 22:07:18

ポイント15pt

ここでいうJavaのcoding conventionとは何を指していますか?

俗にいうコーディング規約は、そのプロジェクトごとに多少変化を設けることも必要だと思っています。

例えばですが、オブジェクト指向倶楽部のJavaのコーディング規約には、この宣言の箇所については、一切触れられていません。

強いて解釈すると、

・Javaの言語仕様に準じる

  つまり、宣言位置は先頭でなくてもよい

   or

  プロジェクトごとに決める

・先頭でまとめては行わない

  (50)宣言と初期化の部分を強引に解釈すると、

   「初期化と宣言を同時に行うべき」→「初期値が決定する、初期化すべき場所で宣言する」

と受け取れて、そうすると、どちらで解釈しても先頭で宣言すべきでないというのが妥当かと思っています。

他の方がおっしゃっているように、従来Cにおいては関数の先頭で宣言する必要がありました。

それが、C99やC++、あるいは、Javaではどの場所でも宣言できるようになったのには、それなりの理由があったはずです。と考えれば、先頭で宣言すべきというのは、決してよい方法ではないと認識しています。

ただ、プロジェクトによってはこれを推奨した方がよい場合もあります。それは、ベテランのC屋さんが多いプロジェクトの場合です。この場合、多くの人が先頭での宣言のコードになれていますので、その人たちに読みやすいコードを規約とすることはありだと思います。

余談ですが、以前関わったC++プロジェクトで、できるだけCっぽく書いてくださいといわれて、なるべく先頭で宣言したことはあります。

何かの参考になれば。

id:imsut

> ここでいうJavaのcoding conventionとは何を指していますか?

http://java.sun.com/docs/codeconv/

です。具体的には

http://java.sun.com/docs/codeconv/html/CodeConventions.doc5.html...

ですね。

> と考えれば、先頭で宣言すべきというのは、決してよい方法ではないと認識しています。

確かにそう考えた方が自然な気がします。

ただ、私が見たことがあるコードは、

ブロック先頭で変数定義しているものがほとんどなので、

何かメリットがあるのではないかと考えていました。

2008/03/23 23:03:54
  • id:airplant
    好みの問題もあると思いますが、、、

    カウンターなどの使い捨て変数は、途中でもいいと思います。
    しかし、ブロック内で意味を持たせて使う他の変数は、先頭にまとめて定義してある程度その変数を元にプログラムを書いていったほうが品質や効率が良くなるであろうと考えています。

    歴史のほうの人間なので、途中で宣言すると、どれを何に使っているのかわけ分からなくなる古いタイプの人間でした。

この質問への反応(ブックマークコメント)

トラックバック

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません