6

誰もが知っているように、Visual C ++ランタイムは、初期化されていない、または解放されたばかりのメモリブロックを特別な非ゼロマーカーでマークします。初期化されていないすべてのメモリを手動でゼロに設定せずに、この動作を完全に無効にする方法はありますか?それは私の有効なnullではないチェックで大混乱を引き起こしています0xFEEEFEEE != 0

うーん、多分私はもう少しよく説明する必要があります。私は(newを介して)変数を作成して初期化しますが、それはすべてうまくいきます。(削除を介して)解放すると、ポインターが。0xFEEEFEEEの代わりにに設定されますNULL。の適切なチェックを挿入するとNULL、独自のメモリを管理するすべての優れたプログラムがそうであるように、問題なくチェックに0xFEEEFEEE合格すると問題が発生します。NULL削除するときにすべてのポインタを手動で設定する以外NULLに、メモリがすでに解放されたことを検出するための良い方法はありますか?Boostを使用するのはそれだけなので、Boostを使用したくないのは、オーバーヘッドが必要ないという理由だけです。

4

15 に答える 15

17

ポインタを作成するときは、明示的に に初期化しNULLます。の後も同様deleteです。初期化されていないデータの値によっては(一部の特定のケースを除く)、トラブルが発生しています。

boost::shared_ptrポインターが初期化されているかどうかを自動的に処理するスマート ポインター クラス ( など) を使用することで、多くの頭痛の種から解放されます。

于 2008-09-15T19:05:17.623 に答える
14

VC++ の動作は、実行できる有効なチェックで大混乱を引き起こすべきではありません。0xfeeefeee が表示されている場合は、メモリに書き込んでいない (またはメモリを解放していない) ため、とにかくメモリから読み取るべきではありません。

于 2008-09-15T19:08:39.270 に答える
8

初期化されていないメモリを読み取っている場合、チェックは間違いなく「有効」ではありません。メモリが解放されます。すでに別の用途で使用されている可能性があります。C/C++ では、初期化されていないメモリの内容を推測することはできません。

Java (および C#) は、割り当てられたメモリが使用前にゼロになることを保証します。もちろん、ガベージ コレクションにより、解放されたメモリがまったく表示されなくなります。しかし、それはメモリを直接公開する C ヒープのプロパティではありません。

于 2008-09-15T19:10:06.787 に答える
7

deleteオブジェクトへのすべてのポインターを にリセットするのは、 の責任ではありませんNULL。また、Windows DEBUG ランタイムのデフォルトのメモリ フィルを変更するべきではなく、boost::shared_ptr<>何らかの方法でポインターのようなものを使用する必要があります。

とはいえ、本当に自分の足を撃ちたいのなら、そうすることができます。

このようなアロケーター フックを使用して、Windows DEBUG ランタイム既定の塗りつぶしを変更できます。これは、HEAP に割り当てられたオブジェクトでのみ機能します。

int main(int argc,char** arv)
{
  // Call first to register hook    
  _CrtSetAllocHook(&zero_fill);
  // Do other stuff
  malloc(100);
}


int zero_fill(int nAllocType, 
              void* pvData, 
              size_t nSize,
              int nBlockUse, 
              long lRequest, 
              const unsigned char *szFileName, 
              int nLine )
{
  /// Very Importaint !! 
  /// infinite recursion if this is removed !!
  /// _CRT_BLOCK must not do any thing but return TRUE
  /// even calling printf in the _CRT_BLOCK will cause
  /// infinite recursion
  if ( nBlockUse == _CRT_BLOCK )
    return( TRUE );
  switch(nAllocType)
  {
  case _HOOK_ALLOC:
  case _HOOK_REALLOC:
    // zero initialize the allocated space.
    memset(pvData,0,nSize);
    break;
  case _HOOK_FREE:
    break;
  }
  return TRUE;
}
于 2008-09-15T19:37:08.797 に答える
6

デバッグ モードではなくリリース モードでビルドすると、ランタイムは初期化されていないメモリをまったく埋めませんが、ゼロにはなりません。ただし、この動作に依存するべきではありません。memset()、ZeroMemory()、または SecureZeroMemory() を使用してメモリを明示的に初期化するか、メモリがまだ初期化されていないことを示すフラグをどこかに設定する必要があります。初期化されていないメモリを読み取ると、未定義の動作が発生します。

于 2008-09-15T19:09:06.787 に答える
5

あなたは言う:

変数を (new を介して) 作成して初期化すると、すべてうまくいきます。(削除によって) 解放すると、ポインタが NULL ではなく 0xFEEEFEEE に設定されます。NULL の適切なチェックを挿入すると、独自のメモリを管理するすべての優れたプログラムがそうであるように、0xFEEEFEEE が NULL チェックを問題なく通過するため、問題が発生します。

MSVC のデバッグ ヒープ ルーチンでさえ、削除しているポインターの値を変更しません。削除しているポインターの値は (NULL にさえ) 変更されません。削除したばかりのオブジェクトに属するポインターにアクセスしているように思えますが、これは単純明快なバグです。

あなたがやろうとしていることは、単に無効なメモリアクセスを隠蔽するだけだと確信しています。実際に何が起こっているかを示すために、コードのスニペットを投稿する必要があります。

于 2008-09-16T15:51:15.960 に答える
4

これは、デバッガーでポインターの未割り当てメモリを確認できるため、実際には VC++ (および他のコンパイラーと信じています) の非常に優れた機能です。その機能を無効にする前に、よく考えます。C++ でオブジェクトを削除するときは、NULL何か後でオブジェクトを再度削除しようとする場合に備えて、ポインタを に設定する必要があります。この機能により、ポインターを に設定するのを忘れた場所を見つけることができますNULL

于 2008-09-16T03:16:12.017 に答える
4

@ジェフ・ハバード(コメント):

これは、実際には意図せずに私が望む解決策を提供してくれます。_HOOK_FREE で pvData を NULL に設定でき、ポインター アドレスの 0xFEEEFEEE で問題が発生することはありません。

これが機能している場合は、NULL ポインターをテストしているときに解放されたメモリを読み取っていることを意味します (つまり、ポインター自体は、解放したメモリに存在します)。

これはバグです。

あなたが使用している「解決策」は、バグを修正するのではなく、単に隠すことです。その解放されたメモリが別のものに割り当てられると、突然、間違った値を間違ったものへのポインターとして使用することになります。

于 2008-09-16T22:07:53.590 に答える
4

リリース モードで動作している場合は、せん断運が原因です。

Mike B が、デバッグ修正によってバグが隠されていると考えるのは正しいことです。解放モードでは、解放されているが に設定されていないポインタが使用されており、ポインタが指すNULLメモリはまだ「有効」です。将来のある時点で、メモリ割り当てが変更されるか、メモリ イメージが変更されるか、何らかの理由で「有効な」メモリ ブロックが「無効」になります。その時点で、リリース ビルドが失敗し始めます。デバッグモードは「修正済み」であるため、問題を見つけるためにデバッグモードに切り替えても意味がありません。

次のコードは機能しないことに全員が同意していると思います。

char * p = new char[16];     // 16 bytes of random trash
strcpy(p, "StackOverflow");  // 13 characters, a '\0' terminator, and two bytes of trash
delete [] p;                 // return 16 bytes to the heap, but nothing else changes;

if (p != NULL)               // Why would p be NULL?  It was never set to NULL
    ASSERT(p[0] == 'S');     // In debug, this will crash, because p = 0xfeeefeee and 
                             // dereferencing it will cause an error.
                             // Release mode may or may or may not work, depending on
                             // other memory operations

他のほぼすべてのポスターが言っているように、ポインターはNULL呼び出し後にに設定する必要がありますdelete。自分で行うか、ブーストや他のラッパーを使用するか、このスレッドのマクロを使用するかはあなた次第です。

于 2008-09-17T03:15:54.270 に答える
3

何が起こっているのかというと、私のコードはデバッグ コンパイルではクラッシュしますが、リリース コンパイルでは成功します。

顧客のマシンでリリース ビルドがクラッシュします。それはいつもそうです。

デバッガーでチェックしたところ、delete を呼び出した後、ポインターが 0xFEEEFEEE に設定されています。

ポインターに対して delete を呼び出した後、ポインターは変更されません。0xfeeefeee、0xfeeefeee、...、0xfeeefeeeに設定されるのは、彼らが指すメモリです。

プログラムが解放されたメモリ (DEBUG ビルドの 0xfeeefeee パターンによって都合よく示される) からデータを読み取ることに気付いた場合は、バグがあります。

于 2008-12-06T16:41:34.240 に答える
1

@[ジェフ ハバード]:

何が起こっているのかというと、私のコードはデバッグ コンパイルではクラッシュしますが、リリース コンパイルでは成功します。デバッガーで確認しましたが、ポインターを削除した後、ポインターが設定され0xFEEEFEEEています。繰り返しになりますが、リリース時の同じコードはクラッシュせず、期待どおりに動作します。

これは非常に奇妙な動作です。おそらく、回避策によって隠されている潜在的なバグがあると確信してい_CrtSetAllocHook()ます。

シグネチャは、0xFEEEFEEE解放されたメモリを示すために OS ヒープ マネージャによって使用されます ( http://www.nobugs.org/developer/win32/debug_crt_heap.htmlを参照)。万が一、再現コードを投稿して、使用しているコンパイラのバージョンを正確に示すことができますか?

于 2008-09-17T02:05:37.667 に答える
0

mallocを使用している場合、メモリは何にも初期化されません。あなたは何でも手に入れます。ブロックを割り当てて0に初期化する場合は、初期化のみでmallocに似た「calloc」を使用します(mallocをエミュレートする場合は1に設定する要素サイズパラメーター)。若干の違いがあるため、使用する前にcallocを確認する必要があります。

http://wiki.answers.com/Q/What_is_the_difference_between_malloc_and_calloc_functions

于 2008-09-15T22:53:19.683 に答える
0

独自の #define を作成し、それを使用する習慣をつけてみませんか?

いえ

#define SafeDelete(mem) { delete mem; mem = NULL; }
#define SafeDeleteArray(mem) { delete [] mem; mem = NULL; }

もちろん、好きな名前を付けることができます。deleteZ、deletesafe など、使い慣れたものなら何でも構いません。

于 2008-09-16T03:38:53.927 に答える
0

ここでビジュアルスタジオのデフォルトを無効にすることはできないと確信しています。無効にしたとしても、値はメモリが割り当てられる前にメモリにあったものになります。

最初にそれらを 0 に設定する習慣を身につけるのが最善の方法です。追加の文字は 2 つだけです。

int *ptr=0;

0 として定義されている NULL マクロを使用することもできます (ただし、デフォルトではないため、windows.h のようなものを含めて自分で定義するときは、複数の定義に注意してください!

于 2008-09-15T19:07:39.723 に答える