C言語-7

C言語の基礎学習が完了しましたね!お疲れ様でした。これでC言語の根幹となる概念(ポインタ、メモリ管理、構造体など)はすべて習得しました。

ここからは、C言語で本格的なシステムやデータ処理を行うために必要な、プログラミングの応用デバッグの基礎について解説します。


13. 再帰関数(Recursion)

再帰関数とは、関数自身の内部から、その関数自身を呼び出す手法です。繰り返し処理を簡潔に表現でき、特にデータ構造数学的処理(階乗、フィボナッチ数列など)と相性が良いです。

🚨 再帰関数の基本構造

再帰関数を正しく設計するには、以下の2点が不可欠です。

  1. 基本ケース (Base Case): 再帰が終了する条件。これがなければ無限ループに陥ります。
  2. 再帰ステップ (Recursive Step): 問題をより小さな部分問題に分解し、自分自身を呼び出す部分。

📝 階乗(Factorial)の計算例

n!=n×(n−1)×⋯×1

C

#include <stdio.h>

// 階乗を計算する再帰関数
long factorial(int n) {
    // 1. 基本ケース: nが0または1の場合、1を返す
    if (n <= 1) {
        return 1;
    }
    // 2. 再帰ステップ: n * (n-1)! を呼び出す
    return n * factorial(n - 1); 
}

int main() {
    int num = 5;
    printf("%d の階乗: %ld\n", num, factorial(num)); // 出力: 120 (5*4*3*2*1)
    return 0;
}

14. 連結リスト(Linked List)の基礎

C言語では、データ構造をポインタ構造体を使って自分で実装します。その最も基本的なものが連結リストです。

連結リストは、データと「次の要素へのポインタ」を持つ構造体を鎖のように繋いでいく、動的なデータ構造です。

⛓️ ノード構造の定義

リストの各要素は「ノード」と呼ばれ、データとポインタのペアを持ちます。

C

#include <stdlib.h>

// ノードの構造体定義
struct Node {
    int data;            // 保持するデータ
    struct Node *next;   // 次のNodeを指すポインタ
};

💡 連結リストの利点

  • 動的なサイズ変更: 配列のように宣言時にサイズを固定する必要がなく、実行中にメモリを確保・解放してサイズを自由に変更できます。
  • 効率的な挿入・削除: リストの途中に要素を挿入・削除する際、ポインタの接続先を変更するだけで済むため、配列と比べて効率的です。

15. デバッグの基本

C言語のデバッグは、メモリ管理が手動であるため、特に重要です。

🐛 ゼロからのデバッグ

手法内容目的
printf デバッグ重要な処理の前後や変数の中身を printf で出力し、どこで値がおかしくなったかを確認する。最も手軽な手法。ポインタの指す値や、関数が正しく呼ばれているかを確認。
アサート (assert.h)プログラムの前提条件が満たされているかをチェックし、満たされない場合は強制終了させる。バグを早期に発見し、原因究明を助ける。特に引数のチェックに有効。
デバッガの利用GDB(GNU Debugger)などのツールを使い、プログラムをステップ実行し、変数の値やメモリの状態を詳細に観察する。最も強力な手法。複雑なメモリ破壊バグやポインタの異常を追跡するのに不可欠。

Google スプレッドシートにエクスポート

🛑 assert の利用例

C

#include <stdio.h>
#include <assert.h> // assert関数を使うために必要

// この関数は正の数 (> 0) を引数として受け取ることを前提とする
int safe_divide(int a, int b) {
    // bがゼロではないことをチェック(前提条件)
    assert(b != 0); 
    
    // bが0の場合、assertが失敗してプログラムが強制終了し、エラーメッセージを出す
    return a / b;
}

int main() {
    printf("結果: %d\n", safe_divide(10, 2));
    // safe_divide(10, 0); // ここで実行するとassertが失敗し、強制終了する
    return 0;
}

C言語は、これらの応用概念を学ぶことで、OS、コンパイラ、組み込み機器など、コンピュータの核心的な部分を制御できるようになる、非常に強力な言語です。