Kode berikut menerima kesalahan seg pada baris 2:
char *str = "string";
str[0] = 'z'; // could be also written as *str = 'z'
printf("%s\n", str);
Sementara ini bekerja dengan sangat baik:
char str[] = "string";
str[0] = 'z';
printf("%s\n", str);
Diuji dengan MSVC dan GCC.
c
segmentation-fault
c-strings
Markus
sumber
sumber
Jawaban:
Lihat C FAQ, Pertanyaan 1.32
sumber
mprotect
untuk melambangkan perlindungan read-only (lihat di sini ).Biasanya, string literal disimpan dalam memori read-only ketika program dijalankan. Ini untuk mencegah Anda mengubah konstanta string secara tidak sengaja. Dalam contoh pertama Anda,
"string"
disimpan dalam memori hanya-baca dan*str
menunjuk ke karakter pertama. Segfault terjadi ketika Anda mencoba mengubah karakter pertama menjadi'z'
.Dalam contoh kedua, string
"string"
yang disalin oleh compiler dari rumah read-only kepadastr[]
larik. Kemudian mengubah karakter pertama diizinkan. Anda dapat memeriksa ini dengan mencetak alamat masing-masing:Juga, mencetak ukuran
str
pada contoh kedua akan menunjukkan kepada Anda bahwa kompiler telah mengalokasikan 7 byte untuknya:sumber
%zu
untuk mencetaksize_t
Sebagian besar jawaban ini benar, tetapi hanya untuk menambah sedikit kejelasan ...
"Memori baca saja" yang dirujuk orang adalah segmen teks dalam istilah ASM. Itu adalah tempat yang sama di memori tempat instruksi dimuat. Ini hanya-baca untuk alasan yang jelas seperti keamanan. Saat Anda membuat karakter * diinisialisasi ke string, data string dikompilasi ke dalam segmen teks dan program menginisialisasi pointer untuk menunjuk ke dalam segmen teks. Jadi jika Anda mencoba mengubahnya, kaboom. Segfault.
Ketika ditulis sebagai array, kompiler menempatkan data string yang diinisialisasi dalam segmen data sebagai gantinya, yang merupakan tempat yang sama dengan variabel global Anda dan live tersebut. Memori ini bisa berubah, karena tidak ada instruksi di segmen data. Kali ini ketika kompilator menginisialisasi array karakter (yang masih hanya char *) itu menunjuk ke segmen data daripada segmen teks, yang dapat Anda ubah dengan aman saat dijalankan.
sumber
C99 N1256 konsep
Ada dua penggunaan literal string karakter yang berbeda:
Inisialisasi
char[]
:Ini "lebih banyak sihir", dan dijelaskan pada 6.7.8 / 14 "Inisialisasi":
Jadi ini hanyalah jalan pintas untuk:
Seperti array reguler lainnya,
c
dapat dimodifikasi.Di tempat lain: ia menghasilkan:
Jadi ketika Anda menulis:
Ini mirip dengan:
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":
6.7.8 / 32 "Inisialisasi" memberikan contoh langsung:
Implementasi ELF GCC 4,8 x86-64
Program:
Kompilasi dan dekompilasi:
Output berisi:
Kesimpulan: GCC menyimpannya
char*
di.rodata
bagian, bukan di.text
.Jika kami melakukan hal yang sama untuk
char[]
:kami memperoleh:
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:yang mengandung:
sumber
Dalam kode pertama, "string" adalah konstanta string, dan konstanta string tidak boleh dimodifikasi karena sering dimasukkan ke dalam memori read only. "str" adalah pointer yang digunakan untuk memodifikasi konstanta.
Dalam kode kedua, "string" adalah penginisialisasi array, semacam kependekan dari
"str" adalah array yang dialokasikan pada stack dan dapat dimodifikasi secara bebas.
sumber
str
global ataustatic
.Karena tipe
"whatever"
dalam konteks contoh pertama adalahconst char *
(bahkan jika Anda menugaskannya ke karakter non-const *), yang berarti Anda tidak boleh mencoba dan menulisnya.Compiler telah memberlakukan ini dengan meletakkan string di bagian read-only memori, karenanya menulis kepadanya menghasilkan segfault.
sumber
Untuk memahami kesalahan atau masalah ini, Anda harus terlebih dahulu tahu perbedaan b / w pointer dan array jadi di sini pertama-tama saya telah menjelaskan perbedaan Anda b / w mereka
array string
Dalam memori array disimpan dalam sel memori terus menerus, disimpan sebagai
[h][e][l][l][o][\0] =>[]
adalah 1 Char sel ukuran byte memori, dan ini sel-sel memori terus menerus dapat diakses dengan nama bernama strArray here.so sini array stringstrarray
itu sendiri berisi semua karakter string diinisialisasi ke it.In ini terjadi di sini"hello"
sehingga kita dapat dengan mudah mengubah konten memorinya dengan mengakses setiap karakter dengan nilai indeksnyadan nilainya berubah menjadi
'm'
nilai strarray berubah menjadi"mello"
;satu hal yang perlu diperhatikan di sini adalah bahwa kita dapat mengubah isi string array dengan mengubah karakter demi karakter tetapi tidak dapat menginisialisasi string lain secara langsung seperti
strarray="new string"
itu tidak validPointer
Seperti kita ketahui titik penunjuk menunjuk ke lokasi memori dalam memori, penunjuk yang tidak diinisialisasi menunjuk ke lokasi memori acak sehingga dan setelah inisialisasi menunjuk ke lokasi memori tertentu
di sini pointer ptr diinisialisasi ke string
"hello"
yang string konstan disimpan dalam memori hanya baca (ROM) sehingga"hello"
tidak dapat diubah karena disimpan dalam ROMdan ptr disimpan di bagian tumpukan dan menunjuk ke string konstan
"hello"
jadi ptr [0] = 'm' tidak valid karena Anda tidak dapat mengakses memori hanya baca
Tetapi ptr dapat diinisialisasi ke nilai string lainnya secara langsung karena itu hanya pointer sehingga dapat menunjuk ke alamat memori variabel variabel tipe datanya
sumber
Set di atas
str
menunjukkan nilai literal"string"
yang dikodekan secara keras dalam gambar biner program, yang mungkin ditandai sebagai hanya-baca dalam memori.Jadi
str[0]=
sedang mencoba untuk menulis ke kode read-only dari aplikasi. Saya kira ini mungkin tergantung pada kompiler.sumber
mengalokasikan pointer ke string literal, yang diletakkan oleh kompiler di bagian yang tidak dapat dimodifikasi dari executable Anda;
mengalokasikan dan menginisialisasi array lokal yang dapat dimodifikasi
sumber
int *b = {1,2,3)
seperti kita menulischar *s = "HelloWorld"
?FAQ C yang @matli ditautkan dengan menyebutkannya, tetapi belum ada orang lain di sini, jadi untuk klarifikasi: jika string literal (string kutipan ganda di sumber Anda) digunakan di mana pun selain untuk menginisialisasi array karakter (yaitu: @ Contoh kedua Mark, yang bekerja dengan benar), string itu disimpan oleh kompiler dalam spesial tabel string statis , yang mirip dengan membuat variabel statis global (read-only, tentu saja) yang pada dasarnya anonim (tidak memiliki variabel "nama "). Bagian read-only adalah bagian yang penting, dan itulah sebabnya contoh kode pertama Mark @ adalah.
sumber
int *b = {1,2,3)
seperti kita menulischar *s = "HelloWorld"
?Itu
line mendefinisikan pointer dan mengarahkannya ke string literal. String literal tidak dapat ditulis sehingga saat Anda melakukannya:
Anda mendapatkan kesalahan seg. Pada beberapa platform, literal mungkin berada dalam memori yang dapat ditulisi sehingga Anda tidak akan melihat segfault, tetapi itu kode yang tidak valid (menghasilkan perilaku yang tidak ditentukan) terlepas.
Garis:
mengalokasikan array karakter dan menyalin string literal ke dalam array itu, yang sepenuhnya dapat ditulis, sehingga pembaruan selanjutnya tidak ada masalah.
sumber
int *b = {1,2,3)
seperti kita menulischar *s = "HelloWorld"
?Literal string seperti "string" mungkin dialokasikan di ruang alamat yang dapat dieksekusi sebagai data hanya baca (beri atau ambil kompiler Anda). Ketika Anda menyentuhnya, itu aneh bahwa Anda berada di area pakaian renang dan membiarkan Anda tahu dengan kesalahan seg.
Dalam contoh pertama Anda, Anda mendapatkan pointer ke data const itu. Dalam contoh kedua, Anda menginisialisasi array 7 karakter dengan salinan data const.
sumber
sumber
Di tempat pertama,
str
adalah pointer yang menunjuk"string"
. Kompiler diizinkan untuk menempatkan string literal di tempat-tempat dalam memori yang tidak dapat Anda tulis, tetapi hanya dapat membaca. (Ini seharusnya memicu peringatan, karena Anda menetapkanconst char *
achar *
. Apakah peringatan Anda dinonaktifkan, atau apakah Anda mengabaikannya saja?)Di tempat kedua, Anda membuat array, yang merupakan memori yang Anda punya akses penuh, dan menginisialisasi dengan itu
"string"
. Anda membuatchar[7]
(enam untuk huruf-huruf, satu untuk mengakhiri '\ 0'), dan Anda melakukan apa pun yang Anda suka dengannya.sumber
const
awalan membuat variabel Read-Onlychar [N]
, tidakconst char [N]
, jadi tidak ada peringatan. (Anda dapat mengubahnya dalam gcc setidaknya dengan melewati-Wwrite-strings
.)Asumsikan stringnya adalah,
Dalam kasus pertama, literal harus disalin ketika 'a' masuk ke dalam ruang lingkup. Di sini 'a' adalah array yang didefinisikan pada stack. Ini berarti string akan dibuat pada stack dan datanya disalin dari kode (teks) memori, yang biasanya hanya-baca (ini adalah implementasi khusus, kompiler dapat menempatkan data program read-only ini dalam memori read-writable juga ).
Dalam kasus kedua, p adalah pointer yang didefinisikan pada stack (cakupan lokal) dan merujuk string literal (data program atau teks) yang disimpan di tempat lain. Biasanya memodifikasi memori semacam itu bukanlah praktik yang baik atau dianjurkan.
sumber
Pertama adalah satu string konstan yang tidak dapat dimodifikasi. Kedua adalah array dengan nilai yang diinisialisasi, sehingga dapat dimodifikasi.
sumber
Kesalahan segmentasi disebabkan ketika Anda mencoba mengakses memori yang tidak dapat diakses.
char *str
adalah pointer ke string yang tidak dapat dimodifikasi (alasan untuk mendapatkan segfault).sedangkan
char str[]
array dan dapat dimodifikasi ..sumber