Trong lịch sử (có lẽ bằng cách viết lại các phần của nó), nó đã trái ngược. Trên các máy tính đầu tiên của đầu những năm 1970 (có lẽ PDP-11 ) chạy phôi nguyên mẫu C (có lẽ BCPL ) không có MMU và không có bảo vệ bộ nhớ (tồn tại trên hầu hết các máy tính lớn của IBM / 360 ). Vì vậy, mỗi byte của bộ nhớ (kể cả xử lý các chuỗi chữ hoặc mã máy) có thể bị ghi đè bởi một chương trình có sai sót (tưởng tượng một chương trình thay đổi một số %
để /
trong một printf (3) định dạng chuỗi). Do đó, chuỗi và hằng số theo nghĩa đen là có thể ghi.
Khi còn là một thiếu niên vào năm 1975, tôi đã mã hóa trong bảo tàng Palais de la Découverte ở Paris trên các máy tính cũ của những năm 1960 mà không có bộ nhớ bảo vệ: IBM / 1620 chỉ có một bộ nhớ cốt lõi - bạn có thể khởi tạo bàn phím thông thường, vì vậy bạn phải gõ vài chục các chữ số để đọc chương trình ban đầu trên băng đục lỗ; CAB / 500 có bộ nhớ trống từ tính; bạn có thể vô hiệu hóa việc viết một số bản nhạc thông qua các công tắc cơ học gần trống.
Sau đó, máy tính có một số dạng đơn vị quản lý bộ nhớ (MMU) với một số bảo vệ bộ nhớ. Có một thiết bị cấm CPU ghi đè lên một số loại bộ nhớ. Vì vậy, một số phân đoạn bộ nhớ, đáng chú ý là phân đoạn mã (còn gọi là .text
phân đoạn) trở thành chỉ đọc (ngoại trừ bởi hệ điều hành đã tải chúng từ đĩa). Việc trình biên dịch và trình liên kết đặt các chuỗi ký tự trong phân đoạn mã đó là điều tự nhiên và các chuỗi ký tự chỉ trở thành đọc. Khi chương trình của bạn cố ghi đè lên chúng, đó là một hành vi không xác định . Và việc có một đoạn mã chỉ đọc trong bộ nhớ ảo mang lại một lợi thế đáng kể: một số quy trình chạy cùng một chương trình có chung RAM ( bộ nhớ vật lýtrang) cho đoạn mã đó (xem MAP_SHARED
cờ cho mmap (2) trên Linux).
Ngày nay, các bộ vi điều khiển giá rẻ có một số bộ nhớ chỉ đọc (ví dụ Flash hoặc ROM) và giữ mã của chúng (và các chuỗi ký tự và các hằng số khác) ở đó. Và các bộ vi xử lý thực (như cái trong máy tính bảng, máy tính xách tay hoặc máy tính để bàn của bạn) có một bộ quản lý bộ nhớ tinh vi và bộ máy bộ đệm được sử dụng cho bộ nhớ ảo & phân trang . Vì vậy, đoạn mã của chương trình thực thi (ví dụ trong ELF ) là bộ nhớ được ánh xạ dưới dạng phân đoạn chỉ đọc, có thể chia sẻ và có thể thực thi (bằng mmap (2) hoặc thực thi (2) trên Linux; BTW bạn có thể đưa ra chỉ thị cho ldđể có được một đoạn mã có thể ghi nếu bạn thực sự muốn). Viết hoặc lạm dụng nó thường là một lỗi phân khúc .
Vì vậy, tiêu chuẩn C là baroque: về mặt pháp lý (chỉ vì lý do lịch sử), chuỗi ký tự không phải là const char[]
mảng, mà chỉ là char[]
các mảng bị cấm ghi đè.
BTW, một số ngôn ngữ hiện tại cho phép ghi đè chuỗi ký tự (ngay cả Ocaml, trong lịch sử - và rất tệ - có chuỗi ký tự có thể ghi đã thay đổi hành vi gần đây trong 4.02 và hiện có chuỗi chỉ đọc).
Trình biên dịch C hiện tại có thể tối ưu hóa và có "ions"
và "expressions"
chia sẻ 5 byte cuối cùng của chúng (bao gồm cả byte null kết thúc).
Cố gắng biên dịch mã C của bạn trong tập tin foo.c
với gcc -O -fverbose-asm -S foo.c
và cái nhìn bên trong file lắp ráp tạo ra foo.s
bởi GCC
Cuối cùng, ngữ nghĩa của C đủ phức tạp (đọc thêm về CompCert & Frama-C đang cố gắng nắm bắt nó) và thêm các chuỗi ký tự không đổi có thể ghi sẽ làm cho nó thậm chí còn phức tạp hơn trong khi làm cho các chương trình yếu hơn và thậm chí kém an toàn hơn (và ít hơn hành vi được xác định), do đó, rất khó có khả năng các tiêu chuẩn C trong tương lai sẽ chấp nhận các chuỗi chữ có thể ghi. Có lẽ trái lại họ sẽ làm cho họ const char[]
mảng như họ nên về mặt đạo đức.
Cũng lưu ý rằng vì nhiều lý do, dữ liệu có thể thay đổi khó xử lý hơn bởi máy tính (tính liên kết bộ đệm), để mã cho, để nhà phát triển hiểu, hơn là dữ liệu không đổi. Vì vậy, tốt nhất là có hầu hết dữ liệu của bạn (và đáng chú ý là chuỗi ký tự) không thay đổi . Tìm hiểu thêm về mô hình lập trình chức năng .
Trong những ngày Fortran77 cũ trên IBM / 7094, một lỗi thậm chí có thể thay đổi hằng số: nếu bạn CALL FOO(1)
và nếu FOO
tình cờ sửa đổi đối số của nó được chuyển qua tham chiếu thành 2, thì việc triển khai có thể đã thay đổi các lần xuất hiện khác thành 1 thành 2 và đó thực sự là một sự cố lỗi nghịch ngợm, khá khó tìm.