#include <stdio.h>
int main() {
char *t = "hello world";
puts(t);
//printf("%s", t);
t = "goodbye world";
puts(t);
}
tのメモリが割り当てられていないのに、実行したときにsegfaultが発生しないのはなぜですか?
#include <stdio.h>
int main() {
char *t = "hello world";
puts(t);
//printf("%s", t);
t = "goodbye world";
puts(t);
}
tのメモリが割り当てられていないのに、実行したときにsegfaultが発生しないのはなぜですか?
tはポインタなので、tが別の文字列を指すようにしているだけです。
文字列リテラルはプログラムメモリに静的に割り当てられるため、明示的にメモリを割り当てる必要はありません。
t
ここでは、読み取り専用メモリにある可能性のある匿名文字列の最初の文字へのポインタです。const char
ポインタを:へのポインタとして宣言することをお勧めします。
const char *t = "hello world";
こちらもご覧ください。
コンパイラが割り当てる必要のあるすべてのメモリはt
、32ビットシステムでは4バイトです。それは単なるポインタであることを忘れないでください。最初の数行では「HelloWorld」を指していますが、その後は「さようならの世界」を指すように変更します。Cは、定義した文字列に十分なメモリを割り当て、それらを指すことができるようにポインタを渡します。あなたはそれについて心配する必要はありません。また、これらの文字列は静的で読み取り専用であるため、安全に言うことはできませんt[4] = 'b';
。
メモリはt
;に割り当てられます。ポインタを保持するのに十分なメモリが割り当てられます(通常、32ビットプログラムでは4バイト、64ビットプログラムでは8バイト)。
さらに、の初期化t
により、ポインタが次の場所を指すようになります。
char *t = "hello world";
文字列リテラルにも、どこかにスペースが割り当てられます。多くの場合、これはメモリの読み取り専用部分にあるため、実際に使用する必要がconst char *t = "hello world";
あります。明示的なを使用しない場合でも、const
を指す文字列を変更しようとしないでくださいt
。t
しかし、それがどこか有効な場所を指していることを確認するのはコンパイラの問題です。
同様に、割り当て後:
t = "goodbye, Cruel World!";
変数は、コンパイラーによって割り当てられたスペースを指しています。あなたがそれを悪用しない限り(そしてあなたのコードが悪用しない限り)、これは問題ありません。
トラブルに巻き込まれるのは次のようなものです。
char *t;
puts(t); // t is uninitialized; undefined behaviour
t = 0; // equivalently, t = NULL;
puts(t); // t contains the null pointer; undefined behaviour
初期化されていないローカル変数には、任意の値を含めることができます。何が起こるかを確実に予測することはできません。一部のマシンでは、nullポインターが含まれていてクラッシュを引き起こす可能性がありますが、それは信頼できるものではありません。
nullポインターは有効なものを指さないため、nullポインターを逆参照すると、未定義の動作が発生し、多くの場合、未定義の動作はクラッシュになります。(通常、DEC VAXマシンでは、クラッシュではなくアドレス0でゼロバイトを取得しました。これにより、(部分的に)ヘンリースペンサーの 十戒の1つになりました。混沌と狂気が最後にあなたを待っているので、NULLポインタに従ってください。」)
したがって、プログラムでは、メモリが割り当てられ、t
初期t
化され、(読み取り専用の)文字列定数を指すように割り当てられるため、プログラムがクラッシュする理由はありません。