Apa keuntungan dari string literal menjadi read-just justify (-ies / -ied):
Namun cara lain untuk menembak diri sendiri di kaki
char *foo = "bar"; foo[0] = 'd'; /* SEGFAULT */
Ketidakmampuan untuk secara elegan menginisialisasi array kata-baca dalam satu baris:
char *foo[] = { "bar", "baz", "running out of traditional placeholder names" }; foo[1][2] = 'n'; /* SEGFAULT */
Rumit bahasanya sendiri.
char *foo = "bar"; char var[] = "baz"; some_func(foo); /* VERY DANGEROUS! */ some_func(var); /* LESS DANGEROUS! */
Menyimpan memori? Saya sudah membaca di suatu tempat (tidak dapat menemukan sumbernya sekarang) sejak dahulu kala, ketika RAM langka, kompiler mencoba mengoptimalkan penggunaan memori dengan menggabungkan string yang serupa.
Misalnya, "lebih" dan "regex" akan menjadi "moregex". Apakah ini masih benar hari ini, di era film berkualitas blu-ray digital? Saya mengerti bahwa embedded system masih beroperasi di lingkungan sumber daya terbatas, tetapi tetap saja, jumlah memori yang tersedia telah meningkat secara dramatis.
Masalah kompatibilitas? Saya berasumsi bahwa program lawas yang akan mencoba mengakses memori read-only akan crash atau melanjutkan dengan bug yang belum ditemukan. Jadi, tidak ada program legacy yang harus mencoba mengakses string literal dan karenanya mengizinkan untuk menulis ke string literal tidak akan membahayakan program legacy portabel yang valid, tidak meretas, dan portabel .
Apakah ada alasan lain? Apakah alasan saya salah? Apakah masuk akal untuk mempertimbangkan perubahan untuk membaca-menulis string literal dalam standar C baru atau setidaknya menambahkan opsi ke kompiler? Apakah ini dianggap sebelumnya atau "masalah" saya terlalu kecil dan tidak signifikan untuk mengganggu siapa pun?
Jawaban:
Secara historis (mungkin dengan menulis ulang bagian dari itu), itu adalah kebalikannya. Pada komputer pertama di awal tahun 1970-an (mungkin PDP-11 ) yang menjalankan prototipe embrionik C (mungkin BCPL ) tidak ada MMU dan tidak ada perlindungan memori (yang ada pada kebanyakan mainframe IBM / 360 yang lebih lama ). Jadi setiap byte memori (termasuk yang menangani string literal atau kode mesin) dapat ditimpa oleh program yang salah (bayangkan sebuah program mengubah beberapa
%
ke/
dalam string format printf (3) ). Oleh karena itu, string dan konstanta literal dapat ditulis.Sebagai seorang remaja pada tahun 1975, saya membuat kode di museum Palais de la Découverte di Paris pada komputer era 1960-an tanpa perlindungan memori: IBM / 1620 hanya memiliki memori inti - yang dapat Anda inisialisasi melalui keyboard, sehingga Anda harus mengetik beberapa lusinan digit untuk membaca program awal pada kaset berlubang; CAB / 500 memiliki memori drum magnetik; Anda dapat menonaktifkan penulisan beberapa trek melalui sakelar mekanis di dekat drum.
Kemudian, komputer mendapatkan beberapa bentuk unit manajemen memori (MMU) dengan beberapa perlindungan memori. Ada perangkat yang melarang CPU untuk menimpa beberapa jenis memori. Jadi beberapa segmen memori, terutama segmen kode (alias
.text
segmen) menjadi hanya-baca (kecuali oleh sistem operasi yang memuatnya dari disk). Itu wajar bagi kompiler dan linker untuk meletakkan string literal dalam segmen kode itu, dan string literal menjadi hanya dibaca. Ketika program Anda mencoba menimpa mereka, itu buruk, perilaku yang tidak terdefinisi . Dan memiliki segmen kode read-only dalam memori virtual memberikan keuntungan yang signifikan: beberapa proses menjalankan program yang sama berbagi RAM yang sama ( memori fisikhalaman) untuk segmen kode itu (lihatMAP_SHARED
flag untuk mmap (2) di Linux).Saat ini, mikrokontroler murah memiliki beberapa memori read-only (mis. Flash atau ROM), dan menyimpan kode mereka (dan string literal dan konstanta lainnya) di sana. Dan mikroprosesor nyata (seperti yang ada di tablet, laptop atau desktop) memiliki unit manajemen memori yang canggih dan mesin cache yang digunakan untuk memori & paging virtual . Jadi segmen kode dari program yang dapat dieksekusi (misalnya dalam ELF ) adalah memori yang dipetakan sebagai segmen read-only, shareable, dan executable (oleh mmap (2) atau execve (2) di Linux; BTW Anda bisa memberikan arahan ke lduntuk mendapatkan segmen kode yang dapat ditulis jika Anda benar-benar ingin). Menulis atau menyalahgunakannya umumnya merupakan kesalahan segmentasi .
Jadi standar C adalah barok: secara hukum (hanya untuk alasan historis), string literal bukan
const char[]
array, tetapi hanyachar[]
array yang dilarang untuk ditimpa.BTW, beberapa bahasa saat ini mengizinkan string literal untuk ditimpa (bahkan Ocaml yang secara historis - dan buruk - memiliki string literal yang dapat ditulis telah mengubah perilaku itu baru-baru ini di 4.02, dan sekarang memiliki string read-only).
Kompiler C saat ini dapat mengoptimalkan dan memiliki
"ions"
dan"expressions"
berbagi 5 byte terakhir (termasuk terminasi null byte).Cobalah untuk mengkompilasi kode C Anda dalam file
foo.c
dengangcc -O -fverbose-asm -S foo.c
dan melihat ke dalam file assembler yang dihasilkanfoo.s
oleh GCCAkhirnya, semantik C cukup kompleks (baca lebih lanjut tentang CompCert & Frama-C yang mencoba menangkapnya) dan menambahkan string literal konstan yang dapat ditulis akan membuatnya lebih misterius saat membuat program lebih lemah dan bahkan kurang aman (dan dengan lebih sedikit perilaku yang didefinisikan), sehingga sangat tidak mungkin bahwa standar C di masa depan akan menerima string literal yang dapat ditulis. Mungkin sebaliknya mereka akan membuat
const char[]
array seperti yang seharusnya secara moral.Perhatikan juga bahwa karena berbagai alasan, data yang dapat berubah lebih sulit ditangani oleh komputer (cache coherency), untuk dikodekan oleh, untuk dipahami oleh pengembang, daripada data konstan. Jadi lebih baik memiliki sebagian besar data Anda (dan terutama string literal) tetap tidak berubah . Baca lebih lanjut tentang paradigma pemrograman fungsional .
Di masa Fortran77 lama di IBM / 7094, bug bahkan dapat mengubah konstanta: jika Anda
CALL FOO(1)
dan jikaFOO
kebetulan memodifikasi argumennya dengan merujuk ke 2, implementasi mungkin telah mengubah kejadian lain dari 1 menjadi 2, dan itu benar-benar bug nakal, cukup sulit ditemukan.sumber
const
standar ( stackoverflow.com/questions/2245664/… )?1
tiba-tiba berperilaku seperti2
dan menyenangkan ...let 2 = 3
berhasil). Ini menghasilkan banyak FUN (dalam definisi kata Dwarf Fortress), tentu saja. Saya tidak tahu bagaimana penerjemah dirancang sehingga memungkinkan ini, tapi itu.Compiler tidak dapat menggabungkan
"more"
dan"regex"
, karena yang pertama memiliki byte nol setelahe
sementara yang terakhir memilikix
, tetapi banyak kompiler akan menggabungkan string literal yang cocok dengan sempurna, dan beberapa juga akan mencocokkan string literal yang memiliki ekor yang sama. Kode yang mengubah string string dengan demikian dapat mengubah string string yang berbeda yang digunakan untuk beberapa tujuan yang sama sekali berbeda tetapi kebetulan mengandung karakter yang sama.Masalah serupa akan muncul dalam FORTRAN sebelum penemuan C. Argumen selalu disampaikan melalui alamat alih-alih berdasarkan nilai. Dengan demikian, rutin untuk menambahkan dua angka sama dengan:
Jika seseorang ingin memberikan nilai konstan (misal 4.0)
sum
, kompiler akan membuat variabel anonim dan menginisialisasinya4.0
. Jika nilai yang sama diteruskan ke beberapa fungsi, kompiler akan meneruskan alamat yang sama ke semuanya. Sebagai konsekuensinya, jika suatu fungsi yang memodifikasi salah satu parameternya melewati konstanta floating-point, nilai konstanta itu di tempat lain dalam program dapat berubah sebagai hasilnya, sehingga mengarah pada pepatah "Variabel tidak akan; konstanta tidak 't ".sumber