C言語でプログラムを開発している際に、突然「Floating point exception (core dumped)」というエラーメッセージが表示され、実行が停止してしまうことがあります。
このエラーは非常に多くの開発者を悩ませるものですが、その原因の多くは非常に単純な論理ミスに起因しています。
「Floating point(浮動小数点)」という名称が含まれていますが、実際には整数同士の計算でも発生する点がこのエラーの紛らわしい特徴です。
本記事では、このエラーが発生する仕組みから、具体的な原因、そして効率的なデバッグと修正方法について詳しく解説します。
Floating point exception (core dumped)とは何か
「Floating point exception」は、直訳すると「浮動小数点例外」となりますが、UNIX系OS(Linuxなど)においては「SIGFPE」というシグナルを指します。
このシグナルは、CPUが算術演算において異常な処理を検知したときにOSからプロセスへ送られる通知です。
「core dumped」という記述は、異常終了した瞬間のメモリの状態(コアデータ)がファイルとして保存されたことを意味しています。
歴史的な経緯からこの名称が使われていますが、現代のプログラミングにおいては、「ゼロ除算(0での割り算)」が原因のほとんどを占めています。
浮動小数点の計算エラーだけでなく、整数の計算ミスでもこのメッセージが表示されることを覚えておきましょう。
SIGFPEが発生する主な仕組み
C言語のプログラムがコンパイルされると、計算処理はCPUの命令セットに変換されます。
CPUが除算命令を実行しようとした際、分母が0であると演算器が計算を継続できなくなり、ハードウェア例外が発生します。
OSはこの例外をキャッチし、該当するプロセスに対してSIGFPEシグナルを送信します。
デフォルトの動作では、このシグナルを受け取ったプロセスは強制終了し、エラーメッセージを出力する仕組みになっています。
エラーを引き起こす具体的な原因
このエラーの原因を特定するためには、どのようなコードがSIGFPEを誘発するのかを理解する必要があります。
代表的なパターンをいくつか見ていきましょう。
1. 整数におけるゼロ除算
C言語において、最も頻繁にこのエラーを引き起こすのが「整数型変数による0での割り算」です。
以下のサンプルコードを確認してください。
#include <stdio.h>
int main() {
int numerator = 10;
int denominator = 0;
// ここでエラーが発生します
int result = numerator / denominator;
printf("結果: %d\n", result);
return 0;
}
Floating point exception (core dumped)
このように、変数 denominator が0である状態で / 演算子を使用すると、即座にプログラムが停止します。
コンパイル時に数値が直接「0」と書かれている場合はコンパイラが警告を出してくれますが、実行時の入力や計算結果によって0になった場合は検知できません。
2. 剰余演算(%)におけるゼロ除算
割り算の余りを求める % 演算子も、内部的には除算を行っているため同様の例外が発生します。
多くの初心者が / だけを警戒し、% を見落としてしまう傾向があります。
#include <stdio.h>
int main() {
int a = 20;
int b = 0;
// 剰余演算でもSIGFPEが発生する
int remainder = a % b;
printf("余り: %d\n", remainder);
return 0;
}
剰余演算の場合も、分母が0になるとプログラムはクラッシュしてしまいます。
特にループ処理や動的なデータ処理の中で % を使う際は、分母が変化する可能性があるため注意が必要です。
3. 浮動小数点数での特殊なケース
実は、C言語において double や float を使った浮動小数点のゼロ除算は、デフォルトではSIGFPEを発生させないことが多いです。
IEEE 754規格に従う環境では、浮動小数点のゼロ除算は inf (無限大) や nan (非数) を返し、処理を継続します。
しかし、特定のコンパイラオプションや fenv.h を使って例外トラップを有効にしている場合、浮動小数点計算でもこのエラーが投げられます。
また、非常に大きな値を整数型に無理やりキャストしようとした際などに、稀にハードウェア依存のエラーとして発生することもあります。
エラーの修正方法と対策
原因がわかれば、修正方法は比較的シンプルです。
最も重要なのは、演算を行う前に分母が0でないことを保証することです。
if文による事前チェック
最も基本的かつ確実な方法は、除算を行う直前に条件分岐を入れることです。
#include <stdio.h>
int main() {
int num1 = 100;
int num2 = 0; // ユーザー入力などで変動すると想定
if (num2 != 0) {
int result = num1 / num2;
printf("計算結果: %d\n", result);
} else {
fprintf(stderr, "エラー: 0で割ることはできません。\n");
}
return 0;
}
このように、「分母が0の場合は計算をスキップする」というロジックを徹底してください。
ユーザーからの入力を受け取るプログラムでは、このチェックは必須のバリデーションとなります。
アサーション(assert)の活用
開発段階で「論理的にここが0になるはずがない」と考えている場所には、assert 関数を使うのが有効です。
#include <stdio.h>
#include <assert.h>
void calculate(int div) {
// divが0でないことをデバッグ時に保証する
assert(div != 0);
printf("計算: %d\n", 100 / div);
}
int main() {
calculate(0); // ここでアサーション失敗により停止する
return 0;
}
assert を使えば、SIGFPEとしてではなく「アサーション失敗」として停止するため、どこに問題があるのかがより明確になります。
製品版のビルドではアサーションを無効化できるため、開発効率を高めるツールとして非常に優秀です。
デバッグ手法:どこでエラーが起きているか特定する
大規模なプログラムでは、どのファイルの何行目でエラーが発生しているのかを見つけるのが大変です。
そんな時は、デバッガ「GDB」を活用しましょう。
GDBを使ったバックトレースの取得
GDBを使えば、プログラムがクラッシュした瞬間の場所をピンポイントで特定できます。
以下の手順で調査を行ってください。
- コンパイル時に
-gオプションを付けてデバッグ情報を付与する。 gdb ./プログラム名でデバッガを起動する。runコマンドを実行してプログラムを走らせる。- エラーが発生したら
backtrace(またはbt) コマンドを入力する。
これにより、エラーを引き起こした関数呼び出しの履歴と行番号が表示されます。
「Floating point exception」が発生した行がわかれば、その行にある変数の値を print コマンドで確認し、0になっている変数を見つけ出せます。
コンパイラ警告を最大にする
単純なミスであれば、コンパイラの警告レベルを上げるだけで発見できることがあります。
GCCやClangを使用している場合、-Wall -Wextra オプションを常に付けてコンパイルする習慣をつけましょう。
現代の賢いコンパイラは、コードの流れを解析して「ここでゼロ除算が起きる可能性があります」と教えてくれることがあります。
よくある落とし穴:計算順序と型変換
一見すると0で割っていないように見えても、計算の過程で0が発生するケースがあります。
整数の切り捨てによる0
C言語の整数同士の割り算では、小数点以下が切り捨てられます。
例えば (5 / 10) という計算の結果は 0 になります。
この結果を次の計算の分母に使ってしまうと、予期せぬSIGFPEを招きます。
int a = 5;
int b = 10;
int c = a / b; // cは0になる
int d = 100 / c; // ここでエラー
計算の途中経過で値がどう変化するか、特に「1未満になる可能性があるか」を意識することが重要です。
このような場合は、一時的に double 型にキャストして計算を行うなどの工夫が必要です。
まとめ
C言語における「Floating point exception (core dumped)」は、その名前に反して「整数型のゼロ除算」が主な原因です。
このエラーを回避するためには、計算を行う前に必ず分母が0でないかを確認するガード条件を入れることが最も効果的です。
万が一エラーが発生してしまった場合は、GDBなどのデバッガを活用して、どの変数が0になっているのかを素早く特定しましょう。
また、剰余演算子 % の使用時や、整数の切り捨てによる意図しない0の発生にも注意を払ってください。
堅牢なプログラムを書くためには、常に「最悪の入力(0)」が来る可能性を考慮し、適切にバリデーションを行う設計が求められます。
今回解説した内容を参考に、エラーに強いC言語のコードを執筆していきましょう。
