次の小さなプログラムは、浮動小数点エラーをキャッチし、信号に関する情報を出力します。C言語の観点からは、ゼロによる除算は未定義の動作であることに注意してください。たとえば、すべてのシステムにPOSIX信号があるわけではありません。コンパイラーはエラーを簡単に予測できるため、POSIXシステムでも、すべてのコードを単純に削除するか、エラーですぐに終了するかを決定する場合があります。(私は、POSIXシステム上のコンパイラーが期待されることを実行するプログラムを生成することを期待し、想定します。その下では実行されます。しかし、そのような期待は以前はがっかりしました。)
#include <stdio.h>
#include <signal.h>
#include <stdlib.h> // for exit()
void fpehandler (int sig, siginfo_t *info, void *uc)
{
fprintf (stderr,
"Caught signal no. %d; code: %d (FPE_INTDIV would be %d)\n",
sig, info->si_code, FPE_INTDIV);
if(info->si_code == FPE_INTDIV)
{
fprintf (stderr,
"Yes, the error was an integer division by zero.\n");
}
// It's not officially safe to return from a handler
// of a "program error signal" (of which SIGFPE is an example).
// Plus many functions are not strictly safe to
// call in a signal handler (e.g. exit()).
// See https://www.securecoding.cert.org/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers .
// We call _Exit().
_Exit(0); // success, isn't it?
}
int main(void)
{
struct sigaction sa;
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = fpehandler;
sigaction (SIGFPE, &sa, NULL);
// cause "floating point" error
printf("%d\n", 2/0);
// ---- below is unreachable code ;-) ----------
// We shouldn't be here. Something went wrong. return 1.
return 1;
}
cygwin、gcc 5.4.0で実行すると、次のように出力されます。
$ gcc -Wall float-except.c -o float-except && ./float-except
float-except.c: In function 'main':
float-except.c:28:21: warning: division by zero [-Wdiv-by-zero]
printf("%d\n", 2/0);
^
Caught signal no. 8; code: 15 (FPE_INTDIV would be 15)
Yes, the error was an integer division by zero.