C++プログラムがクラッシュする5つの主な原因をサンプルコード付きで解説
C++プログラムの異常動作は、しばしばプログラムのクラッシュ(異常終了)につながります。「Segmentation fault(セグメンテーション違反)」「Aborted」「Floating point exception(浮動小数点例外)」といったエラーメッセージを目にしたことがある方も多いのではないでしょうか。
本記事では、C++プログラムがクラッシュする代表的な原因を、サンプルコードと実行結果とともにわかりやすく解説します。
1. 例外(Exceptions)
C++における例外とは、プログラムが異常な状態に遭遇した際に発生する反応のことです。例外をtry-catchブロックで適切にハンドリングしない場合、プログラムはそのままクラッシュしてしまいます。
以下のプログラムは、ゼロ除算(0による割り算)が原因でクラッシュする典型的な例です。
サンプルコード
#include <iostream>
int main(){
int num1=10;
int num2=0;
int quotient=num1/num2;
printf("\n Quotient is: %d",quotient);
return 0;
}
実行結果
Floating point exception (core dumped)
2. バッファオーバーフロー(Buffer Overflow)
バッファとは、データを一時的に保存するための領域です。プログラムがバッファにデータを書き込む際、そのバッファが保持できるサイズを超えると、余分なデータがバッファの境界外にあふれ出し、隣接するメモリ領域を上書きしてしまいます。
以下のプログラムでは、書き込む文字列が変数numの保持できるサイズを超えたため、プログラムの動作が異常になりクラッシュします。
サンプルコード
#include <iostream>
#include <string.h>
int main(){
int num=100;
std::cout<<"\nValue for num:"<<num;
char c[2];
strcpy(c,"abcdefghijklmnopqrstuvwxyz");
std::cout<<"\nValue for c:"<<c;
return 0;
}
実行結果
Value for num:100
Segmentation fault (core dumped)
3. スタックオーバーフロー(Stack Overflow)
スタックオーバーフローは、呼び出しスタックのポインタがスタックの境界を超えたときに発生する問題です。スタックには限られた容量しか確保されておらず、プログラムが利用可能な領域を超えてスタックを使用すると、スタックがあふれてプログラムがクラッシュします。最も一般的な原因は無限再帰です。
以下のプログラムでは、return文の記述に誤りがあるため、factorial()関数が無限に呼び出され続けます。
サンプルコード
#include <iostream>
#include <string.h>
int factorial(int num){
if(num==0)
return 1;
else
return(factorial(num));
}
int main(){
int n=10;
int fact=factorial(n);
std::cout<<fact;
}
実行結果
Segmentation fault (core dumped)
正しくは、再帰呼び出しの際に引数を減らして「return factorial(num - 1);」とするなど、再帰が必ず終了条件に向かうように実装する必要があります。
4. セグメンテーション違反(Segmentation Fault)
セグメンテーション違反(コアダンプ)は、プログラムが自分に割り当てられていないメモリ領域にアクセスしようとしたときに発生します。
以下のプログラムでは、ポインタstrが無限にインクリメントされ、許可されていないメモリ領域へ文字を書き込み続けるため、クラッシュします。
サンプルコード
#include <iostream>
int main(){
char *str;
char name[]="iostream";
str=name;
while(1)
(*str++)='a';
}
実行結果
Segmentation fault (core dumped)
5. メモリリーク(Memory Leaks)
メモリリークは、動的に確保したメモリが解放されないまま放置されることで発生します。使用しなくなったメモリは必ず解放する必要があります。メモリの確保を繰り返すと、リークしたメモリが時間とともに蓄積し、最終的にはシステムのメモリを圧迫してプログラムのクラッシュにつながります。
以下のように、確保したメモリをfree()で解放しないコードの繰り返しが、メモリリークの典型的な原因です。
サンプルコード
#include <iostream>
int main(){
int *node;
node = (int *) malloc(9999999);
// free(node);
}
まとめ
C++のクラッシュは、主に「未処理の例外」「バッファオーバーフロー」「スタックオーバーフロー」「不正なメモリアクセス」「メモリリーク」が原因で発生します。try-catchによる例外処理、配列サイズの管理、再帰の終了条件の確認、動的メモリの適切な解放などを徹底することで、こうしたクラッシュを効果的に防ぐことができます。
-
C++で行列の各列の0の個数に基づいて列をソートし、インデックスを出力する方法
N行M列のサイズを持つ行列が与えられたとき、各列に含まれる0の個数を数え、その個数に基づいて列をソートし、ソート後の列のインデックスを出力するのが課題です。 例えば、1列目に0が1つ含まれ、2列目には0がまったく含まれず、3列目に0が2つ含まれている場合、0の少ない順に並べると出力は「2 1 3」になります。 入力例と出力例 Input: 0 0 0 1 1 1 1 0 1 Output: 1 3 2 解説 上記の入力における各列の0の個数は次のとおり
-
サイズ d の正十二角形を作れる組み合わせの数を求める C++ プログラム
問題概要 整数 d が与えられたとします。ここで、一辺の長さが 1 の正方形タイルと正三角形タイルが無限枚あるものと考えます。これらのタイルを組み合わせて、一辺の長さが d の正十二角形(12 辺形)を作るとき、その作り方が何通りあるかを求めるのがこの問題です。答えが非常に大きくなる場合は、998244353 で割った余りを返します。 アプローチ この問題は、二項係数を利用することで効率的に解くことができます。結論から言うと、求めるべき答えは C(2d−1, d−1)、すなわち「2d−1 個の中から d−1 個を選ぶ組み合わせの総数」です。 階乗を直接計算すると値が急激に大きくなりオーバー