C言語初心者が避けるべき5つの無限ループパターンとその対策法

プログラマがC言語で無限ループを避けるための5つの方法C言語
この記事は約12分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

プログラミングの世界において、ループは一連のコードを繰り返し実行するための重要な機能です。

しかし、これらのループを正しく制御しないと、無限ループが発生し、プログラムが終了しない状況を引き起こします。

特にC言語の初心者にとって、無限ループは厄介な問題の一つとなることが多いです。

この記事では、C言語の初心者が避けるべき5つの典型的な無限ループパターンと、それぞれのパターンを解決するための具体的な対策方法を説明します。

それぞれの説明の際にはサンプルコードを使用して、より具体的な理解を助けます。

●無限ループとは

無限ループとは、その名前が示す通り、終了条件を満たさずに無限に続けられるループのことを指します。

これはプログラムが意図しない永続的な動作を行い、最終的にはシステムのリソースを消費し切り、プログラムやシステムが応答しなくなる原因となります。

●C言語における無限ループの5つの典型的なパターン

無限ループはいくつかの典型的なパターンに分類することができます。

それでは、これらのパターンを一つずつ見ていきます。

○パターン1:条件式が常に真となるループ

このパターンは、ループの条件式が常に真と評価されるために発生します。

そのため、ループは終了せずに続けられます。

int i = 0;
while (i >= 0) {
    printf("%d\n", i);
    i++;
}

このコードでは、iの値が0から始まり、ループの各反復で増加します。

しかし、条件式 i >= 0 はiの値が増加しても常に真と評価されるため、このループは終了することはありません。

○パターン2:ループ内で変数が更新されないループ

このパターンは、ループの条件に使用される変数がループ内で更新されないために発生します。

int i = 0;
while (i < 10) {
    printf("%d\n", i);
}

このコードでは、iの値はループ内で更新されていないため、条件式 i < 10 は常に真と評価され、ループは終了しません。

○パターン3:複雑な条件式によるループ

このパターンは、ループの条件式が複雑で、ループが終了する条件が適切に設定されていないために発生します。

for (int i = 0, j = 10; i >= 0; i++) {
    printf("i: %d, j: %d\n", i, j);
}

このコードでは、iが10未満またはjが0より大きい場合にループを続けます。

しかし、jが0になってもiが10未満であればループは続き、このループは終了しません。

○パターン4:ループの終了条件が適切に設定されていないループ

このパターンは、ループの終了条件が適切に設定されていないために発生します。

for (int i = 0; i != 11; i += 2) {
    printf("%d\n", i);
}

このコードでは、iが11と等しくない限りループを続けます。

しかし、iは2ずつ増加するので、10とは等しくならず、このループは終了しません。

○パターン5:再帰関数によるループ

このパターンは、再帰関数(自分自身を呼び出す関数)によって引き起こされます。

void recursive_function() {
    printf("This is a recursive function.\n");
    recursive_function();
}

このコードでは、関数 recursive_function が自分自身を呼び出しているため、関数の呼び出しは無限に続きます。

これは無限ループの一種と考えることができます。

●5つの無限ループパターンに対する対策法とサンプルコード

無限ループを避けるためには、その生成につながる可能性のあるパターンを理解し、それに対する具体的な対策を講じることが重要です。

ここでは、上で説明した5つの無限ループパターンについて、それぞれの対策法と具体的なサンプルコードを紹介します。

○パターン1の対策法とサンプルコード

条件式が常に真となるループを避けるためには、ループ条件が必ずいつかは偽になることを保証する必要があります。

このためには、ループ変数の更新を確実に行うことが重要です。

このコードでは、正しくループを終了する方法を表しています。

この例では、初期値が0で、終了条件が10未満と設定されているiという名前の変数を用いてループを制御しています。

#include <stdio.h>

int main() {
    for(int i = 0; i < 10; i++) {
        printf("%d\n", i);
    }
    return 0;
}

上記のコードを実行すると、0から9までの数字が順番に出力されます。

ここで、’i++’によってループごとに変数iが1ずつ増加し、iが10になるとループは終了します。

○パターン2の対策法とサンプルコード

ループ内で変数が更新されない場合、その変数を確実に更新する処理を追加することで無限ループを避けられます。

下記のコードは、1から100までの整数を合計するもので、変数iがループ内で適切に更新されています。

#include <stdio.h>

int main() {
    int sum = 0;
    for(int i = 1; i <= 100; i++) {
        sum += i;
    }
    printf("合計: %d\n", sum);
    return 0;
}

このコードを実行すると、「合計: 5050」という結果が出力されます。

変数iが1から100まで順に増加し、それぞれのiをsumに加えることで、1から100までの合計を計算しています。

○パターン3の対策法とサンプルコード

複雑な条件式によるループを避けるためには、条件式をシンプルに保つことが基本です。

特に、複数の条件を組み合わせる場合は注意が必要です。

次のコードでは、1から10までの偶数だけを出力する例を表します。

ここでは、’i % 2 == 0’という条件式を使って偶数を判定しています。

#include <stdio.h>

int main() {
    for(int i = 1; i <= 10; i++) {
        if(i % 2 == 0) {
            printf("%d\n", i);
        }
    }
    return 0;
}

このコードを実行すると、2, 4, 6, 8, 10という5つの偶数が順に出力されます。

複雑な条件式を使う代わりに、’i % 2 == 0’という単純な条件式をループ内のif文で使用しています。

○パターン4の対策法とサンプルコード

ループの終了条件が適切に設定されていない場合、終了条件を明確にすることで無限ループを防げます。

次のコードでは、0から始まり2ずつ増加する変数iに対して、iが11以上になった時点でループから抜け出す例を表しています。

#include <stdio.h>

int main() {
    for(int i = 0; i < 11; i += 2) {
        printf("%d\n", i);
    }
    return 0;
}

このコードを実行すると、0, 2, 4, 6, 8という5つの数値が順番に出力されます。

ここで、’i += 2’により変数iが2ずつ増加し、iが12に達するとループが終了します。

○パターン5の対策法とサンプルコード

再帰関数によるループは、特に無限ループに陥りやすいので注意が必要です。

基本的に、再帰関数には終了条件が必要です。

次のコードは、再帰関数を用いて1からnまでの数値の合計を計算する例を表しています。

この例では、引数nが0以下になった時点で再帰を終了しています。

#include <stdio.h>

int sum(int n) {
    if(n <= 0) {
        return 0;
    } else {
        return n + sum(n - 1);
    }
}

int main() {
    printf("合計: %d\n", sum(10));
    return 0;
}

このコードを実行すると、「合計: 55」という結果が出力されます。

sum関数は引数nを受け取り、nが0以下になるまで自身を再帰的に呼び出し、1からnまでの合計を計算します。

●応用例:より効率的なループの書き方とそのサンプルコード

プログラミング初心者がループを書くときに、効率的なループの書き方がわからないことがあります。

しかし、効率的なループの書き方を学ぶことで、プログラムの実行時間を短縮し、リソースを節約することができます。

まず、ループを効率的にするための一つのテクニックは、ループの回数を最小限にすることです。

不必要に多くの回数ループを回すと、プログラムの実行時間が増え、リソースが無駄になります。

したがって、必要な回数だけループを回すように心掛けましょう。

さらに、ループ内で行う処理を最適化することも重要です。

例えば、ループ内で同じ計算を何度も行っている場合、その計算結果をループの外で一度だけ計算し、その結果をループ内で使用するように変更できます。

このようにすることで、同じ計算を何度も行うことを防ぎ、プログラムの実行時間を短縮することができます。

次に、これらのテクニックを適用したループの書き方のサンプルコードを紹介します。

このコードでは、1から100までの数字の中で偶数だけを合計する処理を行っています。

この例では、ループの回数を最小限にし、偶数だけを処理するために2ずつカウントアップしています。

#include <stdio.h>

int main() {
    int sum = 0;
    for(int i = 2; i <= 100; i += 2) {
        sum += i;
    }
    printf("合計: %d\n", sum);
    return 0;
}

このコードを実行すると、「合計: 2450」という結果が出力されます。

ここで、’i += 2’により変数iが2ずつ増加し、つまり偶数だけを扱い、それをsumに加えています。

そのため、ループは50回しか行われず、効率的に計算を行うことができます。

また、ループ内で何度も行う処理を最適化する例として、配列の要素の合計を計算する処理を見てみましょう。

配列の長さを取得する処理をループ内で何度も行うのではなく、ループの外で一度だけ行い、その結果をループ内で使用するようにします。

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int sum = 0;
    int length = sizeof(arr) / sizeof(arr[0]);  // 配列の長さを計算

    for(int i = 0; i < length; i++) {
        sum += arr[i];
    }
    printf("合計: %d\n", sum);
    return 0;
}

このコードを実行すると、「合計: 55」という結果が出力されます。

配列の長さを取得する処理をループの外で行い、その結果をループの制御条件で使用することで、無駄な処理を省くことができます。

●無限ループを避けるための一般的な注意点

このセクションでは、C言語プログラミングを行う上で無限ループを避けるための一般的な注意点を提供します。

これらのガイドラインは、無限ループの原因となる典型的な5つのパターンを理解し、それらに対する対策を学んだ上で、さらにループを効率的に書くための知識を深めるために役立つはずです。

○ループ変数の初期化と更新を確認する

ループを書くときには、ループ変数の初期化と更新を忘れないように注意してください。

ループ変数が正しく更新されないと、ループが終了しない可能性があります。

下記のコードは、ループ変数が正しく更新されていることを確認するサンプルコードです。

#include <stdio.h>

int main() {
    for (int i = 0; i < 10; i++) {
        printf("%d\n", i);
    }
    return 0;
}

このコードでは、整数型の変数iを使って、0から9までの数値を表示するループを書いています。

この例では、iが0から始まり、ループの終わりごとに1ずつ増加しています。

そして、iが10に達したとき、ループは終了します。

○終了条件を見直す

無限ループの一般的な原因は、ループの終了条件が適切に設定されていないことです。

終了条件が常に真であるか、ループ変数が増加または減少することで終了条件が満たされない場合、無限ループになります。

下記のコードは、終了条件が適切に設定されている例を表しています。

#include <stdio.h>

int main() {
    int i = 0;
    while (i < 10) {
        printf("%d\n", i);
        i++;
    }
    return 0;
}

このコードでは、whileループを使って、0から9までの数値を表示しています。

変数iの初期値は0で、ループの終わりごとに1ずつ増加します。

iが10に達すると、終了条件「i < 10」が偽となり、ループが終了します。

○ループのネストを適切に管理する

複数のループをネストする場合、それぞれのループ変数の初期化と更新、終了条件を適切に管理することが重要です。

これにより、内側のループが外側のループの終了条件に影響を与え、無意識のうちに無限ループを作成するリスクを避けることができます。

以上が無限ループを避けるための一般的な注意点です。

これらを心に留めておくことで、あなたのC言語プログラミングの品質と効率性が向上することでしょう。

まとめ

ここまで、C言語の無限ループについて、5つの典型的なパターンとそれらを解決する具体的な方法を見てきました。

無限ループは、プログラムが正常に終了しない、メモリを過剰に消費する、予期しない結果を出すなどの問題を引き起こす可能性があります。

無限ループを避けるための一般的な注意点としては、常にループの条件と終了条件を明確に保つ、ループ内で変数が適切に更新されることを確認する、そしてループの複雑さを適切に管理する、といったことが挙げられます。

この記事を通じて、無限ループを避け、より効率的なコードを書くための方法を学んでいただければ幸いです。

プログラミングは試行錯誤の連続ですが、失敗から学び、それを改善していくことでスキルは確実に上がっていきます。

ぜひ、これらのテクニックを活用し、より良いコードを書くための一助にしてください。