Исторически (возможно, переписывая его части), это было наоборот. На самых первых компьютерах начала 1970-х годов (возможно, PDP-11 ) с прототипом эмбрионального C (возможно, BCPL ) не было ни MMU, ни защиты памяти (которая существовала на большинстве старых мэйнфреймов IBM / 360 ). Таким образом , каждые байты памяти ( в том числе и обработках буквенных строк или машинный код) могут быть перезаписаны по ошибочной программе (представьте себе программу , изменяя некоторые , %
чтобы /
в Е () 3 строку формата). Следовательно, буквенные строки и константы были доступны для записи.
Будучи подростком в 1975 году, я запрограммировал в парижском музее Пале-де-ла-Декуверт на старые компьютеры эпохи 1960-х годов без защиты памяти: у IBM / 1620 была только базовая память, которую можно было инициализировать с помощью клавиатуры, поэтому вам пришлось набрать несколько десятков из цифр для чтения исходной программы на перфорированных лентах; CAB / 500 имел магнитную память барабана; Вы можете отключить запись некоторых треков через механические переключатели рядом с барабаном.
Позднее компьютеры получили какую-то форму блока управления памятью (MMU) с некоторой защитой памяти. Было устройство, запрещающее процессору перезаписывать какую-то память. Таким образом, некоторые сегменты памяти, особенно сегмент кода (также известный как .text
сегмент), стали доступны только для чтения (кроме операционной системы, которая загрузила их с диска). Для компилятора и компоновщика было естественным помещать литеральные строки в этот сегмент кода, и литеральные строки стали доступны только для чтения. Когда ваша программа пыталась их перезаписать, это было плохо, неопределенное поведение . А наличие в виртуальной памяти сегмента кода, доступного только для чтения, дает значительное преимущество: несколько процессов, выполняющих одну и ту же программу, используют одну и ту же оперативную память ( физическую память).страниц) для этого сегмента кода (см. MAP_SHARED
флаг для mmap (2) в Linux).
Сегодня дешевые микроконтроллеры имеют некоторую постоянную память (например, Flash или ROM) и хранят там свой код (а также буквенные строки и другие константы). А настоящие микропроцессоры (например, в вашем планшете, ноутбуке или настольном компьютере) имеют сложный блок управления памятью и механизм кэширования, используемый для виртуальной памяти и подкачки . Таким образом, сегмент кода исполняемой программы (например, в ELF ) представляет собой память, отображаемую как доступный только для чтения, разделяемый и исполняемый сегмент (с помощью mmap (2) или execve (2) в Linux; кстати, вы можете дать директивы ldчтобы получить доступный для записи фрагмент кода, если вы действительно этого хотите). Написание или злоупотребление это, как правило, ошибка сегментации .
Таким образом, стандарт C является барочным: юридически (только по историческим причинам) буквальные строки не являются const char[]
массивами, а являются только char[]
массивами, которые запрещено перезаписывать.
Кстати, немногие современные языки позволяют перезаписывать строковые литералы (даже Ocaml, который исторически - и плохо - имел доступные для записи строковые литералы, изменил это поведение недавно в 4.02 и теперь имеет строки только для чтения).
Текущие компиляторы C способны оптимизировать, иметь "ions"
и "expressions"
делиться своими последними 5 байтами (включая завершающий нулевой байт).
Попробуйте скомпилировать ваш код C в файл foo.c
с помощью GCCgcc -O -fverbose-asm -S foo.c
и заглянуть внутрь сгенерированного файла ассемблера foo.s
.
Наконец, семантика C достаточно сложна (узнайте больше о CompCert и Frama-C, которые пытаются ее перехватить), и добавление доступных для записи константных литеральных строк сделает ее еще более загадочной, а программы - более слабыми и еще менее защищенными (и с меньшим количеством определенное поведение), поэтому весьма маловероятно, что будущие стандарты Си будут принимать доступные для записи буквенные строки. Возможно, наоборот, они сделают их const char[]
массивами, какими они должны быть морально.
Также обратите внимание, что по многим причинам изменяемые данные труднее обрабатывать на компьютере (когерентность кэша) для кодирования, чтобы понять, для разработчика, чем постоянные данные. Поэтому желательно, чтобы большинство ваших данных (и особенно буквенные строки) оставались неизменными . Узнайте больше о парадигме функционального программирования .
В старые времена Fortran77 в IBM / 7094 ошибка могла даже изменить константу: если вы CALL FOO(1)
и если FOO
случалось, изменили свой аргумент, передаваемый по ссылке 2, реализация могла бы изменить другие вхождения 1 на 2, и это было действительно непослушная ошибка, довольно трудно найти.