読者です 読者をやめる 読者になる 読者になる

素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

インデックスを増大/減少させるループとunsigned

for (int i = 0; i < N; i++) {
	/* array[i]を使う */
}

というコードについて、iの型はC言語ではsize_tを使うほうが良いのか?という議論から発展して、size_tを使うと減少(降下)ループがきれいに書けなくなる、という問題があがった。
下の話では、size_tが一般的にunsignedであることを踏まえる必要がある。また、Nが1以上であることを仮定している。

for (int i = 0; i < N; i++) {
}

と対称になる、インデックスを減少させるループ

for (int i = N - 1; i >= 0; i--) {
}

は、unsignedでは成立しない。

for (unsigned i = N - 1; i >= 0; i--) {
}

iが0の時i--するとunderflowし無限ループになってしまうからだ。少し考えれば分かるが、非負の数でi >= 0は常に成立する。
よって、この場合のみ使える大して役に立たない(が、少し格好良い)技を紹介する。

for (unsigned i = N - 1; ~i; i--) {
}

である。
i--でunderflowしてUINT_MAXになった時、全ビットが立った状態になるので、その補数が0になることを利用したテクニックである(signedにキャストして考えることもできる。-1の2の補数表現0..01は1..10+1で全ビットが立つ。どちらにしろ起こっていることは同じだ)。

さて、この方法はi -= 2などの時、-1を飛び越えてしまうので柔軟性に欠ける。そのような場合が存在する時は、

for (unsigned i = N - 1; i + step > i; i -= step) {
}

を使うことができる。これはx-yがunderflowする時、x<yになるということを使っている。

これらの方法のどれが一番良いだろうか?実際のところ、これらは面白いだけでどれも実用には適さない。

unsigned i = N;
while (i--) {
}

や、

for (unsigned int i = 0, k; i < N; i++, k = N - i - 1) {
}

を使うのが正しい。後者はstepが2以上の場合にも使用できる。