Code Fix

上級

new[]とdeleteの不一致が「たまたま動いてしまう」危険性

new[]で確保した配列に対して、delete[]ではなく単体用のdeleteを使ってしまう不一致です。未定義動作であるにもかかわらず、int型のような単純な配列ではクラッシュせずに動いてしまうことがあります。

なぜエラーが出ないのか

出力: done(クラッシュせず動いてしまう)
(エラーなし)
C++は何も報告しません。コンパイルも実行も正常に完了するためです
出力: done(クラッシュせず動いてしまう)
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  #include <iostream>
 2  int main() {
 3      int *arr = new int[5];
 4      delete arr;  
              ^
 5      std::cout << "done" << std::endl;
 6      return 0;
 7  }
出力: done(クラッシュせず動いてしまう)危険性: 未定義動作であり保証された動作ではない

new[]で確保した配列にはdelete[]を使うのが正しい対応です。単体用のdeleteを使うのは未定義動作ですが、int型のような単純な配列ではクラッシュせずに動いてしまうことがあります。動いたからといって正しいわけではありません。

直し方: delete arr; を delete[] arr; にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <iostream>
 2  int main() {
 3      double *vals = new double[10];
 4      delete vals;  
               ^
 5      std::cout << "done" << std::endl;
 6      return 0;
 7  }
出力: done(クラッシュせず動いてしまう)危険性: 未定義動作であり保証された動作ではない

「クラッシュしないなら問題ない」というのは危険な誤解です。オブジェクトの配列で同じミスをすると、配列内の最初の要素以外のデストラクタが呼ばれないなど、より深刻な形で表面化することがあります。

直し方: delete vals; を delete[] vals; にします。

この問題を解いてみる →

パターン3

 1  #include <iostream>
 2  int main() {
 3      char *buf = new char[64];
 4      delete buf;  
              ^
 5      std::cout << "done" << std::endl;
 6      return 0;
 7  }
出力: done(クラッシュせず動いてしまう)危険性: 未定義動作であり保証された動作ではない

new[]とdeleteの対応、およびnewとdelete[]の対応、どちらの組み合わせ違いも未定義動作です。確保に使った形と解放に使う形は必ず揃える必要があります。

直し方: delete buf; を delete[] buf; にします。

この問題を解いてみる →

パターン4

 1  #include <iostream>
 2  int main() {
 3      long *nums = new long[8];
 4      delete nums;  
               ^
 5      std::cout << "done" << std::endl;
 6      return 0;
 7  }
出力: done(クラッシュせず動いてしまう)危険性: 未定義動作であり保証された動作ではない

long型の配列でも同じです。new[]で確保したらdelete[]で解放します。

直し方: delete nums; を delete[] nums; にします。

この問題を解いてみる →

パターン5

 1  #include <iostream>
 2  int main() {
 3      float *data = new float[20];
 4      delete data;  
               ^
 5      std::cout << "done" << std::endl;
 6      return 0;
 7  }
出力: done(クラッシュせず動いてしまう)危険性: 未定義動作であり保証された動作ではない

要素数が増えても規則は同じです。確保と解放の形を必ず揃えます。

直し方: delete data; を delete[] data; にします。

この問題を解いてみる →

よくある誤解

「クラッシュしないなら問題ない」というのは危険な誤解です。この不一致は未定義動作であり、動くかどうかは処理系や型(デストラクタの有無など)に依存します。オブジェクトの配列で同じミスをすると、配列内の最初の要素以外のデストラクタが呼ばれないなど、より深刻な形で表面化することがあります。

実務での勘所

多くの実装では、new[]は配列の要素数をポインタの直前に隠れたヘッダとして書き込んでおり、delete[]はそのヘッダを読んで「何個のデストラクタを呼ぶべきか」を判断します。単体用のdeleteはこのヘッダの存在を知らないため、最初の1要素分のデストラクタしか呼ばずに、誤った前提でメモリを解放してしまいます。ただしこれはあくまで典型的な実装上の振る舞いであり、C++の規格はnew[]とdeleteの不一致を単に「未定義動作」としか定めていません。動くように見える場合も規格上の保証は一切無い、という理解が重要です。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)