コンテンツにスキップ

C言語/for

出典: フリー教科書『ウィキブックス(Wikibooks)』

for文の基本構造と動作

[編集]

for文はC言語における繰り返し制御構造の一つで、初期化、条件判定、更新の3つの部分から構成されています。一定回数の繰り返しや、特定の条件が満たされる間の繰り返しを簡潔に記述できます。

for (初期化式; 条件式; 更新式) {
    // 繰り返し実行する文
}

for文の実行順序は以下の通りです:

  1. 初期化式が一度だけ評価されます
  2. 条件式が評価され、真なら本体の文が実行されます
  3. 本体の文の実行後、更新式が評価されます
  4. 2〜3を条件式が偽になるまで繰り返します

for文の実行フローを理解するには、以下のように考えるとよいでしょう。for文は内部的には次のwhile文と同等の動作をします:

// for文
for (初期化式; 条件式; 更新式) {
    ;
}

// 同等のwhile文
初期化式;
while (条件式) {
    ;
    更新式;
}

基本的なfor文の例

[編集]

以下は単純なfor文の例です。変数iを0から9まで変化させながら、その値を出力します。

#include <stdio.h>
int main() {
    for (int i = 0; i < 10; i++) {
        printf("%d ", i);
    }
    // 出力: 0 1 2 3 4 5 6 7 8 9 
    
    return 0;
}

for文の各部分の詳細

[編集]

for文の各部分は以下のように機能します:

for (初期化式; 条件式; 更新式) {
    // 繰り返し実行する文
}

初期化式

[編集]

初期化式はループの開始前に一度だけ実行されます。変数の宣言と初期化を行うことが一般的です。C99以降では、for文の中で変数を宣言することができます。

// C99以降で有効
for (int i = 0; i < 10; i++) {
    printf("%d ", i);
}

// C90以前のスタイル
int i;
for (i = 0; i < 10; i++) {
    printf("%d ", i);
}

条件式

[編集]

条件式は各繰り返しの前に評価され、真(ゼロ以外)の場合にループ本体が実行されます。偽(ゼロ)になるとループは終了します。

// 条件式の例
for (int i = 0; i < 10; i++) ; // i < 10 が条件式
for (int i = 0; i != 100; i++) : // i != 100 が条件式
for (int i = 0; array[i] != '\0'; i++) ; // array[i] != '\0' が条件式

更新式

[編集]

更新式はループ本体の実行後に評価されます。通常はカウンタ変数の増加や減少に使用されます。

// 更新式の例
for (int i = 0; i < 10; i++) ; // i++ が更新式
for (int i = 10; i > 0; i--) ; // i-- が更新式
for (int i = 0; i < 100; i += 2) ; // i += 2 が更新式

for文の各部分の省略

[編集]

for文の初期化式、条件式、更新式はすべて省略可能です。省略した場合、その部分は無条件で真と見なされます。

#include <stdio.h>
int main() {
    // 初期化式の省略
    int i = 0;
    for (; i < 5; i++) {
        printf("%d ", i);
    }
    printf("\n");  // 出力: 0 1 2 3 4

    // 条件式の省略(無限ループになるため、内部で break を使用)
    for (int j = 0; ; j++) {
        printf("%d ", j);
        if (j >= 4) break;
    }
    printf("\n");  // 出力: 0 1 2 3 4

    // 更新式の省略(更新を本体内で行う)
    for (int k = 0; k < 5; ) {
        printf("%d ", k);
        k++;
    }
    printf("\n");  // 出力: 0 1 2 3 4

    // すべて省略(無限ループ)
    int count = 0;
    for (;;) {
        printf("%d ", count);
        count++;
        if (count >= 5) break;
    }
    printf("\n");  // 出力: 0 1 2 3 4

    return 0;
}

複数の変数を使用したfor文

[編集]

for文の初期化式と更新式ではカンマ演算子を使って複数の式を含めることができます。

#include <stdio.h>
int main() {
    // 複数の変数を初期化と更新
    for (int i = 0, j = 10; i < j; i++, j--) {
        printf("i = %d, j = %d\n", i, j);
    }
    
    return 0;
}

/* 出力:
i = 0, j = 10
i = 1, j = 9
i = 2, j = 8
i = 3, j = 7
i = 4, j = 6
*/

ネストしたfor文

[編集]

for文は他のfor文の中にネストして使用できます。これは多次元配列の処理などで頻繁に使用されます。

#include <stdio.h>
int main() {
    // 5x5の模様を表示
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            if ((i + j) % 2 == 0) {
                printf("■ ");
            } else {
                printf("□ ");
            }
        }
        printf("\n");
    }
    
    return 0;
}

/* 出力:
■ □ ■ □ ■ 
□ ■ □ ■ □ 
■ □ ■ □ ■ 
□ ■ □ ■ □ 
■ □ ■ □ ■ 
*/

配列の走査

[編集]

for文は配列の要素を順に処理するのに特に適しています。

#include <stdio.h>
int main() {
    int numbers[] = {10, 20, 30, 40, 50};
    int sum = 0;
    
    // 配列の合計を計算
    for (int i = 0; i < 5; i++) {
        sum += numbers[i];
    }
    
    printf("合計: %d\n", sum);  // 出力: 合計: 150
    
    // 文字列(文字の配列)の処理
    char message[] = "Hello, C!";
    
    // 文字列の長さを計算
    int length = 0;
    for (int i = 0; message[i] != '\0'; i++) {
        length++;
    }
    
    printf("文字列の長さ: %d\n", length);  // 出力: 文字列の長さ: 9
    
    return 0;
}

for文と制御フロー文

[編集]

for文の中ではbreakcontinueを使って制御フローを変更できます。

break文

[編集]

break文はループを即座に終了し、ループの後の文に制御を移します。

#include <stdio.h>
int main() {
    // 5が見つかったらループを終了
    for (int i = 0; i < 10; i++) {
        printf("%d ", i);
        if (i == 5) {
            printf("(5が見つかりました!) ");
            break;
        }
    }
    // 出力: 0 1 2 3 4 5 (5が見つかりました!) 
    
    return 0;
}

continue文

[編集]

continue文は現在の繰り返しの残りの部分をスキップし、次の繰り返しに進みます。

#include <stdio.h>
int main() {
    // 偶数のみを表示
    for (int i = 0; i < 10; i++) {
        if (i % 2 != 0) {
            continue;  // 奇数の場合はスキップ
        }
        printf("%d ", i);
    }
    // 出力: 0 2 4 6 8 
    
    return 0;
}

for文のスコープ

[編集]

C99以降、for文の初期化式で宣言された変数のスコープはfor文のブロック内に限定されます。

#include <stdio.h>
int main() {
    // C99スタイル
    for (int i = 0; i < 3; i++) {
        printf("ループ内: i = %d\n", i);
    }
    // ここでは i は使用できない
    // printf("ループ外: i = %d\n", i);  // コンパイルエラー
    
    // C90スタイル
    int j;
    for (j = 0; j < 3; j++) {
        printf("ループ内: j = %d\n", j);
    }
    printf("ループ外: j = %d\n", j);  // j = 3
    
    return 0;
}

初期化式での複数変数の宣言

[編集]

C99以降、for文の初期化式では同じ型の複数の変数を宣言できます。

#include <stdio.h>
int main() {
    for (int i = 0, j = 10; i < 5; i++, j--) {
        printf("i = %d, j = %d\n", i, j);
    }
    
    // 異なる型の場合は初期化式の外で宣言する必要がある
    int count;
    char ch;
    for (count = 0, ch = 'A'; count < 5; count++, ch++) {
        printf("%c ", ch);
    }
    // 出力: A B C D E 
    
    return 0;
}

多次元配列の処理

[編集]

ネストしたfor文は多次元配列の処理に特に適しています。

#include <stdio.h>
int main() {
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    
    // 3x4の行列を表示
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%3d ", matrix[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

/* 出力:
  1   2   3   4 
  5   6   7   8 
  9  10  11  12 
*/

C99のfor文の初期化変数型の制限緩和

[編集]

C99では、for文の初期化式で宣言する変数の型に対する制限が緩和されました。

#include <stdio.h>
#include <time.h>
int main() {
    // 構造体変数の初期化
    for (struct tm time = { 0 }; time.tm_hour < 24; time.tm_hour++) {
        printf("%02d:00\n", time.tm_hour);
    }
    
    return 0;
}

for文のバリエーションとパターン

[編集]

for文は様々な用途に応じたパターンがあります。以下に代表的なパターンをいくつか示します。

カウントダウン

[編集]
#include <stdio.h>
int main() {
    // 10から1までカウントダウン
    for (int i = 10; i > 0; i--) {
        printf("%d ", i);
    }
    printf("発射!\n");
    // 出力: 10 9 8 7 6 5 4 3 2 1 発射!
    
    return 0;
}

ステップ値を変える

[編集]
#include <stdio.h>
int main() {
    // 2ずつ増加(偶数表示)
    printf("偶数: ");
    for (int i = 0; i <= 10; i += 2) {
        printf("%d ", i);
    }
    printf("\n");  // 出力: 偶数: 0 2 4 6 8 10 
    
    // 3ずつ増加
    printf("3の倍数: ");
    for (int i = 0; i <= 15; i += 3) {
        printf("%d ", i);
    }
    printf("\n");  // 出力: 3の倍数: 0 3 6 9 12 15
    
    return 0;
}

ビット操作

[編集]

for文はビット操作と組み合わせて使用することも多いです。

#include <stdio.h>
int main() {
    unsigned int bits = 0xA5;  // 10100101 in binary
    
    printf("ビット位置とその値:\n");
    for (int i = 0; i < 8; i++) {
        int bit = (bits >> i) & 1;
        printf("ビット %d: %d\n", i, bit);
    }
    
    return 0;
}

素数判定

[編集]

アルゴリズムの具体例として素数判定を示します。

#include <stdio.h>
#include <stdbool.h>
#include <math.h>
bool isPrime(int n) {
    if (n <= 1) return false;
    if (n <= 3) return true;
    if (n % 2 == 0 || n % 3 == 0) return false;
    
    // 6k±1の形式の数のみチェック
    for (int i = 5; i * i <= n; i += 6) {
        if (n % i == 0 || n % (i + 2) == 0) {
            return false;
        }
    }
    
    return true;
}

int main() {
    printf("1から20までの素数:\n");
    for (int i = 1; i <= 20; i++) {
        if (isPrime(i)) {
            printf("%d ", i);
        }
    }
    printf("\n");  // 出力: 2 3 5 7 11 13 17 19
    
    return 0;
}

標準規格における変遷

[編集]

for文の仕様はC言語の標準規格の変遷とともに少しずつ変化してきました。

標準バージョン for文における主な変更点
K&R C 1978 初期のfor文定義
ANSI C (C89/C90) 1989/1990 for文の基本仕様を確立
C99 1999 for文の初期化式での変数宣言をサポート
C11 2011 ほぼ変更なし
C17 2017 ほぼ変更なし
C23 2024 属性指定のサポート強化、Unicode識別子サポート

C++とC言語のfor文の違い

[編集]

参考までに、C++とC言語のfor文の違いについても触れておきます。

// C言語の標準的なfor文
for (int i = 0; i < n; i++) {
    // 処理
}

// C++11以降の範囲ベースfor文(C言語には存在しない)
// for (auto& element : container) {
//     // 各要素に対する処理
// }

for文の実用的な例

[編集]

ファイルの読み込み

[編集]
#include <stdio.h>
//#include <string.h>

#define BUFFER_SIZE 1024

int main() {
    FILE *file = fopen("/etc/passwd", "r");
    if (file == NULL) {
        perror("ファイル '/etc/passwd' を開けませんでした"); // perrorを使用
        return 1;
    }

    char buffer[BUFFER_SIZE];
    for (int line_num = 1;
         fgets(buffer, BUFFER_SIZE, file) != NULL);
         line_num++) {
        buffer[strcspn(buffer, "\n")] = '\0';
        printf("% 3d: %s\n", line_num, buffer);
    }

    fclose(file);
    return 0;
}

文字列操作

[編集]
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main() {
    char str[] = "Hello, World!";
    
    // 文字列の各文字を大文字に変換
    for (int i = 0; str[i] != '\0'; i++) {
        str[i] = toupper(str[i]);
    }
    
    printf("大文字変換後: %s\n", str);  // 出力: HELLO, WORLD!
    
    // 文字列を逆順にする
    int length = strlen(str);
    for (int i = 0; i < length / 2; i++) {
        char temp = str[i];
        str[i] = str[length - 1 - i];
        str[length - 1 - i] = temp;
    }
    
    printf("逆順変換後: %s\n", str);  // 出力: !DLROW ,OLLEH
    
    return 0;
}

まとめ

[編集]

for文はC言語において最も汎用的な繰り返し構造の一つです。初期化、条件判定、更新の3つの部分を組み合わせることで、様々な繰り返し処理を簡潔かつ効率的に実装できます。C99以降の変数宣言の機能拡張やC23における属性指定のサポートなど、言語の進化とともにfor文も進化してきました。

配列の走査、文字列処理、ビット操作、ファイル処理など、for文は様々な用途で活用されています。特に初期化式、条件式、更新式を適切に設計することで、簡潔で読みやすく効率的なコードを記述することができます。

パフォーマンスが重要な場合、ループのオーバーヘッドを減らすための最適化テクニックを適用することも重要です。常にコンパイラの最適化動作を理解し、適切なfor文の設計を心がけましょう。