宣言と定義を分ける
公開する関数宣言をヘッダー、実装をソースファイルへ置きます。
calculator.hC
#ifndef CALCULATOR_H
#define CALCULATOR_H
int add(int left, int right);
#endifcalculator.cC
#include "calculator.h"
int add(int left, int right) { return left + right; }main.cC
#include <stdio.h>
#include "calculator.h"
int main(void) {
int result = add(10, 20);
printf("%d\n", result);
return 0;
}ヘッダーを二重に読み込まない
#ifndefから#endifまでがインクルードガードです。同じヘッダーが複数経路から読み込まれても、宣言を一度だけ有効にします。ヘッダーには利用者が必要な宣言だけを置き、内部専用関数はソースファイル内でstaticにできます。
別々にコンパイルしてリンクする
ビルドCOMMAND
gcc -std=c17 -Wall -Wextra -pedantic -c main.c
gcc -std=c17 -Wall -Wextra -pedantic -c calculator.c
gcc main.o calculator.o -o app
./app実行結果OUTPUT
30-cはリンクせずオブジェクトファイルを作ります。最後に必要なオブジェクトファイルをリンクして実行ファイルにします。
Makefileで手順を記録する
MakefileMAKE
CC = gcc
CFLAGS = -std=c17 -Wall -Wextra -pedantic
app: main.o calculator.o
$(CC) main.o calculator.o -o app
main.o: main.c calculator.h
$(CC) $(CFLAGS) -c main.c
calculator.o: calculator.c calculator.h
$(CC) $(CFLAGS) -c calculator.c
clean:
rm -f app main.o calculator.o実行COMMAND
make
./appコマンド行の先頭はスペースではなくタブです。makeは更新されたファイルに必要な処理だけを実行します。
警告とサニタイザーを使う
警告は不具合の候補です。学習中も-Wall -Wextra -pedanticを有効にし、理由を理解して解消します。GCCやClangを使える開発環境では、次のような検査も役立ちます。
検査付きビルド例COMMAND
gcc -std=c17 -Wall -Wextra -pedantic \
-fsanitize=address,undefined -g main.c calculator.c -o app
./app本番用とは分ける サニタイザーは開発時の検査用です。処理系によって利用方法が異なり、すべての不具合を見つける保証はありません。
デバッガーで値を追う
-g付きでビルドすると、GDBなどで停止位置を設定し、変数を確認できます。
GDBの基本操作COMMAND
gdb ./app
(gdb) break main
(gdb) run
(gdb) next
(gdb) print result
(gdb) continue
(gdb) quitデバッガーが出力するアドレスや行番号は環境とコードにより異なります。最初はmainで止め、一行ずつ進めて予想した値と比べます。
assertで小さく確認する
test_calculator.cC
#include <assert.h>
#include <stdio.h>
#include "calculator.h"
int main(void) {
assert(add(2, 3) == 5);
assert(add(-2, 2) == 0);
puts("すべて成功しました");
return 0;
}コンパイルと実行COMMAND
gcc -std=c17 -Wall -Wextra -pedantic test_calculator.c calculator.c -o test_calculator
./test_calculator実行結果OUTPUT
すべて成功しましたNDEBUGを定義するとassertは無効になります。利用者入力の検証には使わず、通常の条件分岐で処理します。
ミニ課題:料金計算を分割してテストする
単価と個数から合計を返すcalculate_total関数を作り、宣言をprice.h、定義をprice.c、利用例をmain.cへ分けてください。さらにtest_price.cで通常値、0個、複数個をassertし、すべてを警告なしでビルドします。
完成条件
- ヘッダーにインクルードガードがある
- Makefileの
appターゲットでアプリを作れる testターゲットでテストをビルドして実行できる- 一つのソースを変更したとき、必要なファイルだけ再コンパイルされる
