閉じる

【C言語】difftimeの使い方完全ガイド|経過時間計測・タイマー実装の実例付き

C言語で経過時間を扱う場合、最初に触れておきたい標準関数がdifftimeです。

処理時間の計測やタイマー機能、タイムアウト判定など、実用的なプログラムでは「何秒経過したか」を正確かつ安全に扱うことが重要になります。

本記事では、difftimeの基本から応用までを、図解とサンプルコードを交えながら詳しく解説し、実務レベルで使える知識に落とし込んでいきます。

difftimeとは?C言語で経過時間を扱う基本

difftimeの概要と役割

difftimeは、C言語標準ライブラリで提供されている2つの時刻の差(経過時間)を秒単位で返す関数です。

時刻を表すtime_t型の値を2つ受け取り、その差をdouble型の秒数として返します。

C言語で時間を扱う際は、まずtime関数で現在時刻をtime_tとして取得し、その差分をdifftimeで求めることが基本パターンとなります。

このとき、自力で引き算せずにdifftimeを使うことが推奨されます。

理由は後述しますが、環境依存の問題やオーバーフローを避け、安全に時間差を計算するためです。

time_tとdoubleの関係

time_t型は「暦時刻(calendar time)」を表す型で、通常は1970-01-01 00:00:00 UTCからの経過秒数を整数として保持します。

ただし、正確な型(32bitか64bitか、符号付きか符号なし)は処理系依存です。

difftimeは、このtime_t同士の差分をdoubleとして返します。

なぜ整数ではなくdoubleで返すのかというと、実装によってはtime_tが<sup>非常に大きな値</sup>になる可能性があるため、単純な整数型での差分では表現しきれないケースを想定しているからです。

また、将来的にサブ秒(ミリ秒など)を扱う拡張の余地を残している、という設計上の意味合いもあります。

経過時間計測にdifftimeを使うメリット

経過時間の測定はend - startと単純に引き算することもできますが、difftimeを使うことで次のようなメリットがあります。

  • 環境依存の違いを吸収しやすい
    32bit環境・64bit環境などでtime_tの型が異なっても、difftimeに任せることで安全に差分を計算できます。
  • オーバーフローに対する安全性が高い
    極端に大きな時刻差を扱う場合でも、直接の整数引き算に比べて、標準が想定した方法で計算されます。
  • APIとして意味が明確
    コードを読む人に「これは時間差を計算している」という意図が明確に伝わり、可読性が向上します。

このように、「time_t同士の差はdifftimeで計算する」というのが、C言語での基本スタイルになります。

difftimeの基本的な使い方

difftimeの関数プロトタイプとヘッダ

difftimeを使用するためには、<time.h>ヘッダをインクルードする必要があります。

関数プロトタイプは次の通りです。

C言語
#include <time.h>

/* difftime関数のプロトタイプ */
double difftime(time_t time1, time_t time0);

2つの引数time1time0はどちらもtime_t型で、関数はtime1 - time0の結果をdoubleで返します。

difftimeの戻り値と単位

difftimeの戻り値の単位は「秒」です。

具体的には、次のような性質があります。

  • 正の値: time1の方がtime0より後の時刻
  • 0: 2つの時刻が同じ
  • 負の値: time1の方がtime0より前の時刻

戻り値の型はdoubleなので、秒数として扱うのが基本ですが、そのままintlongにキャストして使用することもあります。

ただし、キャストによる情報落ち(小数切り捨て)には注意してください。

time関数と組み合わせた経過時間の取得

一般的な使い方は、time関数と組み合わせて「startからendまで何秒かかったか」を測定する方法です。

典型的なパターンは次のようになります。

  1. 処理開始時にtimestartを取得。
  2. 何らかの処理を行う。
  3. 処理終了時にtimeendを取得。
  4. difftime(end, start)で経過秒数を計算。

ここでは、単純な待機処理を行い、その前後でtimeを取得してdifftimeで経過秒数を求めるサンプルを示します。

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

/* 
 * difftimeの基本的な使い方サンプル
 * - time()で開始時刻と終了時刻を取得
 * - difftime()で経過秒数をdoubleで計算
 */
int main(void)
{
    time_t start;  /* 開始時刻を格納する変数 */
    time_t end;    /* 終了時刻を格納する変数 */
    double elapsed;/* 経過秒数(秒単位) */

    /* 現在時刻(開始時刻)を取得 */
    start = time(NULL);
    if (start == (time_t)-1) {
        /* time()が失敗した場合のエラーハンドリング */
        fprintf(stderr, "time() failed.\n");
        return 1;
    }

    printf("処理を開始します。3秒ほどお待ちください...\n");

    /* 簡易な待機処理: おおよそ3秒待つ(ポータブルではないがデモ用) */
    /* 実際にはsleep(3)などの関数を使うことを推奨 */
    {
        time_t now;
        do {
            now = time(NULL);
        } while (difftime(now, start) < 3.0);
    }

    /* 現在時刻(終了時刻)を取得 */
    end = time(NULL);
    if (end == (time_t)-1) {
        fprintf(stderr, "time() failed.\n");
        return 1;
    }

    /* difftimeで経過時間を計算 (end - start の秒数) */
    elapsed = difftime(end, start);

    printf("経過時間: %.0f 秒\n", elapsed);

    return 0;
}
実行結果
処理を開始します。3秒ほどお待ちください...
経過時間: 3 秒

環境によって1秒前後の誤差が生じる場合もありますが、秒単位の簡易的な計測としてはこのように使用します。

difftimeを使った経過時間計測とタイマー実装

処理時間の計測

プログラムの一部が「おおよそ何秒かかるか」を知りたい場合、difftimeは簡易な処理時間計測にも使えます。

ただし、time/difftimeは秒単位であり、高精度なベンチマークには不向きです。

ミリ秒単位以上の精度が必要な場合は後述の高精度タイマーを検討します。

以下は、ダミーの重い処理を行い、その前後で経過時間を測定する例です。

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

/* ダミーの重い処理(ただのループ) */
void heavy_work(void)
{
    volatile long long sum = 0;
    for (long long i = 0; i < 500000000LL; ++i) {
        sum += i;
    }
    /* volatileにしているので最適化で消されにくい */
}

int main(void)
{
    time_t start, end;
    double elapsed;

    /* 処理前の時刻 */
    start = time(NULL);
    if (start == (time_t)-1) {
        fprintf(stderr, "time() failed.\n");
        return 1;
    }

    heavy_work(); /* 計測したい処理 */

    /* 処理後の時刻 */
    end = time(NULL);
    if (end == (time_t)-1) {
        fprintf(stderr, "time() failed.\n");
        return 1;
    }

    elapsed = difftime(end, start);

    printf("heavy_work() にかかった時間: %.0f 秒\n", elapsed);

    return 0;
}
実行結果
heavy_work() にかかった時間: 2 秒

実行環境の性能によって秒数は変わりますが、全体としてどの程度時間がかかっているかを把握するのに有用です。

ループ処理の経過時間チェック

長時間動き続けるループ処理の中で、一定時間ごとに状態を確認したりログを出したりしたい場面でも、difftimeは便利です。

次のサンプルでは、無限ループの中で1秒ごとにメッセージを表示します。

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

int main(void)
{
    time_t start;
    time_t last_report;
    time_t now;
    double elapsed_total;
    double elapsed_interval;

    /* ループ開始時刻 */
    start = time(NULL);
    if (start == (time_t)-1) {
        fprintf(stderr, "time() failed.\n");
        return 1;
    }

    last_report = start;

    printf("ループを開始します。Ctrl+Cで終了してください。\n");

    while (1) {
        now = time(NULL);
        if (now == (time_t)-1) {
            fprintf(stderr, "time() failed.\n");
            break;
        }

        /* 全体の経過秒数 */
        elapsed_total = difftime(now, start);

        /* 前回ログ出力からの経過秒数 */
        elapsed_interval = difftime(now, last_report);

        if (elapsed_interval >= 1.0) {
            printf("経過時間: %.0f 秒\n", elapsed_total);
            last_report = now; /* ログ出力時刻を更新 */
        }

        /* ここで他の処理を行うイメージ */
    }

    return 0;
}

このように、「前回記録した時刻」と「現在時刻」の差difftimeで比較することで、一定周期の処理を実現できます。

タイマー機能の実装例

秒単位であれば、difftimeを使って簡易な「カウントダウンタイマー」を実装することもできます。

指定した秒数だけ待機し、その後に何らかの処理を実行するイメージです。

次のコードは、指定した秒数のカウントダウンを表示するサンプルです。

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

/* 簡易カウントダウンタイマー */
void countdown_timer(int seconds)
{
    time_t start, now;
    double elapsed;
    int remaining;

    start = time(NULL);
    if (start == (time_t)-1) {
        fprintf(stderr, "time() failed.\n");
        return;
    }

    while (1) {
        now = time(NULL);
        if (now == (time_t)-1) {
            fprintf(stderr, "time() failed.\n");
            break;
        }

        elapsed = difftime(now, start);   /* 経過時間(秒) */
        remaining = seconds - (int)elapsed; /* 残り秒数(小数は切り捨て) */

        if (remaining <= 0) {
            printf("\r残り時間: 0 秒     \n");
            printf("タイマー終了!\n");
            break;
        }

        /* \rで行頭に戻って上書き表示する */
        printf("\r残り時間: %d 秒", remaining);
        fflush(stdout);
    }
}

int main(void)
{
    int duration = 10; /* 10秒タイマー */
    printf("%d 秒タイマーを開始します。\n", duration);
    countdown_timer(duration);
    return 0;
}
実行結果
10 秒タイマーを開始します。
残り時間: 10 秒
残り時間: 9 秒
...
残り時間: 1 秒
残り時間: 0 秒
タイマー終了!

実際の時間と画面表示に多少のズレが出ることはありますが、秒単位の簡易タイマーとしては十分機能します。

タイムアウト処理の実装例

サーバー通信やユーザー入力待ちなど、「一定時間以内に何かが起こらなかったらタイムアウトする」という処理も、difftimeで書くことができます。

ここでは、ユーザーからの入力を待ちつつ、10秒以上入力がなければタイムアウトとする簡易的な例を示します。

注意

実際の標準入力の待ち時間制御はプラットフォーム依存の関数を使う必要があり、ここでは疑似的なループでイメージを示しています。

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

/*
 * 疑似タイムアウト処理サンプル
 * 実際にはselect()やpoll()、非同期I/Oなどが必要になる場面が多いですが、
 * difftimeで「タイムアウトしたかどうか」を判定するロジックのイメージを示します。
 */
int main(void)
{
    const int TIMEOUT_SECONDS = 10;
    time_t start, now;

    start = time(NULL);
    if (start == (time_t)-1) {
        fprintf(stderr, "time() failed.\n");
        return 1;
    }

    printf("%d秒以内に何か入力してください(疑似タイムアウト)。\n", TIMEOUT_SECONDS);

    while (1) {
        now = time(NULL);
        if (now == (time_t)-1) {
            fprintf(stderr, "time() failed.\n");
            return 1;
        }

        /* タイムアウト判定 */
        if (difftime(now, start) >= TIMEOUT_SECONDS) {
            printf("\nタイムアウトしました。\n");
            break;
        }

        /* 実際にはここで入力の有無をチェックする処理を行う */
        /* 例: select()でstdinを監視する など */

        /* デモ用にループを回し続けるだけ */
    }

    return 0;
}

このように、「処理開始からの経過時間が一定値を超えたらタイムアウト」というロジックはdifftimeで簡潔に表現できます。

秒から分・時間への変換テクニック

difftimeの戻り値は秒ですが、実際には分や時間で表示したい場合も多くあります。

その場合は単純な除算で変換できます。

典型的な変換方法を示します。

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

int main(void)
{
    time_t start, end;
    double elapsed;
    long total_seconds;
    int hours, minutes, seconds;

    /* デモ用に、startとendの差を手動で設定する場合もあるが、
       ここでは実際に数秒待ってから計測してみる。 */

    start = time(NULL);
    if (start == (time_t)-1) {
        fprintf(stderr, "time() failed.\n");
        return 1;
    }

    printf("10秒ほどお待ちください...\n");
    {
        time_t now;
        do {
            now = time(NULL);
        } while (difftime(now, start) < 10.0);
    }

    end = time(NULL);
    if (end == (time_t)-1) {
        fprintf(stderr, "time() failed.\n");
        return 1;
    }

    elapsed = difftime(end, start);  /* 経過秒数(小数を含む可能性あり) */

    /* 小数以下を切り捨てて整数秒に変換 */
    total_seconds = (long)elapsed;

    /* 時:分:秒に分解 */
    hours   = (int)(total_seconds / 3600);
    minutes = (int)((total_seconds % 3600) / 60);
    seconds = (int)(total_seconds % 60);

    printf("経過時間: %ld 秒 (約 %d時間 %d分 %d秒)\n",
           total_seconds, hours, minutes, seconds);

    return 0;
}
実行結果
10秒ほどお待ちください...
経過時間: 10 秒 (約 0時間 0分 10秒)

このように、秒→分→時間への変換は、3600(=60×60)や60で割ったり余りをとるだけで簡単に実現できます。

difftime使用時の注意点と実践テクニック

time_tの範囲とオーバーフローの注意点

time_tは処理系依存の型であり、その範囲や符号の有無によって表現できる日時の範囲が変わります。

代表的な例としては、次のような違いがあります。

環境の例time_tの典型的な実装表現可能なおおよその範囲
32bit UNIX系(古い)32bit符号付き整数1901年〜2038年(いわゆる2038年問題)
64bit UNIX系/modern64bit符号付き整数十分に広い(数十億年規模)
Windows(一部実装)32bit or 64bit整数実装依存

自分でtime_t同士を引き算すると、範囲の端に近い値を扱った場合にオーバーフローを起こす危険があります。

difftimeは、こうした環境依存の問題を考慮した形で時間差を返すことが期待されているため、できる限りdifftimeを利用する方が安全です。

環境依存(32bit/64bit)とポータビリティ

ポータブルなコードを書く上で重要なのは、time_tが何ビットか、符号付きかどうかを前提にしないことです。

次の点に注意すると、異なる環境でも動作しやすくなります。

  • time_tをintやlongに安易にキャストしない
    型サイズが合わず、情報が欠落する可能性があります。
  • 差分は必ずdifftimeで計算する
    end - startのような直接の演算は避けます。
  • printfでtime_tを表示する場合は工夫が必要
    %ldなどを安易に使うと環境によっては不正になるため、long longにキャストするなどの対応が必要です(実際にはprintfではなくstrftimeで整形表示することが多いです)。

difftimeを使うことで、「time_tの実体が何であっても、秒単位の差分をdoubleで扱える」というポータビリティ上の利点が得られます。

時計の変更やタイムゾーンの影響

timeが返すtime_tは、システム時計(リアルタイムクロック)を基準とした「暦時刻」を表します。

そのため、次のような操作の影響を受けます。

  • 管理者がシステム時計を手動で変更した。
  • NTPなどの時刻同期によって時計が前後に補正された。
  • タイムゾーン設定が変更された(ただしtime_t自体は通常UTC基準なので、ローカル時刻との対応が変わる形)。

長時間の計測を行っている途中で時計が大きく変更された場合difftimeで得られる値は実際の経過時間と一致しなくなる可能性があります。

このため、「経過時間の正確さ」が重要な用途では、後述するプロセス時間やモノトニッククロック(常に単調増加するカウンタ)を使うことが推奨されます。

clock関数との違いと使い分け

<time.h>にはclockという関数も存在し、こちらも時間関連の関数です。

しかし、clockdifftimeとは目的が異なります。

関数名目的・意味戻り値の型代表的な用途
time現在の暦時刻(カレンダー時刻)time_t現在日時の取得、日付時刻の表示
difftime2つの暦時刻の差(秒)double経過秒数の測定(粗い精度)
clockプロセスが消費したCPU時間clock_tプログラムのCPU使用時間の計測

clockは「CPU時間」であり、壁時計の時間(リアルタイム)とは異なります

I/O待ちが長いプログラムでは、clockの値はあまり増えませんが、difftimeでの経過時間は大きくなります。

使い分けの目安としては、次のように考えるとわかりやすいです。

  • ユーザーに「何秒経ちました」と見せたいtime + difftime
  • アルゴリズムの計算量を評価したい、CPU性能を測りたいclockや高精度タイマー

計測精度の限界と高精度タイマーへの橋渡し

difftimeが扱うtime_tは、通常1秒単位の精度しかありません。

そのため、次のような用途には不向きです。

  • ミリ秒以下の精度が必要なパフォーマンス測定
  • 短時間(数ミリ秒〜数十ミリ秒)の処理時間の比較
  • リアルタイム性が求められる制御

高精度な時間計測が必要な場合は、プラットフォーム固有の高精度タイマーを利用することになります。

  • POSIX系: clock_gettimegettimeofdayなど
  • Windows: QueryPerformanceCounterなど

これらは標準Cの範囲を超えますが、「秒単位でよい簡易な計測はdifftime、より精密な計測はOS依存API」という使い分けを意識しておくと、設計段階で迷いにくくなります。

まとめ

difftimeは、C言語で経過時間を秒単位で扱うための基本関数です。

timeと組み合わせることで、処理時間の簡易計測、ループ内での定期チェック、カウントダウンタイマー、タイムアウト判定など、さまざまな実用的機能をシンプルなコードで実現できます。

time_tの環境依存や時計変更の影響、clock関数との違いを理解したうえで、用途ごとに適切なAPIを選ぶことが重要です。

秒単位の粗い計測にはdifftimeを、それ以上の精度が必要な場合には高精度タイマーへと橋渡ししていく設計を心がけてください。

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

URLをコピーしました!