22

私はgdbで次のことを調べています:

char *a[] = {"one","two","three","four"};
char *b[] = {"one","two","three","four"};
char *c[] = {"two","three","four","five"};
char *d[] = {"one","three","four","six"};

...そして、私は以下を取得します:

(gdb) p a
$17 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"}
(gdb) p b
$18 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"}
(gdb) p c
$19 = {0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four", 0x80961b7 "five"}
(gdb) p d
$20 = {0x80961a4 "one", 0x80961ac "three", 0x80961b2 "four", 0x80961bc "six"}

文字列ポインターが同等の単語で同じであることに本当に驚いています。別の配列の文字列と同じであるかどうかに関係なく、各文字列がスタック上に独自のメモリを割り当てられると思っていたでしょう。

これはある種のコンパイラ最適化の例ですか、それともこの種の文字列宣言の標準的な動作ですか?

4

2 に答える 2

28

これは「文字列プーリング」と呼ばれます。Microsoft コンパイラではオプションですが、GCC ではオプションではありません。MSVC で文字列プールをオフにすると、異なる配列内の「同じ」文字列が複製され、メモリ アドレスが異なるため、余分な (不要な) 50 バイトほどの静的データが必要になります。

-fwritable-strings編集: v 4.0 より前の gcc には、文字列プーリングを無効にするオプションがありました。このオプションの効果は 2 つあります。文字列リテラルを上書きできるようにし、文字列プーリングを無効にしました。したがって、コードでこのフラグを設定すると、やや危険なコードが許可されます

/* Overwrite the first string in a, so that it reads 'xne'.  Does not */ 
/* affect the instances of the string "one" in b or d */
*a[0] = 'x';
于 2012-07-09T17:06:26.323 に答える
8

a( 、bcおよびがローカル変数として宣言されていると仮定しますd。これが、スタック関連の期待の理由です。)

C の文字列リテラルには静的な保存期間があります。それらは「スタック上」に割り当てられることはありません。それらは常にグローバル/静的メモリに割り当てられ、プログラムが実行されている限り「永久に」存続します。

a、、および配列がスタックbに割り当てられました。これらの配列に格納されているポインタは、静的メモリを指しています。このような状況では、同一の単語へのポインターが同一であることは異常ではありません。cd

コンパイラが同一のリテラルを 1 つにマージするかどうかは、コンパイラによって異なります。一部のコンパイラには、この動作を制御するオプションさえあります。文字列リテラルは常に読み取り専用です (そのconst char *ため、配列に型を使用することをお勧めします)。そのため、実際のポインター値に依存し始めるまでは、マージされているかどうかに大きな違いはありません。

PS 好奇心から:これらの文字列リテラルがスタックに割り当てられていたとしても、同じリテラルが複数回「インスタンス化」されると予想されるのはなぜですか?

于 2012-07-09T16:58:04.703 に答える