C でポインターを学習しようとしていますが、次の概念と混同されています。
char *string = "hello"
char *string2;
主な違いは何ですか:
A.) *string2 = string;
それから
B.) string2 = "bye";
一部の写真が役立つ場合があります。
次のメモリ マップを想定します (アドレスは完全に任意であり、既知のアーキテクチャを反映していません)。
項目アドレス 0x00 0x01 0x02 0x03 ---- ---------- ---- ---- ---- ---- 「こんにちは」0x00501234「h」「e」「l」「l」 0x00501238 'o' 0x00 「さようなら」0x0050123A「b」「y」 0x0050123C 'e' 0x00 0x?? 0x?? ... 文字列 0x80FF0000 0x00 0x50 0x12 0x34 string2 0x80FF0004 0x?? 0x?? 0x?? 0x??
これは、宣言後の状況を示しています。 "hello"
および"bye"
文字列リテラルでありchar
、メモリ内の「どこか」の配列として格納されるため、プログラムの存続期間にわたって使用できます。文字列リテラルの内容を変更しようとすると、未定義の動作が発生することに注意してください。、、など のstring
関数への引数として、文字列リテラル (または文字列リテラルのアドレスに評価されるようなポインター式) を渡したくありません。scanf
strtok
fgets
string
char
文字列リテラルのアドレスを含むへのポインタ"hello"
です。 string2
へのポインタでもありchar
、その値は不定です (0x??
は不明なバイト値を表します)。
あなたが書くとき
string2 = "bye";
"bye"
(0x0050123A)のアドレスを に割り当てると、string2
メモリ マップは次のようになります。
項目アドレス 0x00 0x01 0x02 0x03 ---- ---------- ---- ---- ---- ---- 「こんにちは」0x00501234「h」「e」「l」「l」 0x00501238 'o' 0x00 「さようなら」0x0050123A「b」「y」 0x0050123C 'e' 0x00 0x?? 0x?? ... 文字列 0x80FF0000 0x00 0x50 0x12 0x34 文字列 2 0x80FF0004 0x00 0x50 0x12 0x3A
単純に思えますよね?
では、発言を見てみましょう
*string2 = string;
ここにはいくつかの問題があります。
まず、余談です。C の宣言は、オブジェクトではなく式の型を中心にしています。 string2
文字へのポインタです。文字値にアクセスするには、単項演算子で逆参照する必要があります。 string2
*
char x = *string2;
式 の型*string2
はchar
であるため、宣言は次のようになります。
char *string2;
拡張により、式の型は string2
、char *
または へのポインタchar
です。
だからあなたが書くとき
*string2 = string;
タイプ( ) の値をタイプchar *
( string
)の式に代入しようとしています。とは互換性のある型ではないため、これは機能しません。このエラーは、翻訳 (コンパイル) 時に表示されます。もし書いていたらchar
*string2
char *
char
*string2 = *string;
両方の式が typechar
であり、代入は有効です。
ただし、まだ何も割り当てていない場合string2
、その値は不確定です。有効な書き込み可能なアドレスに対応する場合と対応しない場合があるランダムなビット文字列が含まれています。無効な可能性のあるランダムなポインター値を無視しようとすると、未定義の動作が発生します。正常に動作しているように見えるかもしれませんが、完全にクラッシュするかもしれません。その間に何かをするかもしれません。この問題は実行時まで現れません。さらに良いことに、文字列リテラル"bye"
を に割り当てた場合string2
、上記の問題に遭遇します。文字列リテラルの内容を変更しようとしています。繰り返しますが、これは実行時まで現れない問題です。
初心者のPOVを見逃して、他の回答者によって行われているいくつかの微妙な推論があります.
char *string = "hello";
文字配列を指すように初期化されるポインター変数を宣言します (伝統的に適切な型が一致します)。
ステートメント
*string = "hello";
ポインター変数であるべきものを逆参照し、ポイントされた場所に値を割り当てます。(これは変数宣言ではありません。それはその上でどこかで行う必要があります。) ただし、string
型があり (したがって型があり) 、代入の右側がポインター値を持つ式であるため、型の不一致がありchar *
ます*string
。char
これは、ステートメントの意図に応じて、次の 2 つの方法で修正できます。
string = "hello"; /* with "char *" expressions on both sides */
また
*string = 'h'; /* with "char" expressions on both sides */
string
1 つ目は、一連の文字 ( ) を含むメモリを指すように再割り当てしhello\000
ます。2 番目の代入は、 が指す文字をstring
値 に変更しchar
ますh
。
確かに、これは少し紛らわしいテーマであり、すべての C
プログラマーが理解するために多少の苦痛を経験しています。 ポインター宣言構文は、ステートメント内の同じテキストとは (関連はありますが) 少し異なる効果があります。 もっと練習して、ポインターを含む式を書いたりコンパイルしたりする経験を積んでいけば、私の言葉が完全に理解できるようになるでしょう。
char *
文字へのポインタです。などのリテラル"hello"
は、文字列の最初の文字へのポインタを返します。したがって、 文字列の最初の文字を指すstring = "bye"
意味があります。string
"bye"
*string
一方、は、が指す文字string
です。これはポインタではなく、8ビット整数です。これが、割り当てが無意味であり、格納されているメモリセグメントが読み取り専用*string = "bye"
であるため、セグメンテーション違反につながる可能性がある理由です。"bye"
編集後:
違いは、A) がコンパイルされないことです。コンパイルされた場合、初期化されていないポインターを逆参照しているため、未定義の動作になります。
また、投稿後に質問を大幅に変更しないでください。
*string
は、「何string
を指していても」と読むことができますchar
。それに割り当て"bye"
ても意味がありません。
AC 文字列は単なる文字の配列です。上記のような C 文字列リテラル"hello"
は、文字配列の最初の要素へのポインターを「返す」と見なすことができます{ 'h', 'e', 'l', 'l', 'o' }
。
したがって、char *string = "bye"
理にかなっていますが、そうではありchar string = "bye"
ません。