第3章:最初のCプログラム

この章で学ぶこと

  • Hello Worldプログラムの構造
  • コンパイルの方法
  • プログラムの実行
  • エラーメッセージの読み方
  • デバッグの基本
  • ---

    3.1 Hello World

    プログラマーの伝統

    新しいプログラミング言語を学ぶとき、最初に書くプログラムは「Hello, World!」と表示するものです。これは1978年に出版された「The C Programming Language」(通称K&R本)で紹介されて以来、世界中のプログラマーの伝統となっています。

    コードを書く

    前の章で作成した hello.c ファイルを使用します。まだ作成していない場合は、以下の手順で作成してください。

    $ cd ~/42/c_beginner
    $ vim hello.c
    

    以下のコードを入力します:

    #include <stdio.h>
    
    int main(void)
    {
        printf("Hello, World!\n");
        return 0;
    }
    

    コードの解説

    1行ずつ見ていきましょう。

    #include

    #include <stdio.h>
    

    これはプリプロセッサディレクティブと呼ばれます。

  • #include: 別のファイルを取り込む指示
  • : Standard Input/Output(標準入出力)のヘッダーファイル
  • printf 関数を使うために必要
  • 簡単に言うと、「printf を使いたいので、その説明書を読み込んでください」という意味です。

    int main(void)

    int main(void)
    

    これはmain関数の定義です。

  • int: この関数が整数を返すことを示す
  • main: 関数の名前(特別な名前)
  • void: 引数を取らないことを示す
  • main関数はプログラムの入口です。Cプログラムは必ずmain関数から実行が始まります。

    波括弧 {}

    {
        ...
    }
    

    波括弧は、関数の本体(実行する処理)を囲みます。

    printf("Hello, World!\n");

    printf("Hello, World!\n");
    

  • printf: 画面に文字を表示する関数
  • "Hello, World!\n": 表示する文字列
  • \n: 改行(New Line)を表す特殊文字
  • ;: 文の終わりを示す
  • return 0;

    return 0;
    

  • return: 関数から値を返す
  • 0: 正常終了を意味する値

main関数が 0 を返すと、「プログラムは正常に終了しました」という意味になります。

---

3.2 コンパイル

コンパイラとは

C言語のソースコード(.c ファイル)は、そのままでは実行できません。コンパイラを使って機械語に変換する必要があります。

[hello.c] → [コンパイラ] → [a.out(実行ファイル)]

gccコンパイラ

最も広く使われているCコンパイラはgcc(GNU Compiler Collection)です。

インストール確認

$ gcc --version
Apple clang version 14.0.0 (clang-1400.0.29.202)
...

バージョン情報が表示されれば、gccが使える状態です。

Macでは gcc コマンドは実際には clang というコンパイラを呼び出しますが、使い方はほぼ同じです。

gccがインストールされていない場合

Mac: Xcodeコマンドラインツールをインストール

$ xcode-select --install

Ubuntu/WSL: build-essentialパッケージをインストール

$ sudo apt update
$ sudo apt install build-essential

コンパイルの実行

$ gcc hello.c

エラーが表示されなければ、コンパイル成功です。

$ ls
a.out  hello.c

a.out という実行ファイルが作成されました。

出力ファイル名の指定

-o オプションで、出力ファイルの名前を指定できます。

$ gcc hello.c -o hello
$ ls
hello  hello.c

hello という実行ファイルが作成されました。

---

3.3 プログラムの実行

実行方法

作成した実行ファイルを実行するには、パスを指定して呼び出します。

$ ./hello
Hello, World!

./ は「現在のディレクトリの」という意味です。これを付けないと、システムのコマンドディレクトリ(/usr/bin など)から探してしまいます。

実行の流れ

  • ./hello と入力してEnter
  • OSが hello 実行ファイルをメモリに読み込む
  • main関数から実行開始
  • printf が "Hello, World!\n" を画面に表示
  • return 0 でプログラム終了
  • プロンプトに戻る

たったこれだけですが、コンピュータ内部では膨大な処理が行われています。

終了ステータスの確認

プログラムの終了ステータス(return の値)は、特殊変数 $? で確認できます。

$ ./hello
Hello, World!
$ echo $?
0

0 は正常終了を意味します。エラーが発生した場合は、0 以外の値が返されます。

---

3.4 コンパイルエラー

エラーは怖くない

コードを書いていると、必ずエラーが発生します。エラーメッセージは友達です。何が問題かを教えてくれています。

典型的なエラーとその対処

セミコロンの欠落

#include <stdio.h>

int main(void)
{
    printf("Hello, World!\n")   // セミコロンがない
    return 0;
}

コンパイル:

$ gcc hello.c
hello.c:6:5: error: expected ';' after expression
    return 0;
    ^
    ;
1 error generated.

読み方:

  • hello.c:6:5: hello.cの6行目、5文字目
  • error: エラー
  • expected ';' after expression: 式の後にセミコロンが必要
  • エラーメッセージは「6行目」を指していますが、実際の問題は5行目です。コンパイラは「6行目まで来て、前の行にセミコロンがないことに気づいた」のです。

    対処: 5行目の末尾に ; を追加

    括弧の不一致

    #include <stdio.h>
    
    int main(void)
    {
        printf("Hello, World!\n";   // ) がない
        return 0;
    }
    

    $ gcc hello.c
    hello.c:5:30: error: expected ')'
        printf("Hello, World!\n";
                                 ^
    hello.c:5:11: note: to match this '('
        printf("Hello, World!\n";
              ^
    1 error generated.
    

    対処: "Hello, World!\n" の後に ) を追加

    引用符の閉じ忘れ

    #include <stdio.h>
    
    int main(void)
    {
        printf("Hello, World!\n);   // " が閉じていない
        return 0;
    }
    

    $ gcc hello.c
    hello.c:5:12: error: missing terminating '"' character
        printf("Hello, World!\n);
               ^
    hello.c:6:5: error: expected expression
        return 0;
        ^
    2 errors generated.
    

    対処: 文字列の末尾に " を追加

    未定義の関数

    #include <stdio.h>
    
    int main(void)
    {
        prntf("Hello, World!\n");   // printf のスペルミス
        return 0;
    }
    

    $ gcc hello.c
    hello.c:5:5: error: implicit declaration of function 'prntf' is invalid in C99
        prntf("Hello, World!\n");
        ^
    1 error generated.
    

    対処: prntfprintf に修正

    ヘッダーファイルの欠落

    // #include <stdio.h> を書き忘れた
    
    int main(void)
    {
        printf("Hello, World!\n");
        return 0;
    }
    

    $ gcc hello.c
    hello.c:4:5: error: implicit declaration of function 'printf' is invalid in C99
        printf("Hello, World!\n");
        ^
    1 error generated.
    

    対処: ファイルの先頭に #include を追加

    エラーを読むコツ

  • 最初のエラーから対処する - 1つのエラーが連鎖的に他のエラーを引き起こすことがある
  • 行番号を確認する - ただし、実際の問題は前の行にあることも多い
  • エラーメッセージをGoogle検索する - 同じ問題で困った人がいるはず
  • 最小限のコードで再現する - 問題を切り分ける
  • ---

    3.5 警告(Warning)

    エラーと警告の違い

  • エラー(Error): プログラムがコンパイルできない致命的な問題
  • 警告(Warning): コンパイルはできるが、問題がある可能性がある
  • 警告を表示するオプション

    gcc にはデフォルトで表示されない警告があります。-Wall オプションで多くの警告を表示できます。

    $ gcc -Wall hello.c -o hello
    

    -Wall は "Warning all" の意味...と思いきや、実際には「すべて」ではありません。さらに厳しいチェックをするには:

    $ gcc -Wall -Wextra -Werror hello.c -o hello
    

  • -Wall: 一般的な警告を有効にする
  • -Wextra: 追加の警告を有効にする
  • -Werror: 警告をエラーとして扱う
  • 警告の例

    #include <stdio.h>
    
    int main(void)
    {
        int x;                  // 初期化していない
        printf("%d\n", x);      // 未初期化の変数を使用
        return 0;
    }
    

    $ gcc -Wall hello.c
    hello.c:6:20: warning: variable 'x' is uninitialized when used here
        printf("%d\n", x);
                       ^
    hello.c:5:10: note: initialize the variable 'x' to silence this warning
        int x;
             ^
              = 0
    

    対処: int x = 0; と初期化する

    警告を無視しない

    警告は「コンパイルは通るけど、問題があるかもしれないよ」というメッセージです。無視せずに対処しましょう。

    42のプロジェクトでは、警告が残っていると減点対象になることがあります。

    ---

    3.6 コンパイルプロセスの詳細

    4つの段階

    C言語のコンパイルは、実は4つの段階を経ています。

    [ソースコード] → [プリプロセス] → [コンパイル] → [アセンブル] → [リンク] → [実行ファイル]
    

    1. プリプロセス

    #include#define などのプリプロセッサディレクティブを処理します。

    $ gcc -E hello.c -o hello.i
    

    hello.i には、stdio.h の内容が展開されたコードが含まれています。

    2. コンパイル

    Cコードをアセンブリ言語に変換します。

    $ gcc -S hello.c -o hello.s
    

    hello.s にはアセンブリコードが含まれています:

        .section    __TEXT,__text,regular,pure_instructions
        .globl  _main
    _main:
        pushq   %rbp
        movq    %rsp, %rbp
        ...
    

    3. アセンブル

    アセンブリコードを機械語(オブジェクトファイル)に変換します。

    $ gcc -c hello.c -o hello.o
    

    hello.o はバイナリファイルで、人間が読むことはできません。

    4. リンク

    オブジェクトファイルとライブラリを結合して、実行ファイルを作成します。

    $ gcc hello.o -o hello
    

    通常は gcc hello.c -o hello とするだけで、4つの段階すべてが自動で行われます。

    ---

    3.7 Makefileの基礎

    なぜMakefileが必要か

    複数のファイルからなるプロジェクトでは、コンパイルコマンドが長く複雑になります。

    $ gcc -Wall -Wextra -Werror file1.c file2.c file3.c -o myprogram
    

    毎回これを入力するのは大変です。Makefile を使うと、簡単なコマンドでコンパイルできるようになります。

    最小限のMakefile

    プロジェクトディレクトリに Makefile という名前のファイルを作成します。

    $ vim Makefile
    

    hello: hello.c
    	gcc -Wall -Wextra -Werror hello.c -o hello
    

    重要: インデントはタブ文字でなければなりません。スペースではエラーになります。

    vimでタブを入力するには、Ctrl + V の後に Tab キーを押します。

    Makefileの使い方

    $ make
    gcc -Wall -Wextra -Werror hello.c -o hello
    

    make コマンドを実行するだけで、Makefileに書かれた処理が実行されます。

    Makefileの基本構造

    ターゲット: 依存ファイル
    	コマンド
    

  • ターゲット: 作成したいファイル名(または処理名)
  • 依存ファイル: ターゲットを作成するのに必要なファイル
  • コマンド: 実行するコマンド(タブでインデント)
  • もう少し実用的なMakefile

    CC = gcc
    CFLAGS = -Wall -Wextra -Werror
    
    hello: hello.c
    	$(CC) $(CFLAGS) hello.c -o hello
    
    clean:
    	rm -f hello
    
    re: clean hello
    

  • CC: 使用するコンパイラ
  • CFLAGS: コンパイルオプション
  • clean: 生成ファイルを削除
  • re: クリーンしてから再コンパイル

$ make          # hello をコンパイル
$ make clean    # hello を削除
$ make re       # 削除してから再コンパイル

---

3.8 実践:プログラムを改造してみよう

課題1: 自分の名前を表示する

hello.c を修正して、自分の名前を表示するプログラムを作成してください。

#include <stdio.h>

int main(void)
{
    printf("Hello, [あなたの名前]!\n");
    return 0;
}

課題2: 複数行を表示する

複数の printf を使って、複数行を表示してみましょう。

#include <stdio.h>

int main(void)
{
    printf("Line 1\n");
    printf("Line 2\n");
    printf("Line 3\n");
    return 0;
}

課題3: 特殊文字を使う

\n 以外の特殊文字(エスケープシーケンス)を試してみましょう。

| 表記 | 意味 | |------|------| | \n | 改行 | | \t | タブ | | \\ | バックスラッシュ | | \" | ダブルクォート |

#include <stdio.h>

int main(void)
{
    printf("Name:\tTaro\n");
    printf("Age:\t25\n");
    printf("Quote:\t\"Hello!\"\n");
    return 0;
}

出力:

Name:	Taro
Age:	25
Quote:	"Hello!"

課題4: わざとエラーを起こしてみる

セミコロンを削除したり、スペルミスをしたりして、意図的にエラーを発生させ、エラーメッセージを読む練習をしましょう。

エラーと仲良くなることが、プログラミング上達の秘訣です。

---

3.9 この章のまとめ

学んだこと

  • Hello Worldの構造
- #include : ヘッダーファイルの読み込み - int main(void): プログラムの入口 - printf(): 画面への出力 - return 0: 正常終了

  • コンパイル
- gcc ファイル名.c -o 出力名 - -Wall -Wextra -Werror オプション

  • 実行
- ./プログラム名

  • エラー対処
- エラーメッセージを読む - 行番号を確認 - 最初のエラーから対処

  • Makefile
- コンパイルの自動化 - ターゲットと依存関係

コマンドまとめ

$ gcc hello.c -o hello              # コンパイル
$ gcc -Wall -Wextra -Werror hello.c -o hello  # 警告を有効に
$ ./hello                           # 実行
$ make                              # Makefileを使ったコンパイル
$ make clean                        # クリーンアップ

次の章の予告

次章では、変数と型について学びます。数値や文字を保存し、計算する方法を覚えましょう。

---

Column: 最初のプログラマーたち

世界最初のプログラマー

世界最初のプログラマーは、19世紀のイギリスの数学者エイダ・ラブレス(Ada Lovelace)と言われています。

チャールズ・バベッジが設計した「解析機関」という機械式計算機のためのプログラムを書きました。実際には解析機関は完成しませんでしたが、彼女の残したノートは、現代のプログラミングの概念を先取りしていました。

Hello Worldの起源

「Hello, World!」プログラムは、1978年にブライアン・カーニハンデニス・リッチーが書いた「The C Programming Language」で紹介されました。

当時のバージョンは:

main()
{
    printf("hello, world\n");
}

現代のC言語とは少し異なりますね。#include がなく、main の戻り値の型も指定されていません。C言語も進化してきたのです。

なぜ「Hello, World」なのか

特に深い意味はないようです。カーニハンによると、「何か表示する例が必要だったので、適当に選んだ」とのこと。

しかし、この単純なプログラムが50年近く、世界中のプログラマーの最初の一歩となっています。あなたが今日書いた printf("Hello, World!\n"); は、何百万人ものプログラマーが書いてきたものと同じです。

プログラミングの世界へようこそ!

---

確認問題

問題1

以下のコードで、画面に表示される文字列は何ですか?

printf("Hello\tWorld\n");

解答

Hello	World
\t はタブ、\n は改行)

問題2

gccでコンパイル時にすべての警告を表示するオプションは何ですか?

解答

-Wall(さらに厳しくするなら -Wall -Wextra -Werror

問題3

コンパイルして生成された実行ファイル program を実行するコマンドは何ですか?

解答

./program

問題4

以下のコードにはエラーがあります。何が問題ですか?

#include <stdio.h>

int main(void)
{
    printf("Hello, World!\n")
    return 0;
}

解答

printf の行の末尾にセミコロン(;)がない。 正しくは:printf("Hello, World!\n");

問題5

main 関数の return 0; は何を意味しますか?

解答

プログラムが正常に終了したことを示す。 0 は「成功」を意味し、0 以外の値はエラーや異常終了を意味する。

---

次の章では、変数と型について学びます。数値を扱えるようになりましょう!