Neden değiştirmeye çalışmamalıyım?
Çünkü tanımsız bir davranış. Alıntı C99 N1256 taslak 6.7.8 / 32 "Başlatma" :
ÖRNEK 8: Beyan
char s[] = "abc", t[3] = "abc";
"düz" karakter dizisi nesnelerini tanımlar s
ve t
öğeleri karakter dizesi değişmezleriyle başlatılır.
Bu beyan aşağıdakilerle aynıdır:
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
Dizilerin içeriği değiştirilebilir. Öte yandan, deklarasyon
char *p = "abc";
p
"işaretçi karakterine" türüyle tanımlar ve bunu, karakter karakter dizisi ile başlatılan 4 uzunluklu "karakter dizisi" türüne sahip bir nesneyi işaret edecek şekilde başlatır. p
Dizinin içeriğini değiştirmek için bir girişimde bulunulursa , davranış tanımsızdır.
Nereye gidiyorlar?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: yığın
char *s
:
.rodata
nesne dosyasının bölümü
.text
nesne dosyasının bölümünün Okuma ve Yürütme izinlerine sahip olan ancak Yazma bölümünün dökümünün alındığı bölüm
Programı:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Derleme ve derleme:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
Çıktı şunları içerir:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Böylece dize .rodata
bölümde saklanır .
Sonra:
readelf -l a.out
İçerik (basitleştirilmiş):
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
Varsayılan bağlayıcı komut, hem döker Bu araçlar .text
ve .rodata
yürütülür, ancak değiştirilmemiş olabilir, bir segmente ( Flags = R E
). Böyle bir segmenti değiştirmeye çalışmak Linux'ta bir segfault'a yol açar.
Eğer aynısını şu şekilde yaparsak char[]
:
char s[] = "abc";
elde ederiz:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
böylece yığınta depolanır (göreceli olarak %rbp
) ve elbette değiştirebiliriz.