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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

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

Q&A

解決済

5回答

77056閲覧

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

takey

総合スコア312

C

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

2グッド

0クリップ

投稿2015/11/11 07:51

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

前置の場合

C

1int i; 2for(i=0; i<3; ++i){ 3 printf("%d\n",i); 4}

後置の場合

C

1int i; 2for(i=0; i<3; i++){ 3 printf("%d\n",i); 4}

結果

C

10 21 32

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

ikuwow👍を押しています

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

一般に、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 とか)

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

投稿2015/11/11 10:21

T.Kanno

総合スコア915

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

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

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

投稿2015/11/11 08:00

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takey

2015/11/11 08:14

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

2015/11/11 08:43

横から失礼します。 > 実際に速くなるかは環境によります。 今時のコンパイラーなら単体で使った場合に出力されるアセンブリコードは全く同じです。よほど古いかおバカさんなコンパイラーを使わない限り、`++i`が速くなるなんてことはありませんので、お好きな方をお使いください。 ただし、C++の場合は状況が変わりまして、クラスで++演算子をオーバーライドした場合は明確に呼ばれる関数が変わりますので、意識する必要はあるかもしれません。 が、最適化オプションを入れていれば不要なコードは削除されるので、結果的には同じになることが多いです。
takotakot
takey

2015/11/12 00:45

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

0

同じです。
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 08:24

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/11 10:05

> ③のi++の代わりに、++iとかとかi+=1とか、i=i+1とか書く人は > 何かコダワリか意図があるんでしょう。普通はi++です。 i++と++iのどちらでも良い時、私は++iを意図的に選びます。 たぶん、私と同じ++i派の人も一定数いると思いますよ。 ループを回す時、「変数の値を設定して使う」と「次の使用に備えて変数の値を設定しておく」の2種類の選択肢がある場合が結構あります。 前者の方が多少なりとデバッグが楽なので、私は前者になるようできるだけ工夫してループを回します。その延長線で気分的に++i が好きなので、++i を選びます。 後、maisumakunさんとcatsforepawさんがちょっとだけ触れられていますが、++i の方が速い場合が僅かにあるので、どちらでも良いなら++i を選びます。 ということで単なる好みの範疇ですが、どちらでも良い時に++i と書いているコードを見るとなんとなく嬉しくなっちゃいます。
退会済みユーザー

退会済みユーザー

2015/11/11 12:55

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

2015/11/11 13:04

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

2015/11/11 13:40

NoritakaSuzukiさん、こんばんは。 後置は一旦オブジェクトをコピーしておき、オブジェクトをインクリメントしてから、コピーしておいた値を返却するのでその分遅くなるのです。 でも、インライン展開される等により、コンパイラが値を使っていないことを認識できるケースでは最適化が効く筈なのであまり気にする必要はないと思います。 ところで、検索したら下記サイトに意外なことが書いてありました。 > http://bokko.hatenablog.com/entry/20080606/1212695409 > C++で後置インクリメントよりも前置インクリメントが多用される理由 これはこれでちょっとびっくり。後置で書く人も少なくないと思いますので。
退会済みユーザー

退会済みユーザー

2015/11/11 15:14

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

2015/11/12 05: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"; }
guest

0

ベストアンサー

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

投稿2015/11/11 08:00

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takey

2015/11/11 08:14

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

0

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

c

1char c_table[5]; 2int i; 3int cnt; 4 5c_table[0] = "a"; 6c_table[1] = "b"; 7c_table[2] = "c"; 8c_table[3] = "d"; 9c_table[4] = "e"; 10 11 12for(cnt=0 ; cnt < 3 ; cnt++) 13{ 14 i = cnt; 15 printf("case i++ : %c\n", c_table[i++]); 16 17 i = cnt; 18 printf("case ++i : %c\n", c_table[++i]); 19 20} 21 22 23

結果

c

1case i++ : a 2case ++i : b 3 4case i++ : b 5case ++i : c 6 7case i++ : c 8case ++i : d 9 10 11 12

投稿2015/11/11 08:18

KenjiObata

総合スコア440

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問