realloc()にスタック変数へのポインターを渡すことは有効ですか?
-
19-08-2019 - |
質問
int main()
{
char myString = NULL;
realloc(&myString, 5);
strncpy((char *)&myString, "test", 5);
}
正常に動作するようですが、スタックとヒープについてはまだ少し混乱しています。これは許可されていますか?許可されている場合、myString
を手動で解放する必要がありますか、またはスコープ外になったときに解放されますか?
編集:回答ありがとうございます。これも同様に違法だと思います
//I want the code to change myString to "tests"
char myString[5] = "test";
realloc(&myString, strlen(myString)+2);
myString[4] = 's';
myString[5] = '\0';
解決
いいえ、これは完全に間違っています。 reallocは、mallocによって割り当てられたメモリの再割り当てにのみ使用する必要があります。あなたがしていることは偶然にしか機能せず、最終的にひどくクラッシュします
char *myString = malloc(x);
myString = realloc(myString,y);
free(myString)
newとdeleteを使用した方が良いでしょう。しかし、std :: stringを使用したほうが良いでしょう。
他のヒント
投稿したコードに関する問題:
- はい、malloc、realloc、および他の関連するCスタイルのメモリ割り当て関数で割り当てたものをすべて解放する必要があります。
- あなたはcharではなくchar * myStringを持つつもりだったと思う。スタック上の何か(char)のアドレスを渡すことは完全に間違っています。
- reStringで使用する前に、myString charポインターをNULLに初期化する必要があります。
- メモリを上書きする文字列が大きい場合は、4を5ではなくstrncpyに渡す必要があります。
- 例で作成したバッファを解放する必要があります
- realloc呼び出しの戻り値を確認する必要があります。 realloc()
[reallocの戻り値について:]サイズで正常に完了すると 0以外の場合、realloc()は (おそらく移動された)へのポインター 割り当てられたスペース。サイズが0の場合、どちらか nullポインターまたは一意のポインター に正常に渡すことができます free()が返されます。ない場合 十分な空きメモリ、realloc() nullポインターを返し、errnoを設定します [ENOMEM]へ。
-
NULLを渡すと、
- re-allocはmallocのように機能します。
ptrがNULLポインターの場合、realloc() に対してmalloc()のように動作します 指定されたサイズ。
より多くのC ++の方法:
ただし、これをC ++とタグ付けしました。C++のnew演算子を使用する方が型安全です。 new演算子は再割り当てを許可しませんが、割り当てと既存のバッファの再利用(配置new)には機能します。
char *myString = new char[5];
strncpy(myString, "test", 4);
//...
delete[] myString;
または偶数:
#include <string>
//...
std::string str = "test";
これは機能しません。そもそもmallocされていないものを再割り当てしています。いいえ、スコープ外になったときに解放されません-mallocまたはreallocを使用するときは、すべてあなた次第です。
更新:編集は何も変更しません-そもそもmallocされなかったものを再割り当てしようとしています。また、reallocからの戻り値を無視することはできません。reallocがメモリを別の場所に移動する必要がある場合、戻り値でそれを見つけることができます。言い換えれば:
char* ptr = malloc(4);
ptr = realloc(ptr, 5);
realloc後、ptrはメモリ内のまったく異なる場所を指している可能性があり、ptrの元の値を使用し続けると、解放されたメモリを使用したままにしてしまう可能性があります。 p>
これは危険です!これにより、スタックが破損します。関数のスタックに何かを再割り当てしてからmain()に戻ると、実際にはスタックフレームが上書きされ、main()以外の場所に戻ります。これは潜在的なセキュリティホールです。
次を実行してみてください。 reallocでクラッシュした場合、幸運になりました。 memcpy(<!> amp; myString)のようなもので深刻なダメージを与えることができます。
int dostuff();
int main()
{
dostuff();
return 0;
}
int dostuff()
{
char myString = NULL;
realloc(&myString, 5);
strncpy((char *)&myString, "test", 5);
return 0;
}
これは絶対にすべきではありません。スタック変数をfree()またはrealloc()しようとすると、破損したスタック(予測できない制御フローにつながる)、破損したヒープサービス構造、破損したユーザーメモリを含む(ただしこれらに限定されない)未定義の動作が発生する可能性があります。プログラムがAVでクラッシュするだけなら幸運です。場合によっては動作する可能性がありますが、絶対に実行しないでください。
経験則:割り当てられたメモリマネージャにのみメモリを返します。この場合、スタック変数をランタイムヒープに戻そうとしないでください。
プログラムは構文的には有効なC ++ですが、スタックオブジェクトのアドレスをヒープアロケーターに渡すため、未定義の動作が発生します。通常、これは実行時にプログラムがクラッシュすることを意味します。
スタックとヒープは、プログラムを実行するプロセスに割り当てられた2つの異なるメモリ領域です。引数とローカル変数を保持する関数を入力するとスタックが大きくなり、関数から戻ると自動的に縮小します。一方、ヒープは、必要に応じてメモリを取得できる個別のアドレス領域であり、不要になったら明示的に解放する必要があります。
ローカル変数のアドレスがrealloc()に渡されると、メモリを解放して他の場所に割り当てることを試みる場合があります。アドレスはヒープからのものではなく、realloc()はヒープで動作するため、これは失敗します。ほとんどの場合、realloc()はアドレスがヒープからのものではないことを検出し、プログラムを中止します。
これとは別に、サンプルプログラムにはいくつかの論理エラーが含まれています。
char myString = NULL;
文字列ではなく、charを保持する変数を宣言します。 Cスタイルの文字列の型はchar*
、つまりcharへのポインターです。
また、charにはNULL
が割り当てられます。これは、無効なポインタに通常割り当てられるアドレスゼロです。プリプロセッサが0
をリテラルrealloc()
に置き換えるため、これはコンパイルされます。実際には、charにゼロバイトを格納します。これも慣例により、Cスタイル文字列の終端文字です。
realloc(&myString, 5);
上記のように、スタックアロケータにスタックオブジェクトのアドレスを渡すため、これは違法です。この問題は2番目のコード例に残っています。
また、戻り値を破棄します。 <=>は、新しいメモリが割り当てられたアドレスを返します。以前と同じアドレスではない場合があります。 NULLの場合もあります。これは、メモリ不足になったことを通知する<=>の方法です。
strncpy((char *)&myString, "test", 5);
これは正しいですが、キャストは冗長です。
プログラムのより正しいバージョンは次のとおりです。
#include <stdlib.h>
#include <string.h>
int main()
{
/* allocate space for, say, one character + terminator */
char* myString = (char*) malloc(2);
/* some code using myString omitted */
/* get more space */
myString = (char*) realloc(myString, 5);
/* write to the string */
strncpy(myString, "test", 5);
/* free the memory */
free(myString);
return 0;
}
C ++では、realloc()を完全に避けることをお勧めします。たとえば、次のようなものを使用できます。
#include <string>
int main()
{
std::string myString;
/* some code using myString */
myString = "test";
return 0;
}
スタック上にあるためmyString
を解放する必要はありません(スコープを離れると<!> quot; freed <!> quot;を取得します)。
realloc
は違法です。アドレスはNULL
であるか、以前のmalloc
、calloc
、またはx
の呼び出しで返されたアドレスでなければなりません。
宣言するすべての変数はスタック上にあり、ポインタでも:
int * x;
変数pointer
はスタック上にあります!タイプは<=>で、アドレスを保持します。
x =(int *)malloc(sizeof(int));
は、<=>によって返されたアドレスを変数x!に割り当てます。 <=>の内容はメモリアドレスです!
あなたがしていることの問題は、変数ではない何かをいじっているということです。 myStringをcharとして定義したため、アドレスを変更しようとしています。それは悪いです。
realloc()関数は、渡されたものを変更することは想定されていません。ヒープ上のメモリへのポインタ(または、まだ割り当てられていない場合はnullポインタ)を受け取り、ヒープ上のメモリへのポインタを返します。
したがって、nullポインターまたはmalloc()またはrealloc()またはcalloc()によって割り当てられたものへのポインターを提供し、返されたポインターを格納します。
次のようなもの
char * myString = NULL;
myString = realloc(myString, 5);
機能しますが、myStringをfree()する必要があります。
ただし、C ++では、std :: stringを使用します。
2番目のコード例への応答:
はい、これも違法です。 myStringはmalloc(またはcalloc)では割り当てられないため、reallocで再割り当てしたり、freeで解放したりすることはできません。
さらに、reallocは、ポインターへのポインターを最初の引数として受け取りません。割り当てられたメモリへのポインタを受け取り、別の(場合によっては)ポインタを返します。代わりに次のような呼び出しを記述します。
myString = realloc(myString, strlen(myString)+2);