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


80

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


66
なぜそうすべきではないと思いますか?これらの両方のポインタは、まったく同じものを指しています。あなたが見ているのは、おそらく文字列プーリングと呼ばれる最適化手法の効果です。
ダニエルカミルコザール2013

2
データは同じですが、変数は異なります。
seereddi sekhar 2013

2
もちろん、変数は異なります。pとのアドレスを取得した場合p1、これら2つのポインタが2つの異なるアドレスに格納されていることに気付くでしょう。それらの値が同じであるという事実は、この場合は無関係です。
ダニエルカミルコザール2013

はい、値を変更するとアドレスが異なります。
seereddi sekhar 2013

11
@JanHudec:質問をもう一度読んでください。この場合(コンパイラの最適化のため)p == p1(違いはありません)が&p != &p1(違いはあります)。
MSalters 2013

回答:


87

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

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

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

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


印刷の形式は%p次のとおりです。

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


同じアドレスを使用してもメモリの最適化が行われないように、volatileを使用しました。1つの質問は、ポインタの1つを変更すると、他のポインタのデータも変更されるということです。
Megharaj 2013

8
@Megharajポインタi modify one of the pointer, will the data in the other pointed also be modifiedは変更できますが、文字列リテラルは変更できません。たとえばchar *p="abc"; p="xyz";未定義の動作をchar *p="abc"; p[0]='x';呼び出すのに対し、完全に問題ありません。これはとは何の関係もありませんvolatile。使用するかどうかにかかわらvolatileず、ここで関心のある動作を変更するべきではありません。volatile基本的に、毎回メモリからデータを読み取るように強制します。
PP

2
@MSharathHegdeはい。なぜならp、文字列リテラルの点"abc"p[0]='x'、文字列リテラルの最初の文字を変更することを試みます。文字列リテラルを変更しようとすると、Cの未定義の動作である
PP

2
@MSharathHegdeC標準がそれを述べているので。先行標準のC言語では文字列リテラルの変更が許可されていたため、その理由は主に歴史的なものです。その後、C標準(C89)で未定義にされたため、新しいコードではそれが行われず、古いコード(以前の標準)はそのまま機能します。基本的に、既存の(標準化前の)コードを壊さないことは妥協だと私は信じています。もう1つの理由は、文字列リテラルのタイプがchar []Cにあることです。したがって、(const char*C ++の場合のように)読み取り専用にするには、タイプも変更する必要があります。[続き]
PP

7
付録CにK&R 2nd Editionのラインがあります:"Strings are no longer modifiable, and so may be placed in read-only memory"、文字列リテラルは、その歴史的証拠使用;-)変更可能であること
PP

28

あなたのコンパイラは非常に賢いようで、両方のリテラルが同じであることを検出します。また、リテラルは定数であるため、コンパイラーはそれらを2回格納しないことを決定しました。

これは必ずしもそうである必要はないことを言及する価値があるようです。参照してくださいブルームーンさん、この上の答えを


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

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


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


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


ありがとう。結論はコンパイラの最適化ですよね?Cのmain関数は、デフォルトで0を返します
seereddi sekhar 2013

@seereddisekhar:はい、それは一種の最適化です。
2013

2
@seereddisekharただし、2つの文字列(ポインタも含む)を使用して比較する必要があるという意味ではないことに注意してください ==strcmpy()関数を使用てください。AlkがPSに答えたように、他のコンパイラが最適化を使用していない可能性があるため(コンパイラ次第-実装は依存しません)、BlueMoonはそれについて追加しました。
Grijesh Chauhan 2013

2
親愛なる@Megharaj:これについて別の質問をお願いしてもいいですか?この新しい質問へのリンクをコメントとしてここに投稿できます。
2013

1
@Megharaj:文字列リテラルの値を変更することはできません。私の質問で述べたように、それは一定です。
アルク

18

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

技術的には:ポインタを「const」にしないことについて不平を言うべきでした

これは、Visual Studioを使用しているか、-WallなしでGCCを使用していることが原因である可能性があります。

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

ここでは、文字への2つのポインターではなく、2つのc文字列文字配列が必要であることを明示的に指定します。

警告:文字列プーリングはコンパイラ/オプティマイザの機能であり、言語の側面ではありません。そのため、さまざまな環境でのさまざまなコンパイラは、最適化レベル、コンパイラフラグ、文字列がさまざまなコンパイル単位にあるかどうかなどに応じて、さまざまな動作を生成します。


1
gcc (Debian 4.4.5-8) 4.4.5を使用していても、文句を言わない(警告)-Wall -Wextra -pedantic
2013

1
はい、V4.8.1以降、デフォルトではgccconstは文字列リテラルに使用しないことについて警告しません。警告はオプションで有効になります-Wwrite-strings。これは明らかに(のような他のオプションで有効になっていない-Wall-Wextraまたは-pedantic)。
sleske 2013

1
GCC 4.4.7と4.7.2の両方で、-Wallの有無にかかわらず警告が表示されます。pastebin.com/1DtYEzUN
kfsone

15

他の人が言っているように、コンパイラはそれらが同じ値を持っていることに気づいているので、最終的な実行可能ファイルでデータを共有することを決定しています。しかし、それはより巧妙になります:私が以下をコンパイルするとgcc -O

それ4195780 4195783は私のために印刷します。つまり、のp13バイト後に開始するpため、GCCはdef\0ターミネータを含む)の共通サフィックスを確認し、表示したものと同様の最適化を実行しました。

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


3

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


2

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

私はこれを少し前に学んだので、それを本当に明確に説明しなかったかもしれません、ごめんなさい。


2

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

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

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

したがって、コンパイラがコードを解析する方法依存するため、あまり考えないでください。

それで全部です...


1

文字列「abc」自体がメモリ内のアドレスであるためです。もう一度「abc」と書くと、同じアドレスが保存されます


1

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


0

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.