それを変更しようとしないのはなぜですか?
未定義の動作だからです。C99 N1256ドラフト 6.7.8 / 32「初期化」からの引用:
例8:宣言
char s[] = "abc", t[3] = "abc";
「プレーン」なchar配列オブジェクトs
を定義し、t
その要素は文字列リテラルで初期化されます。
この宣言は
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
配列の内容は変更可能です。一方、宣言
char *p = "abc";
p
タイプ "pointer to char"で定義し、要素が文字列リテラルで初期化される長さ4のタイプ "array of char"のオブジェクトを指すように初期化します。を使用p
して配列の内容を変更しようとした場合の動作は未定義です。
彼らはどこへ行くのですか?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
:スタック
char *s
:
.rodata
オブジェクトファイルのセクション
.text
オブジェクトファイルのセクションがダンプされるのと同じセグメント。読み取りと実行の権限はありますが、書き込みはできません。
プログラム:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
コンパイルして逆コンパイルします。
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
出力に含まれるもの:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
したがって、文字列は .rodata
セクションにます。
次に:
readelf -l a.out
含む(簡略化):
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000704 0x0000000000000704 R E 200000
Section to Segment mapping:
Segment Sections...
02 .text .rodata
デフォルトのリンカスクリプトは両方をダンプこの手段.text
及び.rodata
実行が、変更されないことができるセグメントに(Flags = R E
)。そのようなセグメントを変更しようとすると、Linuxでsegfaultが発生します。
同じことをする場合char[]
:
char s[] = "abc";
私達は手に入れました:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
そのため、(に関連して%rbp
)スタックに格納され、もちろんそれを変更できます。