15章 FOR文

http://www.geocities.jp/ky_webid/c/015.html

whileと似ていますが、forの場合は初期値、条件式、再設定式を明示的に指定できるため、forで出来そうなことはwhileでやらずに積極的にforを利用した方が無限ループの罠に嵌りにくいという話ですね。

インクリメント演算子の話も出てきました。

i = i + 1;
i++;
++i;

これらは全て等価とのこと。

「++」を前につけるのと後ろにつけるのとでは演算結果は同じですが、処理の順番に違いがあるようです。これはとても重要とのことなので忘れないように覚えましょう。

ans = ++num;
ans = num++;

仮に変数numに10が格納されていたとします。前者の意味は「変数numの値を+1して、その結果を変数ansに代入する」 です。後者の意味は「変数numの値を変数ansに代入してから、変数numの値を+1する」です。

ということなのでつまり、前者だとansは11になり、後者だとansは10になる。確かに全然違う結果になるので注意が必要ですね。

また

代入式でさえなければ「++i」でも「i++」でも構いません。どちらの形式もよく使われています(ただし、結果を代入する場合には、前置インクリメントの方が効率的です。その意味で、本サイトでは前置インクリメントの方を優先して使います)。

前置インクリメントの方が効率的とありますが、どう効率的なのかがわかりませんでした。

色々調べてみたところ、いくつかの情報が得られました。

大学の頃、プログラミングの授業で「後置演算子は一旦レジスタに退避させなきゃならないので前置演算子に比べると遅くなる」と習って以来、必要に迫られない限りはできるだけ前置で書くようにしている。

void element blog: あなたは前置派? それとも後置派?

恐らく、VC++などの優秀なコンパイラでは、for文に書いた単一の後置演算子なんかは、レジスタに退避されないコードが最適化によって生成されている(と思う)ので、その辺りに気を配らなくていいからだと思います。

Flashコンパイラは基本的に、特に最適化に関しては信用ならないので、理由が無い限りは前置演算子を書くようにしてます。

記憶が曖昧ですが、少なくともAS2の時は、後置インクリメントは必ずスタックに演算前の値が積まれていたような気がします。

void element blog: あなたは前置派? それとも後置派?

7.5.9 後置インクリメント演算子と後置デクリメント演算子

x++ または x-- の形式の後置インクリメントまたはデクリメント演算子の実行時の処理は、次の手順で構成されています。

x が変数の場合 :

  • x を評価して変数を生成します。
  • x の値を保存します。
  • 保存した x の値を引数として、選択された演算子を呼び出します。
  • 演算子から返される値を x の評価によって得られる場所に格納します。
  • 保存された x の値が、演算の結果になります。

7.6.5 前置インクリメント演算子と前置デクリメント演算

    1. x または --x の形式の前置インクリメントまたはデクリメント演算子の実行時の処理は、次の手順で構成されています。

x が変数の場合 :

  • x を評価して変数を生成します。
  • x の値を引数として、選択された演算子を呼び出します。
  • 演算子から返される値を x の評価によって得られる場所に格納します。
  • 演算子から返された値が、演算の結果になります。
「C#でi++と++iについて」(1) Insider.NET − @IT

iをインクリメントした値を作成し、iに代入する。

(1) work ← i
(2) work ← work + 1
(3) i ← work

※work はマシン内の作業領域と考えて下さい。

iにiを代入してからiをインクリメントした値を作成する。

(1) work ← i
(2) i ← work
(3) work ← work + 1

MSN Japan - ニュース, 天気, メール (Outlook, Hotmail), Bing検索, Skype

なんとなくイメージが湧きました。

内部動作的には前置インクリメントの方が良さげですが、最近のコンパイラによっては最適化の時点で単一の後置インクリメントは無駄なく処理されている可能性が高いということですかね。

なにはともあれ勉強になりました。

前置きが長くなりましたが、ここらで練習問題。

問題1

キーボードから入力された回数だけ、画面に * を表示するプログラムを作って下さい。(これは第14章の練習問題?と同じ問題です。今回はfor文を使って作って下さい)

さっきやったのと同じなので簡単ですね。

int main () {
    int num = 0;
    int i = 0;

    scanf("%d",&num);
    for(i=0;i<num;++i) {
        printf("*");
    }
    return 0;
}

問題なし

問題2

次のプログラムを実行した結果を答えて下さい。

int main(void){
    int i;

    for(i=0; i<5; ++i)  /* 変数iが5未満のときループ */
    {
        switch( i ){
        case 0:
            printf( "0\n" );
            break;
        case 1:
            printf( "1\n" );
            break;
        case 2:
            printf( "2\n" );
            break;
        default:
            if( i >= 3 ){
                printf( "3以上\n" );
            }
            else{
                printf( "0未満\n" );
            }
            break;
        }
    }
    return 0;
}

迷うところはないですね。

実行結果はこうなるはず

0
1
2
3以上
3以上

問題3

1から順に数字を2倍、2倍、2倍・・・としていき、それぞれの結果を画面に表示するプログラムを作って下さい。値が1000を超えた時点で終了させます。つまり、次のような結果になるはずです。

1
2
4
8
16
32
64
128
256
512

1000になるまでループですか。while使う方が良いような気もしますが、forの章なのでforを使って実装してみます。

int main () {
    int i;
    
    for (i=1;i<=1000;i=i*2) {
        printf("%d\n",i);
    }
    return 0;
}

できました。実行結果も完璧です。