次の例では:
int *i;
*i=1;
値を直接メモリ位置に置いていることがわかっているので、ハングするプログラムが生成されます。
私が持っている質問は、なぜ次のようなものですか?
int *i=1;
整数のキャストに関連する警告を生成するだけで、プログラムをハングさせませんか?
そして、なぜこの命令はエラーも警告も生成しないのですか?
char *s="acd";
前の例に似たものを使用している場合
ありがとう
3つのケースについて個別に話しましょう。
int *i;
*i=1;
最初の行はポインタにメモリを割り当てますが、それを初期化せず、iにランダムなガベージ値を残します。2行目は、ランダムなガベージによって記述されたメモリアドレスに書き込もうとし、実行時に未定義の動作を引き起こします。
int *i=1;
これにより、ポインタにメモリが割り当てられ、値1(つまりメモリアドレス0x1
)が割り当てられます。これは暗黙的に整数値1をポインタにキャストするため、null以外のメモリアドレスへのポインタを初期化することは非常にまれであるため、警告が表示されます。
余談ですが、もしあなたがするなら:
int *i=1;
*i=1;
値1をメモリアドレスに書き込もうとし0x1
、最初の場合と同じ未定義の動作を引き起こします。
char *s="acd";
これにより、スタック上にnullで終了する文字列が作成され、sがそれを指すようになります。文字列リテラルはchar*に割り当てるのが自然なことなので、警告はありません。
最初のケースは、メモリに書き込もうとしている唯一のケースです。
int *i; // i is a pointer but it's value is undefined
*i = 1; // Whoops, you just wrote to a random memory location.
int *i=1; // i is an invalid pointer (address 1), but you aren't writing to it
char *s="acd"; // s points to the string literal "acd" - again: no write.
int *i = 1;
と同等です:
int *i;
i = 1;
同様に:
char *s = "acd";
...と同等です:
char *s;
s = "acd";
タイプ定義でのアスタリスクの出現は、i
とs
がポインターであることを示すための表記法です。これは、型定義の外で使用されているアスタリスクが間接参照を示している場合とは異なります。
したがって、これらの場合、ポインタ値自体を割り当てるだけです...それを逆参照したり、ポインタが指すメモリに書き込んだりすることはありません。ポインタ自体のメモリはスタックに割り当てられるため、考慮されます。正しく初期化されていないときに逆参照して書き込もうとすると、危険が生じます。
文字列の場合、逆参照できることに注意してください。コンパイラが文字列リテラルを確認したときに、の4文字のメモリacd\0
が暗黙的に確保されました...その後、リテラルはそのメモリのアドレスに評価されました。だからあなたのs
ポインタは良いです。ポインタi
は任意の値であり、ほとんどのシステムで読み取れない可能性が非常に高くなります。
int *i;
ポインタを宣言します。関数で実行された場合、これは自動変数であり、初期化されません。その後、あなたがしようとすると
*i = 1;
名前に割り当てられたメモリ位置にあった値によって指定されたメモリにアクセスしますi
。おそらくそのメモリにアクセスする権限がないため、プログラムがクラッシュします。
あなたが書くとき
int *i = 1;
ポインタを宣言し、メモリ位置1を指すように初期化します。メモリ位置1に何も書き込もうとしていないことに注意してください。これは、ポインタを指しi
ているだけです。書き込もうとすると、再びセグメンテーション違反が発生します。
最後のケースでは、文字列リテラルを指す文字ポインターを宣言します("string"
そのリテラルが格納されているアドレスに評価されるため)。これは通常、読み取りはできるが書き込みはできないメモリの特別な部分に格納されます。
最初の部分では、宣言:
int *i=1;
実際には次のことと同じです。
int *i;
i=1;
つまり、ポインタiはメモリ位置1(32ビットアドレス空間がある場合は0x00000001)にあると宣言されます。警告は、整数をアドレスに変換しているという理由だけです。
2番目の部分では、文字列を宣言するための正しい構文です。これがエラーを引き起こさない理由です。
最初の例では
int *i;
*i = 1;
ポインタを宣言し、初期化しないようにします。つまり、初期化されていないということは、基本的にランダムな値を持っているため、ポインタがランダムなメモリ位置を指していることを意味します。次に、その場所のメモリを値に設定しようとすると、プログラムがクラッシュする可能性があります。初期化されていないポインタの使用は未定義の動作です。
2番目の例では、実際にポインターを初期化します。ポイントする場所に値を割り当てるのではなく、i
実際にはメモリアドレスにi
ポイント1
を設定します。
3番目の例は、2番目の例の変形であり、リテラル文字列が格納されs
ているメモリを指すようにポインタに指示します。"acd"
最初の例では:
int *i;
*i=1;
iが指す場所に1を割り当てていますが、これは初期化されていないため、無効な場所に書き込んでいる可能性があります。
2番目の例では:
int *i = 1;
ポインタ値iに値1を割り当てています。つまり、iがメモリアドレス1を指していると言っています。これは次のようになります。
int *i;
i = 1;
int*
iがタイプであり、タイプがであるため、警告が発生しますint
。
最後の例では:
char *s="acd";
文字列リテラルを指すようにsを割り当てています。文字列リテラルはプログラムの静的メモリに格納されるため、実行時に有効なアドレスに置き換えられます。stirngリテラルのタイプchar*
は、と同じタイプであるs
ため、警告はありません。
int *i;
変数がwhilei
へのポインタであることを宣言しますint
*i = 1;
に格納されているアドレスに整数1を格納しようとしi
ます。に何かを保存する前にこのコマンドを実行するとi
、これは未定義の動作になります。ランダムな値が含まれている可能性があるi
ため、ランダムなアドレスに1を格納しようとしています。
int *i = 1;
i
変数をへのポインタとして宣言し、int
そのポインタを1に初期化しようとします。1は整数であり、整数のアドレスではないため、警告が表示されます。
のような文字列リテラル"acd"
は、定義上、文字列内の文字を含む終了配列ですNULL
。char
割り当て時に、この配列はへのポインタに減衰しchar
ます。割り当てているchar *
ので、問題はありません。ただし、その配列の要素を変更することになっているとは思わないので、const char *
gccが喜んで警告するものに割り当てる方がよいでしょう。