web-dev-qa-db-ja.com

Linuxでは、文字列リテラルのメモリアドレスが他のメモリアドレスと大きく異なるのはなぜですか?

文字列リテラルは、メモリ内で他の定数や変数(Linux OS)とは非常に異なるアドレスを持っていることに気付きました:多くの先行ゼロがあります(印刷されません)。

例:

const char *h = "Hi";
int i = 1;
printf ("%p\n", (void *) h);
printf ("%p\n", (void *) &i);

出力:

0x400634
0x7fffc1ef1a4c

それらは.rodata実行可能ファイルの一部。 OSが後でそれを処理する特別な方法があるので、リテラルはメモリの特別な領域になります(先行ゼロ付き)?そのメモリロケーションには利点がありますか、それとも何か特別なものがありますか?

62
Noidea

Linuxでのプロセスメモリのレイアウト方法は次のとおりです( http://www.thegeekstuff.com/2012/03/linux-processes-memory-layout/ から):

Linux process memory layout

。rodataセクションは、Initialized Global Dataブロックの書き込み保護されたサブセクションです。 ( [〜#〜] elf [〜#〜] 実行可能ファイルが。dataを指定するセクションはゼロ以外の値に初期化された書き込み可能グローバルに対応する書き込み可能グローバル。ゼロに初期化された書き込み可能グローバルは、。bssブロックに移動します。 static変数は配置に関係なく。)

写真は住所の数値を説明するものでなければなりません。

さらに調査したい場合、Linuxでは、実行中のプロセスのメモリレイアウトを記述する/ proc/$ pid/maps仮想ファイルを検査できます。予約された(ドットで始まる)ELFセクション名は取得しませんが、メモリ保護フラグを調べることにより、メモリブロックがどのELFセクションに由来するかを推測できます。たとえば、実行中

$ cat /proc/self/maps #cat's memory map

私にくれます

00400000-0040b000 r-xp 00000000 fc:00 395465                             /bin/cat
0060a000-0060b000 r--p 0000a000 fc:00 395465                             /bin/cat
0060b000-0060d000 rw-p 0000b000 fc:00 395465                             /bin/cat
006e3000-00704000 rw-p 00000000 00:00 0                                  [heap]
3000000000-3000023000 r-xp 00000000 fc:00 3026487                        /lib/x86_64-linux-gnu/ld-2.19.so
3000222000-3000223000 r--p 00022000 fc:00 3026487                        /lib/x86_64-linux-gnu/ld-2.19.so
3000223000-3000224000 rw-p 00023000 fc:00 3026487                        /lib/x86_64-linux-gnu/ld-2.19.so
3000224000-3000225000 rw-p 00000000 00:00 0
3000400000-30005ba000 r-xp 00000000 fc:00 3026488                        /lib/x86_64-linux-gnu/libc-2.19.so
30005ba000-30007ba000 ---p 001ba000 fc:00 3026488                        /lib/x86_64-linux-gnu/libc-2.19.so
30007ba000-30007be000 r--p 001ba000 fc:00 3026488                        /lib/x86_64-linux-gnu/libc-2.19.so
30007be000-30007c0000 rw-p 001be000 fc:00 3026488                        /lib/x86_64-linux-gnu/libc-2.19.so
30007c0000-30007c5000 rw-p 00000000 00:00 0
7f49eda93000-7f49edd79000 r--p 00000000 fc:00 2104890                    /usr/lib/locale/locale-archive
7f49edd79000-7f49edd7c000 rw-p 00000000 00:00 0
7f49edda7000-7f49edda9000 rw-p 00000000 00:00 0
7ffdae393000-7ffdae3b5000 rw-p 00000000 00:00 0                          [stack]
7ffdae3e6000-7ffdae3e8000 r--p 00000000 00:00 0                          [vvar]
7ffdae3e8000-7ffdae3ea000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

最初 r-xpブロックは間違いなく。text(実行可能コード)、最初のr--pブロックfrom。rodata、および以下のrw-ブロックfrom。 bssおよび。data (ヒープとスタックブロックの間には、動的リンカーによって動的にリンクされたライブラリからロードされたブロックがあります。)


注:標準に準拠するには、int* ために "%p"から(void*)またはその他の動作は未定義です。

73
PSkocik

これは、文字列リテラルの静的ストレージ期間があるためです。つまり、彼らはプログラム全体を通して生きます。このような変数は、いわゆるヒープにもスタックにもない特別なメモリ位置に格納できます。したがって、アドレスの違い。

16
Armen Tsirunyan

ポインタisの場所は、ポインタpointsの場所とは異なることに注意してください。より現実的な(リンゴ対リンゴ)比較は

printf ("%p\n", (void *) &h);
printf ("%p\n", (void *) &i);

hpのアドレスが類似していることに気付くでしょう。または、より現実的な別の比較は

static int si = 123;
int *ip = &si;
printf ("%p\n", (void *) h);
printf ("%p\n", (void *) ip);

hipが同様のメモリ領域を指していることがわかると思います。

7
Steve Summit

リテラルは読み取り専用変数であり、リテラルプールの概念もあることを考慮してください。リテラルプールとは、プログラムの一意のリテラルのコレクションです。参照が1つにマージされると、重複する定数は破棄されます。

ソースごとに1つのリテラルプールがあり、リンク/バインドプログラムの洗練度に応じて、リテラルプールを互いに隣接して配置して1つの.rodataを作成できます。

また、リテラルプールが読み取り専用で保護されているという保証もありません。コンパイラの設計では言語はそう扱います。

私のコードの断片を考えてみましょう。ができた

const char * cp = "hello world";
const char * cp1 = "hello world";

優れたコンパイラは、そのソースコード内で、読み取り専用リテラルcp、cp1が同一の文字列を指していることを認識し、cp1がcpのリテラルを指し、2番目のものを破棄します。

もう一点。リテラルプールは、256バイトの倍数または異なる値の場合があります。プールデータが256バイト未満の場合、スラックに16進ゼロが埋め込まれます。

異なるコンパイラーは、共通の開発標準に従い、[〜#〜] c [〜#〜]でコンパイルされたモジュールをAssembly languageでコンパイルされたモジュールにリンクすることを許可します。他の言語。 2つのリテラルプールは、.rodataに連続して配置されます。

1
printf ("%p\n", h); // h is the address of "Hi", which is in the rodata or other segments of the application.
printf ("%p\n", &i); // I think "i" is not a global variable, so &i is in the stack of main. The stack address is by convention in the top area of the memory space of the process.
0
user2760751