web-dev-qa-db-ja.com

異なる文字列リテラルへの2つのcharポインターのアドレスは同じです

#include<stdio.h>
#include<string.h>

int main()
{
    char * p = "abc";
    char * p1 = "abc";
    printf("%d %d", p, p1);
}

2つのポインタの値を出力すると、同じアドレスが出力されます。どうして?

80
seereddi sekhar

同じ内容の2つの異なる文字列リテラルが同じメモリ位置に配置されるか、異なるメモリ位置に配置されるかは、実装によって異なります。

pp1は、同じアドレスを指す場合とそうでない場合があるため、常に同じ内容であっても2つの異なるポインターとして扱う必要があります。コンパイラの最適化に依存すべきではありません。

C11標準、6.4.5、文字列リテラル、セマンティクス

これらの配列が異なるかどうかは、それらの要素が適切な値を持っているかどうかは不明です。プログラムがそのような配列を変更しようとした場合の動作は未定義です。


印刷の形式は%pである必要があります:

  printf("%p %p", (void*)p, (void*)p1);

理由については この答え を参照してください。

86
P.P.

コンパイラはかなり賢いようで、両方のリテラルが同じであることを検出しています。また、リテラルは一定であるため、コンパイラーはそれらを2回保管しないことを決定しました。

これは必ずしもそうである必要はないことは言及する価値があるようです。 Blue Moonこれに対する回答 を参照してください。


ところで:printf()ステートメントは次のようになります

printf("%p %p", (void *) p, (void *) p1);

"%p"はポインター値を出力するために使用され、void *タイプのポインターに対してのみ定義されます。* 1


また、コードではreturnステートメントが抜けていると思いますが、C標準は変更中のようです。他の人は親切にこれを明確にするかもしれません。


* 1:ここでのvoid *へのキャストは、char *ポインターの場合は必要ありませんが、他のすべての型へのポインターの場合は必要です。

28
alk

コンパイラが「文字列プーリング」と呼ばれる処理を実行しました。両方が同じ文字列リテラルを指す2つのポインターが必要であることを指定したため、リテラルのコピーが1つだけ作成されました。

技術的に:ポインターを「const」にしないと不満が出たはずです

const char* p = "abc";

これはおそらく、Visual Studioを使用しているか、-WallなしでGCCを使用しているためです。

それらをメモリに2回保存することを明示的に希望する場合は、以下を試してください。

char s1[] = "abc";
char s2[] = "abc";

ここでは、文字への2つのポインターではなく、2つのc-string文字配列が必要であることを明示的に述べています。

警告:文字列プーリングはコンパイラー/オプティマイザー機能であり、言語のファセットではありません。異なる環境でのこのような異なるコンパイラーは、最適化レベル、コンパイラー・フラグ、およびストリングが異なるコンパイル単位にあるかどうかなどに応じて異なる動作を生成します。

18
kfsone

他の人が言ったように、コンパイラーはそれらが同じ値であることを認識しており、最終的な実行可能ファイルでデータを共有することを決定しています。しかし、より洗練されています:gcc -Oを使用して以下をコンパイルすると

#include<stdio.h>
#include<string.h>

int main()
{
  char * p = "abcdef";
  char * p1 = "def";
  printf("%d %d", p, p1);
}

4195780 4195783が表示されます。つまり、p1pの3バイト後に始まるため、GCCはdefの共通のサフィックス(\0ターミネーターを含む)を認識し、あなたが示したもの。

(コメントするには長すぎるため、これは答えです。)

14
huon

コード内の文字列リテラルは、コードの読み取り専用データセグメントに格納されます。 "abc"のような文字列リテラルを書き留めると、実際には 'const char *'が返され、コンパイラの警告がすべてあった場合、その時点でキャストしていることがわかります。あなたがこの質問で指摘したまさにその理由のためにそれらの文字列を変更することは許されていません。

3
Salgar

これは実際には使用しているコンパイラによって異なりますです。

TC++ 3.5を使用する私のシステムでは、2つのポインタに2つの異なる値、つまり2つの異なるアドレスを出力します。

あなたのコンパイラはそれがするように設計されていますメモリ内の任意の値の存在をチェックしますそしてその存在に応じてそれは再割り当てされますまたは同じ参照を使用します同じ値が参照される場合、以前に保存された値の.

ですから、あまり考えすぎないでくださいコンパイラーの解析方法によって異なりますコード。

それがすべてです...

2
Rajesh Paul

文字列リテラル( "abc")を作成すると、文字列リテラルを含むメモリに保存され、同じ文字列リテラルを参照する場合は再利用されます。つまり、両方のポインターが同じ場所を指し、「 abc "文字列リテラルが格納されます。

私はこれを少し前に学んだので、本当にはっきりと説明していなかったかもしれません、申し訳ありません。

2
Lord Zsolt

それはコンパイラの最適化ですが、移植性のための最適化を忘れています。コンパイルされたコードは、実際のコードよりも読みやすい場合があります。

1
Amir Saniyan

文字列「abc」自体がメモリ内のアドレスであるためです。あなたが再び「abc」を書くとき、それは同じアドレスを保存します

1
SANDEEP

あなたは文字列リテラルを使用しています、

コンパイラが2つの同じ文字列リテラルをキャッチすると、

同じメモリ位置を与えるため、同じポインタ位置を示します。

0
Dev