閉じる

【C言語】プログラムを異常終了する(abort)の使い方と注意点

プログラム実行中に取り返しのつかないエラーが起きた場合、処理を即座に打ち切って異常終了させたいことがあります。

C言語のabortはそのための関数です。

本記事では、abortの基本、exitとの違い、使い方、注意点、使うべき場面と避けるべき場面を、C言語初心者の方にもわかりやすく解説します。

異常終了(abort)とは

異常終了(abort)の意味

abortは、プログラムを即座に異常終了させるための関数です。

C言語標準ではabortを呼ぶとSIGABRTシグナルを発生させ、プログラムは決して通常の手続きでは戻りません

多くのOSではコアダンプを生成し、デバッガで原因調査ができるようになります。

abortは「復旧不能の重大エラーを検知したときに、安全のために即座に止める最終手段」です。

補足

  • C標準ではabortvoid abort(void)で宣言され、戻りません。
  • SIGABRTをハンドルしても、ハンドラから復帰すると最終的に異常終了します(実装依存の細部はありますが、継続実行は想定外です)。
  • POSIX環境ではデフォルトでコアダンプを出すことが多いです。

exitとの違い

exitは「正常な終了手続きを踏んで」終了する関数です。

一方abortは「異常終了」で、後片付けをスキップします。

違いを整理します。

項目exitabort
目的正常終了異常終了
atexitハンドラ呼ばれる呼ばれない
ストリームのflush原則行われる保証なし
シグナル送出しないSIGABRTを送出
終了ステータス指定値(EXIT_SUCCESS/EXIT_FAILUREなど)実装依存(一定でない)
コアダンプ通常なし環境次第で出る

「後片付けをして落ちる」ならexit、「とにかく止める」ならabortと覚えると理解しやすいです。

abortの使い方

ヘッダファイル(stdlib.h)をインクルード

abort<stdlib.h>で宣言されています。

使う前に必ずインクルードします。

C言語
#include <stdlib.h>  // abortを使うのに必要

関数の宣言と呼び出し

宣言は次の通りです。

引数はなく、戻り値もありません。

C言語
// C標準での宣言
void abort(void);

呼び出すと制御は戻らないため、この後に書いたコードは到達不能になります。

C言語
// 例: 到達不能であることを明示するコメントを付ける
abort();  // ここから先には進みません
/* 以降は実行されません */

最小のサンプル

もっとも小さな使用例です。

メッセージを1つ表示した後、異常終了します。

C言語
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    puts("プログラム開始");
    // 致命的なエラーを検出したと想定して異常終了する
    abort();  // 以降には到達しません
    puts("この行は実行されません");  // 到達不能
    return 0;  // 到達不能
}

実行結果の一例(Linuxの場合。環境により異なります)

プログラム開始
Aborted (core dumped)

「Aborted…」の行はシェルなどの実行環境が表示するもので、プログラム自身が出力したものではありません。

よくあるミス

初心者の方が陥りやすい点を、理由と対策付きでまとめます。

  1. 正常系のエラー処理にabortを使う
    利用者にとっては「ただ落ちる」ため不親切です。exit(EXIT_FAILURE)やエラーコードの返却で丁寧に終了させます。
  2. エラーメッセージを出さずにabortする
    原因調査が困難になります。fprintf(stderr, "...")で要因を出し、fflush(stderr)の後でabortしましょう。
  3. atexitやファイルcloseが実行されると思っている
    abortでは呼ばれません。必要な最低限のflushを自前で行ってからabortします。
  4. 終了コードを固定値だと思っている
    実装依存です。終了コードに依存した外部監視を書かないようにします。
  5. catchして続行できると思っている
    Cでは例外はありません。シグナルハンドラで受けても通常継続はできない前提です。

abortは「最後の切り札」。

むやみに使わず、使うときは情報を残して落とすことが肝心です。

abortの注意点

atexitは呼ばれない

atexitで登録した終了時処理はabortでは呼ばれません。

比較用に、同じコードをabortexitで実行してみます。

C言語
#include <stdio.h>
#include <stdlib.h>

static void on_exit_handler(void) {
    puts("atexitハンドラが呼ばれました");
}

int main(void) {
    if (atexit(on_exit_handler) != 0) {
        fputs("atexitの登録に失敗\n", stderr);
        return 1;
    }

    puts("abortの直前です");
    abort();  // ここで異常終了するため、on_exit_handlerは呼ばれない

    // 到達しません
    return 0;
}
実行結果
abortの直前です
Aborted (core dumped)

同等の処理をexitで行うと以下のようになります。

C言語
#include <stdio.h>
#include <stdlib.h>

static void on_exit_handler(void) {
    puts("atexitハンドラが呼ばれました");
}

int main(void) {
    atexit(on_exit_handler);
    puts("exitの直前です");
    exit(EXIT_FAILURE);  // 正常な終了手続き(異常終了コードだが手順は正常)
}
実行結果
exitの直前です
atexitハンドラが呼ばれました

後片付けを必ず実行したいならabortではなくexitを使います。

出力が消えることがある

stdout(標準出力)はバッファリングされます。

abortはバッファのflushを保証しないため、改行なしの出力などが消えることがあります。

悪い例(出力が消えることがある)

C言語
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    printf("重要なメッセージ(改行なし)");  // バッファに溜まる可能性がある
    abort();  // flushされずに終了 -> 表示されないことがある
}

改善例(stderrへの出力と明示的flush)

C言語
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    fprintf(stderr, "致命的エラー: 何らかの要因が発生しました\n");
    fflush(stderr);  // できるだけ確実に出力してから落とす
    abort();
}

stderrは多くの環境で無バッファまたは行バッファですが、仕様上は「必ず無バッファ」とは限りません。

確実性を上げるにはfflush(stderr)を併用します。

ファイルやリソースの後片付けはされない

開いているファイル、ソケット、動的メモリなどの後片付けはabortでは行われません。

OSはプロセス終了時に多くの資源を回収しますが、標準I/Oのユーザ空間バッファに残ったデータは失われます

失われる例

C言語
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    FILE *fp = fopen("example.txt", "w");
    if (!fp) {
        perror("fopen");
        return 1;
    }

    fputs("この行はflushせずに書いています", fp);  // まだユーザ空間バッファ
    // fcloseやfflushをしないまま異常終了
    abort();

    // 到達しません
    // fclose(fp);
}

この場合、example.txtが空のままだったり、途中までしか書かれていない場合があります。

最低限、ログなど残したいものはfflush(fp)してからabortしてください。

POSIX環境ならfsyncでディスクへの反映まで保証できますが、標準Cの範囲外です。

致命的エラー時に「完全な後片付け」を期待してはいけません。

必要最小限の情報を吐いて、安全に止めるのが基本方針です。

終了コードは一定でないことがある

abortの終了コードは実装依存です。

例として以下のような挙動が観測されます。

環境の例挙動の例
Linux(glibc)SIGABRT(番号6)で終了。シェルの終了ステータスは概ね128+6=134、メッセージ”Aborted (core dumped)”表示のことあり
macOS類似の挙動。コアダンプの扱いは設定次第
Windows(MSVC)終了コード3など。ダイアログや診断が出る場合あり

スクリプトや監視側で「終了コードがXならabort」と決め打ちしないでください。

abortを使う場面と避ける場面

復旧不能の致命的エラーで使う

次のような場面ではabortが有効です。

  • 内部不変条件の破れ(到達不能な分岐に到達、データ構造の重大破損)
  • セキュリティ的に危険な状態(検証不能な入力で一貫性が失われた等)
  • デバッグ時にコアダンプを取得したい場合

assertの失敗がabortに繋がるのは「プログラムの前提が壊れたら直ちに止める」ためです。

ユーザー向けアプリでは極力避ける

GUIアプリや一般ユーザーが使うCLIでは、可能な限り丁寧なエラー処理を心がけます。

設定ファイルの欠如やネットワーク障害など、外部要因で回復可能なエラーではabortではなく、メッセージ表示やexit(EXIT_FAILURE)、再試行などを選びます。

「アプリが突然落ちる」体験は避け、ユーザーに状況と対処を知らせるのが原則です。

エラーメッセージを出してからabortする

やむを得ずabortする場合でも、必ず情報を残してから落とします。

最小限の実装例です。

C言語
#include <stdio.h>
#include <stdlib.h>

// 致命的エラーを報告して直ちに異常終了する簡易マクロ
#define FATAL(msg) do { \
    fprintf(stderr, "FATAL %s:%d %s: %s\n", __FILE__, __LINE__, __func__, (msg)); \
    fflush(stderr); \
    abort(); \
} while (0)

int main(void) {
    // 例: 想定外の状態を検知
    int condition = 0;
    if (!condition) {
        FATAL("想定外の状態を検出しました");
    }

    // 到達しません
    return 0;
}
実行結果
FATAL sample.c:8 main: 想定外の状態を検出しました
Aborted (core dumped)

エラーログはstderrへ、flushしてからabort、が調査効率を大きく上げます。

まとめ

abortは「復旧不能な致命的エラーが起きたら、情報を残して即座に停止する」ための関数です。

exitとの違いは、atexitが呼ばれないストリームのflushが保証されない終了コードが一定でないなどにあります。

初心者の方は次の要点を押さえてください。

  • 通常のエラー処理ではabortを使わず、exitやエラー戻り値で丁寧に処理すること。
  • abort前にfprintf(stderr,...)で要因を出力し、fflushしてから落とすこと。
  • ファイルの内容やログが消える可能性に注意し、必要ならfflush(環境に応じてfsync)を行うこと。
  • 終了コードには依存しないこと。

「むやみに使わない」「使うなら情報を確実に残す」。

これがabortとの正しい付き合い方です。

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!