2

したがって、これを手動で修正できるので、緊急の質問ではありませんが、本当に奇妙だと思いました。

奇妙なことが起こる前の私のコード全体は次のとおりです。

int main(int argc, char** arg) {

int memory[100];
int loadCounter = 0;
bool getInput = true;
print_memory(memory);

そして、その他の無関係なもの。

印刷メモリは、すべてゼロに初期化する必要がある配列を印刷するだけですが、代わりに最初のいくつかの数値は次のようになります。

+1606636544 +32767 +1606418432 +32767 +1856227894 +1212071026 +1790564758 +813168429 +0000 +0000

(配列が埋められると、すべての数字は 0 から 1000 になるはずなので、プラスとフィラーのゼロは書式設定のためだけのものです。リストの残りはゼロです)

また、別の配列変数を初期化しようとしたため、メモリリークもありません。最初の実行では、大量の奇妙な数値も得られました。なぜこうなった?

4

5 に答える 5

2

「C++ 配列は何を初期化するのですか?」と尋ねたので、答えは、スコープに入ったときに割り当てられたメモリ内にあるものを初期化することです。つまり、それらは初期化されていません

一部のコンパイラは、デバッグ ビルドでスタック変数をゼロに初期化することに注意してください。これは、リリース ビルドを開始すると、厄介でランダムに発生する問題につながる可能性があります。

于 2013-09-29T00:46:09.953 に答える
1

使用している配列はスタックに割り当てられています:

intメモリ[100];

特定の関数スコープが終了する (この場合はメイン) または戻ると、メモリは再利用され、リークは発生しません。これがスタック割り当てメモリの仕組みです。この場合、ヒープではなくスタックに 100 個の整数 (私のコンパイラではそれぞれ 32 ビット) を割り当てました。ヒープ割り当ては、スタックから遠く離れたメモリ内の別の場所にあることを願っています。とにかく、ヒープに割り当てられたメモリにはリークの可能性があります。スタックに割り当てられた低レベルの Plain Old Data (コードに記述したように) はリークしません。

関数でランダムな値を取得した理由は、おそらく、整数の「メモリ」配列でデータを初期化しなかったためです。リリース モードでは、アプリケーションまたは C ランタイム (少なくとも Windows では) は、そのメモリを既知のベース値に初期化する処理を行いません。したがって、配列にあるメモリは、スタックが最後にそのメモリを使用していたときに残ったメモリです。数ミリ秒前 (可能性が最も高い) から数秒前 (可能性は低い)、数分前 (可能性は低い) の可能性があります。とにかく、これはガベージ メモリと見なされ、絶対に避ける必要があります。

問題は、print_memory という関数の内容がわからないことです。しかし、その関数がメモリをまったく変更しない場合、一見ランダムな値を取得している理由が説明されます。これらの値を使用する前に、まず何かに初期化する必要があります。私はスタックベースのバッファを次のように宣言するのが好きです:

int メモリ[100] = {0};

これは、コンパイラが配列全体をゼロで埋めるためのショートカットです。文字列やその他の基本的なデータ型でも機能します。

char MyName[100] = {0}; float NoMoney[100] = {0};

どのコンパイラを使用しているかはわかりませんが、Visual Studio で Microsoft コンパイラを使用している場合は問題ありません。

于 2013-09-29T00:47:57.210 に答える
1

他の回答に加えて、これを考慮してください:配列とは何ですか?

Java や C# などのマネージ言語では、高レベルの抽象化を使用します。C と C++ は抽象化を提供しません (OO 機能のような言語の抽象化ではなく、ハードウェアの抽象化を意味します)。これらは、金属に近く動作するように設計されています。つまり、言語は抽象化せずにハードウェア (この場合はメモリ) を直接使用します。

つまり、int aたとえば、ローカル変数を宣言すると、コンパイラは「わかりました。メモリのチャンクを[A,A + sizeof(int)]整数として解釈します。これを 'a' と呼びます」 (A は先頭とそのチャンクの開始アドレスと関数のスタック フレームの開始アドレス)。ご覧のとおり、コンパイラはメモリ セグメントを変数に「割り当てる」だけです。変数の「作成」などの「魔法」は行いません。コードはマシンで実行され、マシンにはメモリと CPU しかないことを理解する必要があります。魔法はありません。

では、関数の実行が開始されたときの変数の値は何でしょうか? 変数のメモリのチャンクが持つデータで表される値。通常、そのデータは現在の観点からは意味がありません (たとえば、文字列によって以前に使用されたデータの一部である可能性があります)。そのため、その変数にアクセスすると、範囲外の値が取得されます。それは私たちが「ガベージ」と呼んでいるものです: 私たちの文脈では意味をなさない、以前に書かれたデータです。

同じことが配列にも当てはまります。配列は、配列のすべての値に適合する十分なスペースを備えた、より大きなメモリのチャンクです: [A,A + (length of the array)*sizeof(type of array elements)]. 変数の場合と同様に、メモリにはがが含まれます

一般に、配列の宣言中に一連の値を使用して配列を初期化する必要があります。初期化子リストを使用してそれを実現できます。

int array[] = {1,2,3,4};

その場合、コンパイラは関数にコードを追加して、配列がその値であるメモリチャンクを初期化します。

補足: 非 POD タイプと静的ストレージ

上記の説明は、基本型や基本型の配列などのPOD 型にのみ適用されます。クラスのような非 POD 型では、コンパイラは変数のコンストラクターへの呼び出しを追加します。これは、クラス インスタンスの値 (属性) を初期化するように設計されています。

また、POD 型を使用する場合でも、変数にstatic ストレージ指定がある場合、プログラムの開始時に static 変数が割り当てられるため、コンパイラは既定値でメモリを初期化します。

于 2013-09-29T01:24:23.920 に答える
0

スタック上のローカル変数は c/c++ で初期化されません。c/c++ は高速になるように設計されているため、関数呼び出しでスタックがゼロになることはありません。

于 2013-09-29T00:46:51.223 に答える