Bir dizeye yazarken neden bir segmentasyon hatası alıyorum?
C99 N1256 taslak
Karakter dizesi değişmezlerinin iki farklı kullanımı vardır:
Başlat char[]
:
char c[] = "abc";
Bu "daha sihirli" dir ve 6.7.8 / 14 "Başlatma" bölümünde açıklanmıştır:
Bir karakter türü dizisi, isteğe bağlı olarak parantez içine alınmış bir karakter dizesi değişmeziyle başlatılabilir. Karakter dizesi değişmezinin ardışık karakterleri (oda varsa veya dizi bilinmeyen boyutta ise son boş karakter dahil) dizinin öğelerini başlatır.
Yani bu sadece bir kısayol:
char c[] = {'a', 'b', 'c', '\0'};
Herhangi bir diğer normal dizi gibi, c
değiştirilebilir.
Başka her yerde: bir üretir:
Yani yazarken:
char *c = "abc";
Bu şuna benzer:
/* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
Dan örtülü döküm Not char[]
için char *
her zaman yasal olduğunu.
Daha sonra c[0]
değiştirirseniz __unnamed
, UB olan da değiştirirsiniz .
Bu, 6.4.5 "Dize değişmez değerleri" nde belgelenmiştir:
Çeviri aşaması 7'de, bir dize değişmez değeri veya değişmez değerlerden kaynaklanan her çok baytlı karakter dizisine sıfır değerinde bir bayt veya kod eklenir. Daha sonra çok baytlı karakter dizisi, diziyi içermek için yeterli olan bir dizi statik depolama süresi ve uzunluğunu başlatmak için kullanılır. Karakter dizesi değişmezleri için, dizi öğeleri char türüne sahiptir ve çok baytlı karakter dizisinin ayrı baytlarıyla başlatılır [...]
6 Elemanlarının uygun değerlere sahip olması koşuluyla bu dizilerin farklı olup olmadığı açıklanmamıştır. Program böyle bir diziyi değiştirmeye çalışırsa, davranış tanımsızdır.
6.7.8 / 32 "Başlatma" doğrudan bir örnek verir:
Ö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.
GCC 4.8 x86-64 ELF uygulaması
Programı:
#include <stdio.h>
int main(void) {
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
Sonuç: GCC char*
bunu .rodata
bölümünde değil bölümünde saklar .text
.
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ığın halinde depolanır ( %rbp
).
Ancak unutmayın varsayılan bağlayıcı komut koyar .rodata
ve .text
yürütmek sahip aynı segmentte, ancak hiçbir yazma izni de. Bu aşağıdakilerle gözlemlenebilir:
readelf -l a.out
içeren:
Section to Segment mapping:
Segment Sections...
02 .text .rodata