オーバーフローとは、変数の取り得る値の範囲を超える場合に発生します。この事象が発生すると、以下のような動作が起きます。
以下のリスト8、9をご覧ください。
unsigned char sum = 0; sum = 255 + 1; printf("合計 = %d", sum);
合計 = 0
リスト8、9には、オーバーフローの例を示しました。unsigned char型の最大値は255です。255+1=256ですが、unsigned char型では取り扱えないため、「0」に戻ります。これが、オーバーフローです。
ゼロ除算とは、0で割る場合に発生する事象のことです。変数を0で除算する場合は、計算結果が「NaN(Not a Number)」のようなおかしな値となり、場合によってはプログラムの動作が停止します(リスト10)。
int a = 255, b = 0, res = 0; res = a / b; printf("結果 = %d\n", res);
リスト10は、ゼロ除算のプログラムの例で、筆者の環境ではプログラムが停止しました。
数学の授業でも、「0で割り算してはいけません」と教わりますが、プログラミングにおいても変わりません。
バグのないプログラムを作成するには、演算ルールに従って計算する必要があります。例えば、「不等号の間違い」「優先順位」「演算の順序」は要注意です。例えば、下記の行列計算を考えます。
上記は行列の計算例です。一般的な乗算は両辺を入れ替えても同じ結果になりますが、行列には交換の法則は成り立ちません。演算ルールは作成するプログラムにも依存しますので、注意してください。
以下の参考文献[2]によると、過去に下記のようなオーバーフローのバグがあったようです。
IoT時代の特徴の一つとして、24時間365日の連続使用が一般的になったことが挙げられる。サーバ攻撃には直接関係しない例だが、米ボーイングの787型機は、無線LANを用いた機体整備を行う、いわば巨大IoT機器である。機体を最新の状態に維持するために、原則として機体の電源を切らない。
しかし、787型機に搭載された電源制御ソフトウェアでは、この「電源を切らない」の想定が漏れていた。このソフトウェアでは、符号付き32ビット整数型を用いたカウンターにより、連続稼働時間を10ミリ秒ごとにカウントしていたが、想定漏れの結果、図4に示す通り、約248日で桁あふれを起こした。その結果、連続稼働時間を正しく計測できなくなった。
※[2]すべてわかるIoT大全2017(吉田琢也、2017年、日経BP)、222ページを一部引用。「図4」は書籍上の図番
このバグは、オーバーフローの一例であり、「248日問題」としてよく知られています※1)。
※1)「変数をカウントアップし、1秒間スリープ」を無限に実行するプログラムを考えます。int型を4バイトとすると取り扱える範囲は、-2147483648から2147483647まで扱えますね。カウンタは0から始めるため、原理上、2147483647/60/60/24=2万4855日カウントできます。
落とし穴は、スリープの秒数を変更した場合です。例えば、10msのスリープがあると考えると、2147483647/60/60/24/100=248.55日となり、248日目で不可解な動作が発生することとなります。
Copyright © ITmedia, Inc. All Rights Reserved.