日記帳だ! with Tux on Libserver

二度目の大改造!! 日記帳…か?を継承し、より柔軟でパワフルなBlogに変身しました。

RSSに対応しています。リンク・コメント・トラックバックは自由にしていただいてほぼ問題ありません。
RSS購読方法、僕のリンク・コメント・トラックバックについての考えを読むことをおすすめします。

<< 過去

未来 >>

1加えることなんて好きに書いてよろしい

友人から電話がかかってきた。明日プログラミングの試験なもので。
さて、その質問の中で「++iとi++って違うの?」というのがあったのだが、まぁ違うけど気にしないと答えた。
i++; ++i; i+=1; i=i+1;
この4つの式は単独で使う限りはどれも意味は同じ。
普通は単独で使うと思うよ。
ただ計算結果が違う。i++は、加算前のiが計算結果。++iは加算後のiが計算結果。
ただ計算結果を使うことは最近では少ないのでは?そうでもないか。
問題になるというとこういうコードか…
int i;
i=10;
while(i--) printf("%d ",i);
i=10;
while(--i) printf("%d ",i);
i--の場合は、iが1のとき、iは0になって計算結果は1で、真だからiが0になってループが回る。
--iの場合は、iが1のとき、iは0になって、計算結果も0で、偽なのでループ終わり。
こういう違いがありますが、非常に紛らわしいので、forなどでインクリメント・デクリメントと条件チェックは分けた方がいいかと。
まぁしかし、インクリメント演算子、デクリメント演算子言いますけど、
これは確かにシンプルに書けますが、別にi+=1でもいいじゃないかというのはありますよね。
Visual Basicにおいては、VB.NETからはこの演算子が導入されたのですが、インクリメント・デクリメント演算子はありません。
けど僕はこれでいいと思いますよ。
i=i+1はiを2回書くので書き間違えが起きやすそうですが、
+=のような代入演算子さえあれば困らないかなと。整数型以外はこれ使わんからねぇ。
もっともC++においてはインクリメント演算子はかなり重要なものだったと思いますけど。
STLにiteratorというのがあったのだが、こいつは++で次に進むだったみたいだ。
.NET Frameworkではこれに相当するのはIEnumeratorだけど、こちらは前進専門。
そう、iteratorは前進後退可能だったり、ランダムアクセスすら可能なのもあったと。
でポインタみたいに振る舞うものだったらしい。なんというかC++は演算子のオーバーロード好きですね。
まぁその辺の都合もあるみたいです。
しかし、.NET Frameworkのライブラリは演算子のオーバーロードはあまりしない。
もしiteratorがあれば、Next、Prevとかいうメソッドを用意してただろう。まぁないんだけどさ。
operator ++が定義されてるのは、System.Decimalだけだった。しかも構造体だからC++のような問題は無いね。
そのC#でせっかくなのでi++と++iの動作の違いをILで見てみることにした。
結論から言えば、結果を使わないならば、++; ++i; i+=1; i=i+1;はいずれも同じ動作。
結果を使うにしてもちょっと順番が変わるだけでやることは変わらない。
まぁCの時代は結果を使わない場合すら、この4つの計算の動作が違ったらしい。
しかしそれはコンパイラの問題だと思うけどね。もっとも便利な動きに最適化されるはずだし。
まぁこれは検証する気にならないけど、ILは非常に読みやすいので…
まず、結果を使わないでインクリメントすること。全部一緒ですよ。
  ldloc.0
ldc.i4.1
add
stloc.0
まず、iの値をldloc.0で積んで、その上に1を積んで、2つを足したのを代わりに積む、それをstloc.0でiに納める。
非常にわかりやすいですね。どのように書いても、i=i+1のように解されることがわかりますね。
まぁ実にILの仕様は単純なものです。1加えるだけにしてもinc命令とかないんですね。
少なくとも.NET Framework上ではどれも性能は同じであると。
さて、結果を使う場合。p=i++に相当するILです。
  ldloc.0
dup
ldc.i4.1
add
stloc.0
stloc.1
2つ目のdup命令と、最後のstloc.1、これはpに代入する命令だけ増えました。
ldloc.0でiを読み込んで、これをdupで2つにコピー。この上の方と、その上に積んだ1を加算して代わりに積む。
その上の方をstloc.0でiに納める。これがiに1加算する行為です。
そして始めにコピーしたやつを結果として使うと。そういうことです。
一方、p=++iに相当するIL。
  ldloc.0
ldc.i4.1
add
dup
stloc.0
stloc.1
dupの順番が違いますね。
まず、iの値をldloc.0で積んで、その上に1を積んで、2つを足したのを代わりに積む、ここまでは結果を使わないときと同じ。
この結果をdupで2つにコピーして、まずstloc.0でiに納める。コピーしたもう1つを結果として使うと。
やってること自体はあまり変わりませんね。
まぁあえて言うなら、p=i++はこれでスタックが3つ必要ですが、p=++iはスタック2つで済んでいると。
その程度ですね。
++iでもi++でもあまり差はないと。けど結果を使う場合は++iの方が性能がいいかもしれないと。
それはCの頃からそうで、C++においてはこの辺の事情があって前置インクリメントが主流みたい。
いやなんか後置インクリメントだと、コピーコンストラクタを動作させないといけないと。
C#もどうもoperator ++を定義するときは、インスタンスをコピーしろらしい。そりゃ流行らんわな。
まぁ確かにC++になって発生した問題かもしれないけど、昔から++iの方がいいと考える人も多かったはず。
けどC++は++Cじゃないんですよね。なぜだろう。
K&Rという有名な本があるんだが、そこではi++のような表記が好んで使われていたらしい。
なぜだろうかと思ったけど多分こう言うことだと思う。
char s[11];
int i=0x30;
char *p;
p=s;
while(i<0x3A){
*p++ = i++;
}
*p='\0';
僕はこんなのは嫌いなんだけどねぇ…まぁしかし実にCらしい。
p++を使うことで、今のpについて処理して、pを進めると言うことが直感的に書ける。
i++も同様。今のiを代入して、iを進めるということができる。
こういうシンプルだけど不気味な記述が多い本らしいです。K&Rというやつは。
以前取り上げた気がするけど、strcpyの実装例として、こんなのが推奨されてると。
void strcpy(char* sf,char* st){
while(*st++ = *sf++);
}
さっきのとほとんど一緒。だけどループの条件わかりにくい。
*sfが0のときには、*st++ = *sf++の結果は0、すなわち偽なので、ここで終了と。
まぁひどいですね。こういう操作が流行したんでしょう。だからCの拡張をC++と呼ぼうとかなったんでしょう。
僕はJavaScriptの頃からC++という言語もあるのだし、1加えることは専らi++と書こうと考えてきたけど、
実はこういうことがありそうです。しかし気にせず好きな表現を使うのが一番いいでしょう。
Author : Hidemaro
Date : 2008/09/28(Sun) 21:05
コンピュータ・インターネット | Comment | trackback (0)
blog comments powered by Disqus

トラックバック

トラックバックURL取得

Tools