Mengapa saya mendapatkan kesalahan segmentasi saat menulis ke string?
C99 N1256 konsep
Ada dua penggunaan literal string karakter yang berbeda:
Inisialisasi char[]
:
char c[] = "abc";
Ini "lebih banyak sihir", dan dijelaskan pada 6.7.8 / 14 "Inisialisasi":
Array tipe karakter dapat diinisialisasi dengan string karakter literal, secara opsional tertutup dalam kurung. Karakter-karakter berturut-turut dari string karakter literal (termasuk karakter null terminating jika ada ruang atau jika array berukuran tidak diketahui) menginisialisasi elemen-elemen array.
Jadi ini hanyalah jalan pintas untuk:
char c[] = {'a', 'b', 'c', '\0'};
Seperti array reguler lainnya, c
dapat dimodifikasi.
Di tempat lain: ia menghasilkan:
Jadi ketika Anda menulis:
char *c = "abc";
Ini mirip dengan:
/* __unnamed is magic because modifying it gives UB. */
static char __unnamed[] = "abc";
char *c = __unnamed;
Perhatikan pemeran implisit dari char[]
hinggachar *
, yang selalu sah.
Kemudian jika Anda memodifikasi c[0]
, Anda juga memodifikasi__unnamed
, yaitu UB.
Ini didokumentasikan pada 6.4.5 "String literal":
5 Dalam fase terjemahan 7, byte atau kode bernilai nol ditambahkan ke setiap urutan karakter multibyte yang dihasilkan dari string literal atau literal. Urutan karakter multibyte kemudian digunakan untuk menginisialisasi array durasi penyimpanan statis dan panjang hanya cukup untuk mengandung urutan. Untuk literal karakter string, elemen array memiliki tipe char, dan diinisialisasi dengan byte individu dari urutan karakter multibyte [...]
6 Tidak ditentukan apakah array ini berbeda asalkan elemen mereka memiliki nilai yang sesuai. Jika program mencoba untuk memodifikasi array seperti itu, perilaku tidak akan ditentukan.
6.7.8 / 32 "Inisialisasi" memberikan contoh langsung:
CONTOH 8: Deklarasi
char s[] = "abc", t[3] = "abc";
mendefinisikan objek array char "biasa" s
dant
yang elemennya diinisialisasi dengan literal karakter string.
Deklarasi ini identik dengan
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
Isi dari array dapat dimodifikasi. Di sisi lain, deklarasi
char *p = "abc";
mendefinisikan p
dengan tipe "pointer to char" dan menginisialisasi untuk menunjuk ke objek dengan tipe "array of char" dengan panjang 4 yang elemennya diinisialisasi dengan karakter string literal. Jika ada upaya yang dilakukan untuk p
mengubah isi array, perilaku tersebut tidak ditentukan.
Implementasi ELF GCC 4,8 x86-64
Program:
#include <stdio.h>
int main(void) {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Kompilasi dan dekompilasi:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
Output berisi:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Kesimpulan: GCC menyimpannya char*
di .rodata
bagian, bukan di .text
.
Jika kami melakukan hal yang sama untuk char[]
:
char s[] = "abc";
kami memperoleh:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
sehingga disimpan di stack (relatif terhadap %rbp
).
Namun perlu dicatat bahwa skrip tautan default menempatkan .rodata
dan .text
di segmen yang sama, yang telah menjalankan tetapi tidak memiliki izin menulis. Ini dapat diamati dengan:
readelf -l a.out
yang mengandung:
Section to Segment mapping:
Segment Sections...
02 .text .rodata