Ubuntuで始めるC言語入門:ccコマンドの基本からビルドオプションまで徹底解説

はじめに

Linux環境における標準コンパイラインターフェースであるccを活用し、C言語プログラムのコーディングからバイナリ生成までの全手順をわかりやすく整理しました。開発環境のセットアップにはじまり、単一ソースコードのビルド、実務で必須となる各種コンパイルオプション(-oや-Wallなど)、さらには規模の大きい開発に欠かせない分割コンパイルの手順まで、実際に手を動かしながら体系的に習得できます。

開発環境のセットアップとccコマンドの動作確認

まずはUbuntu環境でC言語のビルドに必要なツールチェーン一式を導入します。パッケージ情報を最新化した上でメタパッケージを導入するため、sudo apt-get update -qq && sudo apt-get install -y build-essentialを実行しましょう。

sudo apt-get update -qq && sudo apt-get install -y build-essential

システム上で標準のCコンパイラとして割り当てられている実体パスを調べるため、which ccを実行します。

which cc

ccはCソースコードを解析・アセンブル・リンクして実行可能ファイルを生成するための標準コマンドです。
組み込まれているコンパイラの実装やバージョン情報をチェックするには、cc --versionを実行してください。
⚙️ [頻出ビルドオプション一覧]

-o : 生成する実行可能バイナリの出力ファイル名を明示的に指定します。
-c : リンク処理を保留し、ソースコードからオブジェクトファイル(.o)の生成のみを行います。
-Wall : 潜在的なバグの原因となりうるコードに対して、主要な警告メッセージを網羅的に出力させます。
-g : GDBなどのデバッガで解析できるように、バイナリ内にデバッグ用シンボルを埋め込みます。
-O2 : 実行時の処理速度向上とバイナリサイズのバランスを考慮した最適化ビルドを適用します。

cc –version

サンプルコードの作成とシンプルな単一ファイルコンパイル

演習用の作業領域をクリーンに保つため、専用のディレクトリを作成してカレントパスを切り替えます。ターミナルでmkdir -p ~/c_practice && cd ~/c_practiceを実行しましょう。

mkdir -p ~/c_practice && cd ~/c_practice

動作テスト用のシンプルなC言語ソースファイル(hello.c)を用意します。ヒアドキュメントを利用してcatコマンドで直接ファイルへ書き出します。

![cat « ‘EOF’ > hello.c #include <stdio.h>

int main(void) { printf(“Hello, Linux cc!\n”); return 0; } EOF](captures/s02_c02_cat_____EOF____hello_c__include__stdio_h___int_mai.png)

準備したhello.cを引数に渡し、デフォルトの挙動でビルドするためにcc hello.cを実行します。
出力名を指定しない場合、Linuxコンパイラの伝統的な仕様に従ってカレントディレクトリにa.outという名称の実行ファイルが自動生成されます。

cc hello.c

ビルドされたバイナリが意図通りに動作するか確かめるため、相対パスで./a.outを実行します。
コンソール上にソース内で記述した「Hello, Linux cc!」のメッセージが出力されれば成功です。

./a.out

成果物名のカスタマイズ(-o)と堅牢なビルドフラグ(-Wall, -O2)の導入

続いて、計算ロジックを含むサンプルプログラム(calc.c)を作成し、実践的なビルドオプションの効果を試してみましょう。同様にcatコマンドでコードを生成します。

![cat « ‘EOF’ > calc.c #include <stdio.h>

int main(void) { int a = 15, b = 25; printf(“Result: %d\n”, a + b); return 0; } EOF](captures/s03_c01_cat_____EOF____calc_c__include__stdio_h___int_main.png)

コードの品質向上に必須となる警告オプション-Wallと、コード最適化を施す-O2を同時に指定し、さらに出力ファイル名をcalc_appに設定してビルドを行うため、cc -Wall -O2 -o calc_app calc.cを実行します。
文法ミスや警告が検出されなければ、指定したファイル名でバイナリが生成されます。

cc -Wall -O2 -o calc_app calc.c

目的の実行ファイル(calc_app)が正常に出力されているか、ls -l calc_appを実行してメタデータを確認します。
パーミッション欄に実行権限(x)が付与されており、有効なELFバイナリとして生成されていることが分かります。

ls -l calc_app

生成されたcalc_appを./calc_appで直接起動してみましょう。
プログラムが演算を実行し、ターミナルに「Result: 40」と表示されることを確認できます。

./calc_app

分割コンパイルの実践:オブジェクトファイル生成(-c)とリンク処理

中規模以上のC言語プロジェクトでは、複数のソースファイルに処理を分割して個別にコンパイルする手法が一般的に用いられます。まずは共通利用する関数プロトタイプを定義したヘッダーファイル(message.h)をcatコマンドで作成します。

cat « ‘EOF’ > message.h
#ifndef MESSAGE_H
#define MESSAGE_H
void print_msg(void);
#endif
EOF

次に、先ほど宣言した関数の本体ロジックを実装するモジュールファイル(message.c)を書き出します。

cat « ‘EOF’ > message.c
#include <stdio.h>
#include “message.h”
void print_msg(void) {
printf(“Modular Build Complete!\n”);
}
EOF

最後に、ヘッダーを取り込んでプログラムのエントリポイントとなるmain.cを記述します。

cat « ‘EOF’ > main.c
#include “message.h”
int main(void) {
print_msg();
return 0;
}
EOF

各ソースファイル(message.cおよびmain.c)を、最終的なバイナリ化を行わずに中間成果物であるオブジェクトファイルへとコンパイルするため、cc -c message.c main.cを実行します。
-cフラグを付与することでリンカの実行が抑制され、ソース単位で拡張子.oのファイルが出力されます。

cc -c message.c main.c

オブジェクトファイルが正しく配備されたか検証するため、ls -l *.oを実行してファイル一覧を出力します。
ディレクトリ内にmessage.oとmain.oの双方が生成されている様子が確認できます。

ls -l *.o

個別に用意された中間オブジェクト(message.oとmain.o)をリンカによって結合し、単一の実行可能バイナリ(modular_app)を組み上げるため、cc -o modular_app message.o main.oを実行します。
これによりモジュール間のシンボル解決が行われ、完全な実行ファイルが完成します。

cc -o modular_app message.o main.o

結合されたバイナリが正しく動作するかをテストするため、./modular_appを実行します。
別ファイルで定義したルーチンがスムーズに呼び出され、「Modular Build Complete!」という文字列が出力されることを確認しましょう。

./modular_app

本記事では、Ubuntu環境で基本となるccコマンドの扱い方から、実務開発で不可欠な各種コンパイルフラグの適用、さらにはモジュール分割ビルドの一連の流れを実践形式で解説しました。
これらの基礎コマンドとビルドオプションを自在に使いこなし、堅牢で効率的なLinuxネイティブのC言語プログラミング環境を構築してみてください。