【C言語】
無限ループの怖いバグ
(Infinite loop)

無限ループの怖いバグ

■無限ループとは

バグには色々あれど、
ゼロの割り算とこの無限ループは
バグがひとたびでると一斉総点検を命じられ
プロジェクト全員の仕事が一週間位止まります。
※あくまで筆者の経験談

■ループカウンタを更新しないと無限ループ

#include <stdio.h>
int main(void)  {
    for(int i = 0; i<10; i++){
        for(int j = 0; j<10; i++){
            printf("意図しない無限ループ[i=%d][j=%d]\r",i,j);
        }
    }
}

ループカウンタjの更新がありません。
文脈から以下の間違いでしょう。
間違い:for(int j = 0; j<10; i++){
正しい:for(int j = 0; j<10; j++){

※この問題はgccでは検出できず、clangで検出できました。


■ループカウンタにintより狭い型を使って無限ループ

#include    <stdio.h>
typedef unsigned char   U1;
int  main(void){
    //NG:U1型は256を表現できない
    for(U1 i = 0 ; i < 256 ; i++){
        printf("意図しない無限ループ %d\n",i);
    }  
}

U1型では255までしか表現できないので256になる事はなく
無限ループします。
この問題はメモリ節約のためと称して狭い型(char/short)を多用するプロジェクトでよく発生します。
ループカウンタには整数を扱うのに適した int 型を使用する事を推奨します。


■ループカウンタと最大値の型が違うと無限ループ

#include <stdio.h>
typedef unsigned char   U1;
static void f(int max) {
    for(U1 i = 0 ; i < max ; i++){
        printf("意図しない無限ループ %d\n",i);
    }     
}
int main(void){
    f(1024);
}

for文第2式の i < max において、
i は U1 型で255までしか表現できませんが
max は int 型で約21億まで表現できます。
※32bit CPUの場合

このコードの場合、
iを加算してもmaxに到達する事はありません。

このコードの場合無限ループしますが、
残念ながら現状 
gcc/clangで警告は何もでません。


■降順ループで0Uと比較して無限ループ

#include <stdio.h>
#define     MAX     10U
#define     MIN      0U
int main(void){
    for(int i = MAX ;i >= MIN; i--){
        printf("無限ループ %d\r",i);
    }
}

int i は符号付き変数ですが、

i >= MIN の比較において
マクロ MIN は0Uであり、
接尾子Uが付いています。

このため
i>=0Uの両辺は
符号無しとして扱われるため
条件は常に成立します。

よって
このコードは無限ループします。


■降順ループで符号無し変数を使って無限ループ

#include <stdio.h>
#include <string.h>

int main(void){
    int     ary[] = {1,2,3,4,5,6,7};
    size_t  num = sizeof(ary)/sizeof(ary[0]);

    for(size_t i = num - 1;i>=0;i--){
        size_t j = i%num;
        printf("意図しない無限ループ ary[%8zx]=%d\r",j,ary[j]);
    }
}

符号無し変数(size_t i)は
負値にならないので
常に0以上です。

for文の第2条件(i>=0)が常に成り立つので
このコードは無限ループします。

符号無し変数以外のループカウンタを
使用を禁止にしているプロジェクトで
この問題はよく発生するので、
ルールの見直しを推奨します。


■!=で最大値を跨いで無限ループ

#include <stdio.h>
int main(void){
    for(int i = 1;i < 10 ;i+=2){
        printf("〇最大値を跨がない %d\n",i);
    }
    for(int i = 1;i != 10 ;i+=2){
        printf("✖最大値を跨いで無限ループ %d\n",i);
    }
}


ループの終了条件に
ノットイコール(!=)を使うと、
最大値を跨いで
無限ループする危険性があります。

< 、<=などの関係演算子が安全です。


参考:

MSC21-C. ループの終了条件には不等式を用いる

■アンケート

4
降順127回転する時のforループカウンタの型を教えてください