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 (lihat MAP_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 hanya char[]
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
dengan gcc -O -fverbose-asm -S foo.c
dan melihat ke dalam file assembler yang dihasilkan foo.s
oleh GCC
Akhirnya, 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 jika FOO
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.