なぜ私はそれを変えようとすべきではないのですか?
未定義の動作だからです。C99N1256ドラフト 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";
タイプ「pointertochar」で定義p
し、要素が文字列リテラルで初期化される長さ4のタイプ「arrayofconstchar」のオブジェクトを指すように初期化します。配列の内容を変更するために使用しようとした場合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
にダンプすることを意味します( )。このようなセグメントを変更しようとすると、Linuxでセグメンテーション違反が発生します。.rodata
Flags = R E
同じことをするとchar[]
:
char s[] = "abc";
私達は手に入れました:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
したがって、スタックに格納され(に対して%rbp
)、もちろん変更できます。