Di C, seseorang dapat menggunakan string literal dalam deklarasi seperti ini:
char s[] = "hello";
atau seperti ini:
char *s = "hello";
Jadi apa bedanya? Saya ingin tahu apa yang sebenarnya terjadi dalam hal durasi penyimpanan, baik pada saat kompilasi maupun saat dijalankan.
Jawaban:
Perbedaannya di sini adalah itu
akan ditempatkan
"Hello world"
di bagian read-only memori , dan membuats
pointer ke yang membuat operasi penulisan pada memori ini ilegal.Saat melakukan:
menempatkan string literal dalam memori read-only dan menyalin string ke memori yang baru dialokasikan di stack. Dengan demikian membuat
hukum.
sumber
"Hello world"
ada di "bagian read-only memori" dalam kedua contoh. Contoh dengan titik-titik array di sana, contoh dengan array menyalin karakter ke elemen array.char msg[] = "hello, world!";
string yang berakhir di bagian data yang diinisialisasi. Ketika dinyatakanchar * const
berakhir di bagian data hanya baca. gcc-4.5.3Pertama, dalam argumen fungsi, keduanya persis sama:
Dalam konteks lain,
char *
alokasikan pointer, sementarachar []
alokasikan array. Di mana string pergi dalam kasus sebelumnya, Anda bertanya? Compiler diam-diam mengalokasikan array anonim statis untuk menahan string literal. Begitu:Perhatikan bahwa Anda tidak boleh mencoba mengubah konten array anonim ini melalui pointer ini; efeknya tidak terdefinisi (sering berarti crash):
Menggunakan sintaks array secara langsung mengalokasikannya ke memori baru. Dengan demikian modifikasi aman:
Namun array hanya hidup selama ruang lingkup yang sesuai, jadi jika Anda melakukan ini dalam suatu fungsi, jangan kembali atau membocorkan pointer ke array ini - membuat salinan sebagai gantinya dengan
strdup()
atau serupa. Jika array dialokasikan dalam ruang lingkup global, tentu saja, tidak ada masalah.sumber
Deklarasi ini:
Menciptakan satu objek -
char
array ukuran 6, disebuts
, diinisialisasi dengan nilai-nilai'h', 'e', 'l', 'l', 'o', '\0'
. Di mana array ini dialokasikan dalam memori, dan berapa lama itu hidup, tergantung di mana deklarasi muncul. Jika deklarasi berada dalam suatu fungsi, ia akan hidup sampai akhir blok yang dideklarasikan, dan hampir pasti dialokasikan pada stack; jika itu di luar fungsi, itu mungkin akan disimpan dalam "segmen data yang diinisialisasi" yang diambil dari file yang dapat dieksekusi ke dalam memori yang dapat ditulisi ketika program dijalankan.Di sisi lain, deklarasi ini:
Menciptakan dua objek:
char
s yang mengandung nilai-nilai'h', 'e', 'l', 'l', 'o', '\0'
, yang tidak memiliki nama dan memiliki durasi penyimpanan statis (yang berarti bahwa ia hidup untuk seluruh kehidupan program); dans
, yang diinisialisasi dengan lokasi karakter pertama dalam array read-only yang tidak disebutkan namanya itu.Array read-only yang tidak disebutkan namanya biasanya terletak di segmen "teks" dari program, yang berarti ia dimuat dari disk ke memori read-only, bersama dengan kode itu sendiri. Lokasi
s
variabel pointer di memori tergantung pada tempat deklarasi muncul (seperti pada contoh pertama).sumber
char s[] = "hello"
kasus ini,"hello"
ini hanya merupakan penginisialisasi memberitahu kompiler bagaimana array harus diinisialisasi. Ini mungkin atau mungkin tidak menghasilkan string yang sesuai di segmen teks - misalnya, jikas
memiliki durasi penyimpanan statis maka kemungkinan bahwa satu-satunya contoh"hello"
akan berada di segmen data yang diinisialisasi - objeks
itu sendiri. Bahkan jikas
memiliki durasi penyimpanan otomatis, ini dapat diinisialisasi dengan urutan penyimpanan literal daripada salinan (mis.movl $1819043176, -6(%ebp); movw $111, -2(%ebp)
)..rodata
, yang kemudian skrip linker dibuang ke segmen yang sama.text
. Lihat jawaban saya .char s[] = "Hello world";
menempatkan string literal dalam memori read-only dan menyalin string ke memori yang baru dialokasikan di stack. Tapi, jawaban Anda hanya berbicara tentang put string literal dalam memori read-only dan melompat bagian kedua dari kalimat yang mengatakan:copies the string to newly allocated memory on the stack
. Jadi, apakah jawaban Anda tidak lengkap karena tidak menentukan bagian kedua?char s[] = "Hellow world";
hanyalah penginisialisasi dan tidak harus disimpan sebagai salinan read-only terpisah sama sekali. Jikas
memiliki durasi penyimpanan statis maka satu-satunya salinan string kemungkinan berada dalam segmen baca-tulis di lokasis
, dan bahkan jika tidak maka kompiler dapat memilih untuk menginisialisasi array dengan instruksi load-direct atau serupa daripada menyalin. dari string hanya baca. Intinya adalah bahwa dalam kasus ini, string initializer itu sendiri tidak memiliki kehadiran runtime.Diberikan deklarasi
mengasumsikan peta memori hipotetis berikut:
String literal
"hello world"
adalah array 12-elemen darichar
(const char
dalam C ++) dengan durasi penyimpanan statis, yang berarti bahwa memori untuk itu dialokasikan ketika program dijalankan dan tetap dialokasikan sampai program berakhir. Mencoba mengubah isi string literal memunculkan perilaku yang tidak terdefinisi.Garis
mendefinisikan
s0
sebagai penunjuk kechar
dengan durasi penyimpanan otomatis (artinya variabels0
hanya ada untuk lingkup yang dideklarasikan) dan menyalin alamat string literal (0x00008000
dalam contoh ini) ke sana. Perhatikan bahwa sejaks0
menunjuk ke string literal, itu tidak boleh digunakan sebagai argumen untuk fungsi apa pun yang akan mencoba memodifikasinya (misalnya,strtok()
,strcat()
,strcpy()
, dll).Garis
mendefinisikan
s1
sebagai array 12-elemen darichar
(panjang diambil dari string literal) dengan durasi penyimpanan otomatis dan menyalin isi literal ke array. Seperti yang dapat Anda lihat dari peta memori, kami memiliki dua salinan string"hello world"
; perbedaannya adalah Anda dapat memodifikasi string yang ada di dalamnyas1
.s0
dans1
dapat dipertukarkan dalam sebagian besar konteks; ini pengecualiannya:Anda bisa menetapkan ulang variabel
s0
untuk menunjuk ke string string yang berbeda atau ke variabel lain. Anda tidak dapat menetapkan kembali variabels1
untuk menunjuk ke array yang berbeda.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[]
kechar *
, 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
.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:
Jika kami melakukan hal yang sama untuk
char[]
:kami memperoleh:
sehingga disimpan di stack (relatif terhadap
%rbp
).sumber
mendeklarasikan
s
menjadi arraychar
yang cukup panjang untuk menampung initializer (5 +1char
s) dan menginisialisasi array dengan menyalin anggota string string yang diberikan ke dalam array.menyatakan
s
sebagai penunjuk ke satu atau lebih (dalam hal ini lebih)char
dan mengarahkannya langsung ke lokasi tetap (hanya baca) yang berisi literal"hello"
.sumber
s
adalah pointer keconst char
.Di sini,
s
adalah array karakter, yang dapat ditimpa jika kita inginkan.String literal digunakan untuk membuat blok karakter ini di suatu tempat di memori yang
s
ditunjuk oleh pointer ini. Di sini kita dapat menetapkan ulang objek yang ditunjuknya dengan mengubah itu, tetapi selama itu menunjuk ke string literal blok karakter yang ditunjukkannya tidak dapat diubah.sumber
Sebagai tambahan, pertimbangkan itu, karena untuk tujuan hanya baca penggunaan keduanya identik, Anda dapat mengakses char dengan mengindeks baik dengan
[]
atau*(<var> + <index>)
format:Dan:
Tentunya, jika Anda berusaha melakukannya
Anda mungkin akan mendapatkan Kesalahan Segmentasi, karena Anda mencoba mengakses memori hanya baca.
sumber
x[1] = 'a';
yang akan segfault juga (tergantung pada platform, tentu saja).Hanya untuk menambahkan: Anda juga mendapatkan nilai berbeda untuk ukurannya.
Seperti disebutkan di atas, untuk sebuah array
'\0'
akan dialokasikan sebagai elemen terakhir.sumber
Set di atas str untuk menunjuk ke nilai literal "Hello" yang dikodekan dalam gambar biner program, yang ditandai sebagai hanya-baca dalam memori, berarti setiap perubahan dalam string literal ini ilegal dan yang akan menyebabkan kesalahan segmentasi.
menyalin string ke memori yang baru dialokasikan di stack. Dengan demikian membuat perubahan apa pun di dalamnya diizinkan dan sah.
akan mengubah str ke "Mello".
Untuk detail lebih lanjut, silakan lihat pertanyaan serupa:
Mengapa saya mendapatkan kesalahan segmentasi saat menulis ke string yang diinisialisasi dengan "char * s" tetapi bukan "char s []"?
sumber
Dalam kasus:
x adalah nilai - itu dapat ditugaskan untuk. Tetapi dalam hal:
x bukan lvalue, ini adalah rvalue - Anda tidak dapat menetapkannya.
sumber
x
adalah nilai yang tidak dapat dimodifikasi. Di hampir semua konteks meskipun, itu akan mengevaluasi untuk pointer ke elemen pertama, dan bahwa nilai adalah nilai p.sumber
Dalam terang komentar di sini harus jelas bahwa: char * s = "hello"; Adalah ide yang buruk, dan harus digunakan dalam lingkup yang sangat sempit.
Ini mungkin merupakan kesempatan yang baik untuk menunjukkan bahwa "kebenaran konst" adalah "hal yang baik". Kapan pun dan di mana pun Anda bisa, gunakan kata kunci "const" untuk melindungi kode Anda, dari penelepon atau programmer yang "santai", yang biasanya paling "santai" ketika pointer mulai digunakan.
Cukup melodrama, di sini adalah apa yang bisa dicapai ketika menghiasi pointer dengan "const". (Catatan: Seseorang harus membaca deklarasi pointer dari kanan ke kiri.) Berikut adalah 3 cara berbeda untuk melindungi diri sendiri ketika bermain dengan pointer:
- yaitu, objek DBJ tidak dapat diubah melalui hal.
- yaitu, Anda dapat mengubah objek DBJ melalui p, tetapi Anda tidak dapat mengubah pointer p itu sendiri.
- yaitu, Anda tidak dapat mengubah pointer p itu sendiri, Anda juga tidak dapat mengubah objek DBJ melalui hal.
Kesalahan terkait dengan upaya mutasi const-semut ditangkap pada waktu kompilasi. Tidak ada ruang runtime atau penalti kecepatan untuk const.
(Asumsi apakah Anda menggunakan kompiler C ++, tentu saja?)
--DBJ
sumber