• 締切済み

for文内での変数定義

以前にC++の講義を受けた際に for(int i = 0; i < hoge ; i++){    int j;    ・    ・    ・ } のようなコードを書くと、jがhoge分だけ"生成されて しまう"のでよろしくありません。と教えられました。 しかし、最近別の方からこの部分に関しては、jをfor文の 中に定義しようが、最適化?によりfor文の外に出された バイナリが生成されると聞きました。そのため、jが 必要となる直前でjを定義しても(for文の中に定義しても) 問題ない。と教えられました。 どちらが正しいのでしょうか? 近年にC++の仕様変更があったとしたら、それに伴い 変更されたのでしょうか? また、どのようなコードが望ましいなどありましたら 教えてください。

みんなの回答

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.6

「j が hoge 分だけ生成される」というのはちょっと語弊はあるけど, 「j が hoge 回生成される」という意味ならその通り. ただし, 「同時に hoge 個存在する」ということはあり得ないし, それが「良くない」かどうかは別の話. だいたい, これが「良くない」なら「(main 以外の) 関数内部のローカル変数」や「関数の引数」も「良くない」ことになってしまう. で, プログラミングのスタイルとしては「変数のライフタイムはなるべく短くする」というのを原則にしていいと思います. だから, 「for の 1回の繰り返しで生きていればいい (各繰り返しごとに違う変数と思う)」なら for の中で定義することになるし, 「1回の繰り返しを超えて値を保持しなければならない」なら for の前で定義することになります (というかそうするしかない). いずれにせよ「プログラムの通りに動作しているように見えればコンパイラがどのように変えても問題ない」ので, この点について仕様の変更はないはずです. おまけですが, 変数の定義をループ本体に入れた場合, この仕様からコンパイラがどう最適化しても「複数回生成される, ように見える」必要はあります. int だと大して問題ないけど, コンストラクタ (とデストラクタ) をもつクラスでは毎回コンストラクタ/デストラクタを呼び指すことになります.

upanepa
質問者

お礼

ご回答ありがとうございます。

回答No.5

まずは、そんなアホな事を教えている先生を首に・・・ は、おいておいて プログラミングの基本で説明しておきます A.int j;   for (i=0; i<hoge; i++) {    .    .   } というのと B.for (i=0; i<hoge; i++) {    int j;    .    .   } というのはプロからみた場合圧倒的に意味が異なります Aの場合は「このfor以降でjを使用する部分があるので、注意せよ」というのを暗に示しているし(だからjなんて変数名つけるのはバカ) Bの場合は「このループの中で一時的に使用しているワーク変数なので、あんまり気にしなくていい」というのが裏にあります プログラムを「動けばいい」なんて教えている人がいますが、私は間違っていると思います 「美しく、読みやすい」のが最高と思います

upanepa
質問者

お礼

ご回答ありがとうございます。 >「美しく、読みやすい」のが最高と思います 私もそう思います。

  • rentahero
  • ベストアンサー率53% (182/342)
回答No.4

> for(int i = 0; i < hoge ; i++){ >    int j; >    ・ >    ・ >    ・ > } > のようなコードを書くと、jがhoge分だけ"生成されて > しまう"のでよろしくありません。と教えられました。 この例の場合、jは"複数個"生成されるわけではありません。 ただし、この場合において(明示的に)最適化をまったく行わない設定でコンパイルすると、 ループの終了時に変数jが破棄され、(結果として)同じメモリ位置に再度変数jが設定されることになるでしょう。 当然、このコードでは変数jは初期化されていませんので、(再度設定された)変数jの値は直前にループ終了時に入っていた値が 入っているはずです。 また、この(最適化しない)場合では、ループが終了しない(=継続する)場合、破棄した変数を直後に(同じ位置に)設定するという (それ自体にあまり意味のない)処理が起こりうることになります。 つまり、"複数回"生成される、という説明に(一応は)嘘はありません。 ただし、これは「最適化をまったく行わない」ケースにおいてのみです。 実際には最小限の最適化を行うことがふつうです。 ですから > 最近別の方からこの部分に関しては、jをfor文の中に > 定義しようが、最適化?によりfor文の外に出された > バイナリが生成されると聞きました。 お聞きになった内容というのは、つまりコンパイラによる最適化によって、先のコードは {   int j; //←変数を設定する   for(int i = 0; i < hoge ; i++){     ・     ・     ・   } //←ここでは変数を破棄しない } //←ここで変数をスタックから破棄する こう書いても同じ機械語コードが生成されることになるはずだという意味です。 当然 for(int i = 0; i < hoge ; i++){    int j=1; //←変数を設定すると同時に初期化    ・    ・    ・ } //←変数を破棄する こうなっている(変数を初期化している)場合は {   int j; //←変数を設定する   for(int i = 0; i < hoge ; i++){     j=1; //←初期化     ・     ・     ・   } //←ここでは変数を破棄しない } //←ここで変数を破棄する こうなるであろうことは容易に想像できます。 > どちらが正しいのでしょうか? 上記の理由により、「どちらも正しいが、前提条件が違うため比較することに意味がない」といえるでしょう。 もっと突っ込んでいうと、最適化をまったく行わないコンパイラは通常存在しないため、 「複数回生成される」という説明のほうが「非現実的」であるといえるでしょう。 > また、どのようなコードが望ましいなどありましたら教えてください。 この点については、あなたが理解しやすいコードであるべきです。 現在のコンパイラの最適化機能は大変優れています。 コーディングに多少工夫を凝らしたところで、同じ機械語コードに翻訳されたり、 場合によってはかえって効率の悪いコードが生成されることすらあります。

upanepa
質問者

お礼

ご回答ありがとうございます。

回答No.3

コンパイラには通称「as if」ルールというのがあります。 これは 「結果が変わらなければ、コンパイラは何をしてもよい」 というものです。 つまり、二人目の方が仰られているように、int j;がforの 外に置かれようが、中に置かれようが、結果が変わらなければ コンパイラはどっちに置いても良い。ということになります。 スタックにintを置くコストが多少はあるので、今回のような 場合は、forの外に出して「最適化」をコンパイラが"してもいい" ということになります。 さて、どのようなコードが望ましいかについてですが、 「一般的にfor文中に入れたほうが良い」ということになっています。 これは、Code Completeの上巻で述べられていることなのですが、 変数のBindTime(変数を変化させられる時間)は「実際に利用する時間」 に近ければ近いほど良い、つまりfor文の中でしかint j;を利用しない のであればそれより大きいスコープ(for文の外)には置くべきではない ということになっています。 (そして私もそう思います)

upanepa
質問者

お礼

ご回答ありがとうございます。 基本型については、for文の中に入れても 問題なさそうですが、クラス変数については、 場合によってはfor文の外に宣言したほうがいいのかな・・ と思いました。(後番の回答者さんが記述されているように、 コンストラクタとデストラクタがfor文の1回に実行中に 1回づつコールされてしまうため)

  • eroermine
  • ベストアンサー率18% (83/444)
回答No.2

>のようなコードを書くと、jがhoge分だけ"生成されて そんなアホなコンパイラはこの世にありません。 自動変数の自動を動的生成と勘違いしたのでしょう。 自動変数の自動は関数が呼ばれたときに自動的にスタック上に作られる、 ということで関数が呼ばれなければ作られません。 Cも同様。 簡単な関数のアセンブラ出力を眺めれば一目瞭然かと。 ただし、 この世にはスタックの無いコンビューター IBM360系等もありまして、 その場合は動的生成ですね。たぶん。でもうまくやってるでしょう。

upanepa
質問者

お礼

ご回答ありがとうございます。

  • notnot
  • ベストアンサー率47% (4848/10262)
回答No.1

実装の話なので、正解は「色々ある」ですが、一般論で「普通はこう」というのを書きます。 ローカル変数はスタック上に生成しますが、それは関数の入り口でまとめて行います。jへのアクセスはforのブロック内でしかできませんが、変数自体は関数の開始から終了まで存在します。 もちろん、int j=1; などと初期化式を書いた場合はそれは繰り返し回数だけ実施されます。

upanepa
質問者

お礼

ご回答ありがとうございます。

関連するQ&A

  • 教えて下さい。for文内での変数宣言について

    Javaのプログラムで困っています。 for文内でfor分だけ変数を宣言したいのですが、 その際変数名に数字(ナンバリング)を付与した形にしたいと考えています。 例) String Test1 String Test2 : : 上記の様な場合、for文をどの様に作成したらいいですか? num = 5; for(int i = 0; i < num; i++){ String Test = null; } ⇒このままだと変数Testが1つしか出来ない・・・   変数の後にナンバリングしたいのですが上手くいきません。 numの数はユーザの指定で毎回変更されるため、 for文でその数だけ変数を作成したいと考えています。 どなたかご教授宜しくお願い致します。

    • ベストアンサー
    • Java
  • (C++) for文内でのインスタンス生成について

    はじめまして。 いつも色々と質問を読まさせて頂いています。 早速タイトルの質問についてですが、for文の中でループさせながらインスタンスを生成しようとしています。 for (int i=0; i<10; i++) { MyObject *obj = new MyObject(); int intX = new int(100); // メモリのアドレスを確認 printf("%p¥n", &obj); printf("%p¥n", &intX); } この結果、 0xbfffd7dc 0xbfffd7f0 0xbfffd7dc 0xbfffd7f0 0xbfffd7dc 0xbfffd7f0 0xbfffd7dc 0xbfffd7f0 . . のようになり、どうも同じメモリアドレスを参照したインスタンスが生成されているようです。 やりたい事は、10回のインスタンスの生成をforで手抜きして行いたいのですが、この方法ですとすべて同じインスタンスとなってしまい、 別の処理でとある一つのインスタンスに変更を加えると、すべてのインスタンスにも変更がかかってしまいます。 これを実現する方法はあるのでしょうか。 解決方法として、forを使わずに、 MyObject *obj1 = new MyObject(); MyObject *obj2 = new MyObject(); . . とやれば別アドレスの参照となることは分かったのですが。。。 初歩的な質問だと思いますが、お知恵をお借りできればと思います。 よろしくお願いします。

  • for文内での関数の使用がうまくいきません。

    いつもお世話になってます。flash初心者です。 環境:flash 2004 MX, WIN XP ボタンが3つ、それぞれ a0, a1, a2~ a(n個)という名前で存在します。このボタンを押したときに変数の値を代入するためのスクリプトを作りました。 文が長くて、for文を使って、繰り返しの処理を省略したいのですが、for文内で、変数に 配列の値を代入することに失敗してしまいます。 まず、ダイナミックテキストの変数名を myhoge にする。 //各ボタン用のテキストを定義 var hoge = new Array(); hoge[0] = "0番のボタンを押したときのテキスト"; hoge[1] = "1番のボタンを押したときのテキスト"; hoge[2] = "2番のボタンを押したときのテキスト"; ↓ hoge[99] = "99番のボタンを押したときのテキスト"; for (var i:Number = 0; i <= 99; i++) { _root["a"+i].onRelease = function() { _root.myhoge = _root.hoge[i]; } } 上記で、該当ボタンをクリックしたときは、ボタンは反応するのですが、配列のところがうまくいってないみたいです。 原因がわかる方いらっしゃいましたらお願いいたします。

  • 変数に入る値

    以下の1部のコードがあるとします。 #include<stdio.h> #define N 3 #define M 3 int main(){ float i,j,k;    ・    ・ここのプログラムは省略します。    ・ for(i=0; i<N; i++){ for(j=0; j<N; j++){ for(k=0; k<M; k++){ c[i][j] += a[i][k] * b[k][j]; }}} このようなコードがあった場合、c[i][j] += a[i][k] * b[k][j];のところで 3個目のfor文の1回目のループの時、 c[i][j] += a[i][k] * b[k][j];の[i][j]や[i][k]、[k][j]にはいる値は c[0.000000][0.000000]+=a[0.000000][0.000000]~~~~~~~~ とかんがえられるでしょうか? i,j,kはint型のほうがよいですが、確認のためfloatにしました。 意味不明な質問ですみません。 よろしくお願いします。

  • for文での変数宣言について(他言語共通)

    for(var i = 0; i < 9; i++) { } このように、for文の()内で変数宣言をすることがあるだろうと思います。 これを入れ子にするのならば当然(場合にもよりますが) for(var i = 0; i < 9; i++) { for(var j = 0; j < 9; j++) { } } このように、新しい変数を定義します。 しかし、次のように、2つのfor文が並列する場合、2つ目のfor文で変数宣言をすべきではありません。 ですが、 片方だけに宣言がある・for文の外に宣言がある・2つ目のfor文には別の変数を宣言する いずれもしっくり来ません。 どのように書くのが一般的なのでしょうか。 皆様の書き方をご投稿ください。よろしくお願いします。 //ここでvar i = 0;と書くか … A for(i = 0; i < 9; i++)//ここでfor(var i = 0; i < 9; i++)と書くか … B { } for(i = 0; i < 9; i++)//ここでfor(j = 0; j < 9; j++)と別の変数を定義するか … C { } //または、いずれでもない回答者様のオリジナルの書き方 ※この例では、1つ目のfor文が終わった時点でiの値を保持する必要はありません。

  • C++のfor文について

    C++では for ( int i = 0, j = 0; i < 10; i++ ) { } みたいな感じでforのなかで変数の宣言ができます。 ところで上の例ですとint型のiとjを2つ宣言していますが、 もしint型とchar型を宣言したい場合などは char j; for ( int i = 0; i < 10; i++ ) { } みたいにしないといけないのでしょうか? for ( int i = 0, char j; i < 10; i++ ) { } みたいにできると思ってやったらエラーになってしまうので・・。

  • サブルーチン、グローバル変数がわかりません。

    2つの行列の計算をサブルーチン関数とグローバル変数を使って行いたいのですが、サブルーチン関数を宣言する為の、プロトタイプ宣言やプロトタイプ定義や、グローバル変数など、 調べてもよく理解できません。 とりあえず、二次元配列を用いたソースコードを書いてみました。 (1)14行目と23行目からのfor文、(2)40行目と51行目と62行目からのfor文を1つにまとめて、最初のプロトタイプ宣言は void 関数(double a[LINE][COLUMN] , double b{LINE][COLUMN] , double c[LINE][COLUMN]) ; にすればいいと思うのですが、 その後は、どのようにすればいいのでしょうか? あと、scanfも使って、aとbも入力できるようにしたいです。 1 #include <stdio.h> 2 3 #define LINE 3 4 #define COLUMN 3 5 6 int main(int argc, char *argv[]) 7 { 8 double a[LINE][COLUMN] ; 9 double b[LINE][COLUMN] ; 10 double c[LINE][COLUMN] ; 11 int i ; 12 int j ; 13 14 for(i= 0;i < LINE ; i++) 15 { 16 for(j = 0 ; j<COLUMN ; j++) 17 { 18 printf("A[%d][%d]=", i+1 , j+1 ); 19 scanf("%f", &a[i][j]); 20 } 21 } 22 23 for(i =0;i < LINE ; i++) 24 { 25 for(j = 0 ; j<COLUMN ; j++) 26 { 27 printf("B[%d][%d]=", i+1 , j+1 ); 28 scanf(" %f", &b[i][j]); 29 } 30 } 31 32 for(i =0;i < LINE ; i++) 33 { 34 for(j = 0 ; j<COLUMN ; j++) 35 { 36 c[i][j] = a[i][j] + b[i][j] ; 37 } 38 } 39 40 printf("\n行列A:\n"); 41 for(i =0;i < LINE ; i++) 42 { 43 for(j = 0 ; j<COLUMN ; j++) 44 { 45 printf("%10.5f" , a[i][j]) ; 46 } 47 48 printf("\n"); 49 } 50 51 printf("\n行列B:\n"); 52 for(i= 0;i < LINE ; i++) 53 { 54 for(j = 0 ; j<COLUMN ; j++) 55 { 56 printf("%10.5f" , b[i][j]) ; 57 } 58 59 printf("\n"); 60 } 61 62 printf("\nC(和):\n"); 63 for(i= 0;i < LINE ; i++) 64 { 65 for(j = 0 ; j<COLUMN ; j++) 66 { 67 printf("%10.5f" , c[i][j]) ; 68 } 69 70 printf("\n"); 71 } 72 return 0 ; 73 }

  • C言語のfor文について

    C言語のfor文について C言語初心者です。質問させていただきます。 output.datというバイナリファイルに値を書き込みたい(100~149)のですが、どうもうまくいきません。 値が半分だけ正常に入って、あとの半分はおかしな値が入ります。 どなたか分かる方教えていただけないでしょうか。 ソース(一部分)↓ int main(){ int i,j; char output[456]; //ファイルに値を入れる for(i=0,j=100;i<456,j<556;i++,j++){ output[i]=j; } //バイナリ書き込みモードでファイルをオープン if((fp = fopen("output.dat","wb")) == NULL) { printf("<output.dat> file open error\n"); exit(1); } //値をファイルに書き込む fwrite(output,456,1,fp); fclose(fp); exit(0); }

  • for文内の計算時に出るエラーについて

    配列に格納している値を使って計算するプログラムを作成しているのですが、以下のエラーが出てプログラムを実行していくことができません。2次元配列に格納している値をfor文を回して加算するプログラムになっています。プログラム・エラーメッセージと以下に載せていますので、解決策・改善策がお分かりになる方はぜひ教えていただきたいと思います。よろしくお願い致します。 使っているソフトはVisual Studio2010(Visual C++)のWindowsFormアプリケーションです。 プログラム: for(j=1;j<=768;j++){ for(i=1;i<=10;i++){ a[i][j].num+=b[i][j].num } } エラー内容: ランタイムの重大なエラーが発生しました。エラーのアドレスは 0x690c169f、スレッド 0x19e4 です。エラー コードは 0xc0000005 です。これは CLR のバグであるか、またはユーザー コードのアンセーフまたは確認不可能な部分にバグがある可能性があります。このバグの一般的な原因には、スタックが壊れる可能性のある COM-interop または PInvoke のユーザー マーシャリング エラーが含まれています。

  • for文におけるiなどの整数の使用回数

    for文は int i; for(i=0;i<10;i++) { 式や文 } のように使いますが、このiは何度でも使ってもよいのでしょうか? たとえば、 int i,j; for(i=0;i<10;i++) { for(j=0;j<10;j++) { 式や文 } } for(i=0;i<10;i++) { for(j=0;j<10;j++) { 式や文 } } としてもよいのか、あるいは、 int i,j,k,l; for(i=0;i<10;i++) { for(j=0;j<10;j++) { 式や文 } } for(k=0;k<10;k++) { for(l=0;l<10;l++) { 式や文 } } としなければいけないのかということです。 一応調べてはみたのですが、確証が持てないので… よろしくお願いします。