はじめに
Linux環境における標準コンパイラインターフェースであるccを活用し、C言語プログラムのコーディングからバイナリ生成までの全手順をわかりやすく整理しました。開発環境のセットアップにはじまり、単一ソースコードのビルド、実務で必須となる各種コンパイルオプション(-oや-Wallなど)、さらには規模の大きい開発に欠かせない分割コンパイルの手順まで、実際に手を動かしながら体系的に習得できます。
開発環境のセットアップとccコマンドの動作確認
まずはUbuntu環境でC言語のビルドに必要なツールチェーン一式を導入します。パッケージ情報を最新化した上でメタパッケージを導入するため、sudo apt-get update -qq && sudo apt-get install -y build-essentialを実行しましょう。

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

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

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

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

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

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

成果物名のカスタマイズ(-o)と堅牢なビルドフラグ(-Wall, -O2)の導入
続いて、計算ロジックを含むサンプルプログラム(calc.c)を作成し、実践的なビルドオプションの効果を試してみましょう。同様にcatコマンドでコードを生成します。

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

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

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

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

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

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

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

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

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

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

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