質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.34%

  • C

    4010questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

for文におけるiのインクリメントの前置と後置の違いがわかりません

解決済

回答 5

投稿

  • 評価
  • クリップ 0
  • VIEW 22K+

takey

score 275

以下の2つのプログラムは、実行結果はおなじになります。

前置の場合
int i;
for(i=0; i<3; ++i){
   printf("%d\n",i);
}

後置の場合
int i;
for(i=0; i<3; i++){
   printf("%d\n",i);
}

結果
0
1
2

後置の場合はfor文の最後"}"にきたらiがインクリメントされると把握しています。
前置の場合では、どの時点でiの値はインクリメントされるのでしょうか?
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+5

一般に、C言語で記述された式には作用と副作用があります。
意外に誤解されている事ですが、C言語の式の作用は値を返す事だってご存知でしたか?

i = 1 の動作は i = 1 式の結果として 1 を返すことで i に 1 が代入されるのが副作用だって知っていましたか?
多くの場合、直感的な理解とは反対なので注意が必要です。

前置・後置インクリメント演算子の場合、

・ ++i の作用は式の値として、i + 1 の値を返す事、副作用は i に i + 1 の結果が代入される。
・ i++ の作用は式の値として、i の値を返す事、副作用はその後 i に i + 1 の結果が代入される。

以上の違いがあります。

次に、 for(i = 0; i < n; i++) と、for(i = 0; i < n; ++i) などの場合ですが、

++i や i++ が単独で現れて、作用たる式の値が使用されず、副作用の結果のみが使用されるコードの場合にその動作に違いはありません
違いが現れるのは、単独ではなく式の一部として使用されて、その式の値が参照される場合のみです。


話は変わって、実行効率の話ですが、

一般に、前置演算子よりも、後置演算子 i++ や i-- の方が、可能性としてパイプラインハザードを起こしにくいといわれています。
また、プロセッサによっては、プリデクリメント、ポストインクリメントなど --i や i++ に最適なアドレッシングが実装されている事があります。
(記憶が正しければ PDP-11 とか)

いまどきは、微々たる違いですので、あまり気にしなくなってきていますね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

for文にかぎらず、単体でi++;++i;のようにした場合、プログラムの動作にまったく差は出ません。

i++++iの違いは、たとえばfunc(i++)x=++i;のように、これら「自体」の取る値にだけ影響します。「変数をインクリメントする」という目的だけで、値を使わない分には何も変わりません。

なお、i++だと古い値を残す処理が必要になる、と考えて++iで回していることもありますが、実際に速くなるかは環境によります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/11 17:14

    ++iのほうが速度が速くなるというのは初耳でした。

    キャンセル

  • 2015/11/11 17:43

    横から失礼します。

    > 実際に速くなるかは環境によります。
    今時のコンパイラーなら単体で使った場合に出力されるアセンブリコードは全く同じです。よほど古いかおバカさんなコンパイラーを使わない限り、`++i`が速くなるなんてことはありませんので、お好きな方をお使いください。

    ただし、C++の場合は状況が変わりまして、クラスで++演算子をオーバーライドした場合は明確に呼ばれる関数が変わりますので、意識する必要はあるかもしれません。
    が、最適化オプションを入れていれば不要なコードは削除されるので、結果的には同じになることが多いです。

    キャンセル

  • 2015/11/11 23:22

    Google C++ Style Guide では、前置インクリメントが推奨されています。
    どちらの方の説明も正しいです。
    http://www.textdrop.net/google-styleguide-ja/cppguide.xml?showone=%E5%89%8D%E7%BD%AE%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%83%88%E3%81%A8%E5%89%8D%E7%BD%AE%E3%83%87%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%83%88#%E5%89%8D%E7%BD%AE%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%83%88%E3%81%A8%E5%89%8D%E7%BD%AE%E3%83%87%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%83%88
    https://google.github.io/styleguide/cppguide.html#Preincrement_and_Predecrement

    キャンセル

  • 2015/11/12 09:45

    >Google C++ Style Guide では、前置インクリメントが推奨されています。
    C++の場合、後置インクリメントはiの値がコピーされるんですね。今まで何気なく使ってましたが、内部の処理まで考えたことはありませんでした。今度からfor文のイテレータの値は、前置インクリメントを使用することにします。

    キャンセル

checkベストアンサー

+1

同じく、for文の最後"}"ですよ。
iの値を取り出す前にインクリメントするのが前置、iの値を取り出した後でインクリメントするのが後置なので、iの値を取り出していない時、前置/後置に動作上の差はありません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/11 17:14

    同じく”}”でインクリメントされるなら、前置でも後置でもfor文を回す回数は同じですね。
    言われてみれば当たり前でした。ありがとうございました。

    キャンセル

+1

同じです。
for(①;②;③){④}とすると、
① ②④③ ②④③ ②④③ ..... と実行されます。
③が完全に終わった後、②が実行(評価)されるので同じです。
③のi++の代わりに、++iとかとかi+=1とか、i=i+1とか書く人は
何かコダワリか意図があるんでしょう。普通はi++です。
下なら違うので注意が必要です。
    for( i = 0 ; i++ < 3; ){   printf("%d\n",i);    }
    for( i = 0 ; ++i < 3; ){   printf("%d\n",i);    }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/11 19:05

    > ③のi++の代わりに、++iとかとかi+=1とか、i=i+1とか書く人は
    > 何かコダワリか意図があるんでしょう。普通はi++です。

    i++と++iのどちらでも良い時、私は++iを意図的に選びます。
    たぶん、私と同じ++i派の人も一定数いると思いますよ。

    ループを回す時、「変数の値を設定して使う」と「次の使用に備えて変数の値を設定しておく」の2種類の選択肢がある場合が結構あります。
    前者の方が多少なりとデバッグが楽なので、私は前者になるようできるだけ工夫してループを回します。その延長線で気分的に++i が好きなので、++i を選びます。
    後、maisumakunさんとcatsforepawさんがちょっとだけ触れられていますが、++i の方が速い場合が僅かにあるので、どちらでも良いなら++i を選びます。

    ということで単なる好みの範疇ですが、どちらでも良い時に++i と書いているコードを見るとなんとなく嬉しくなっちゃいます。

    キャンセル

  • 2015/11/11 21:55

    たしかに「普通」という表現、あまり良くないですね。ただの感想と言うことですね。
    さて、i++と++iで速度が違うっていうことを私は見たことありませんし、アセンブラ命令にi++と++iに相当するインクリメント命令があったとしても、optimizerがうまくやる前提で、表現力を第一に書くべきです。よね?
    Chironianさんも速度が違うと考えているんですね。速度問題については検証可能な実例があれば、検証してみたい。

    キャンセル

  • 2015/11/11 22:04

    > アセンブラ命令にi++と++iに相当するインクリメント命令があったとしても、optimizerがうまくやる前提で、表現力を第一に書くべきです。よね?
    それについて同意です。気になってアセンブリを出力して確認してみたことがあります。VC++では、i++, ++i, i+=1, i=i+1 全て全く同じコードでした。さすがです。
    ただ、他の方のコメントにも書きましたが、C++のクラスオブジェクトだと意識する必要があるかもしれません。

    キャンセル

  • 2015/11/11 22:40

    NoritakaSuzukiさん、こんばんは。
    後置は一旦オブジェクトをコピーしておき、オブジェクトをインクリメントしてから、コピーしておいた値を返却するのでその分遅くなるのです。
    でも、インライン展開される等により、コンパイラが値を使っていないことを認識できるケースでは最適化が効く筈なのであまり気にする必要はないと思います。

    ところで、検索したら下記サイトに意外なことが書いてありました。
    > http://bokko.hatenablog.com/entry/20080606/1212695409
    > C++で後置インクリメントよりも前置インクリメントが多用される理由
    これはこれでちょっとびっくり。後置で書く人も少なくないと思いますので。

    キャンセル

  • 2015/11/12 00:14

    僅かな事例を以って根拠とすることは、初心者さんにはそれが普通となりかねません。
    有名どころのコンパイラの単純なケースで速度の違いがあるってところを示さないといけないと思うけれど、難しそうですね。

    キャンセル

  • 2015/11/12 14:04

    いや、簡単ですよ。
    私も実際に確認したことはなかったのでやってみました。ちゃんと作れば気にするレベルではないですが、若干差がでるようです。
    msvc2015(32bit)で
    #if 0の場合
      preInc : 3790mSec
      postInc : 5897mSec
    #if 1の場合
      preInc : 3791mSec
      postInc : 3994mSec
    (今の話題とは無関係ですが、ちゃんとコピー・コンストラクタを使いましょうってことですね。)

    #include <iostream>
    #include <Windows.h>

    class test
    {
    private:
    int data;
    public:
    test() {}
    test(int a) : data(a) {}
    test operator++();
    test operator++(int);
    };
    // 前置
    test test::operator++()
    {
    ++data;
    return *this;
    }
    // 後置
    test test::operator++(int)
    {
    #if 0
    test old(*this);
    #else
    test old;
    old=*this;
    #endif
    ++data;
    return old;
    }
    int main()
    {
    DWORD preInc_start=GetTickCount();
    test preInc;
    for (int i=0; i < 1000000000; ++i) ++preInc;
    std::cout << "preInc : " << GetTickCount()-preInc_start << "mSec\n";

    DWORD postInc_start=GetTickCount();
    test postInc;
    for (int i=0; i < 1000000000; ++i) postInc++;
    std::cout << "postInc : " << GetTickCount()-postInc_start << "mSec\n";
    }

    キャンセル

0

あえて違いがあるとしたら、以下のようなケースは、++i とi++ で結果が異なります。

char c_table[5];
int i;
int cnt;

c_table[0] = "a";
c_table[1] = "b";
c_table[2] = "c";
c_table[3] = "d";
c_table[4] = "e";


for(cnt=0 ; cnt < 3 ; cnt++)
{
    i = cnt;
    printf("case i++ : %c\n", c_table[i++]);

    i = cnt;
    printf("case ++i : %c\n", c_table[++i]);

}

結果
case i++ : a
case ++i : b

case i++ : b
case ++i : c

case i++ : c
case ++i : d

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C

    4010questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。