Как память, доступная только для чтения, реализована в C?

Я слышал, что в C, если я делаю

char *s = "hello world". 

"мир привет" фактически хранится в постоянной памяти.

Я не очень понимаю о постоянной памяти. Какое объяснение? Это как флаг для компилятора, который сообщает компилятору не записывать в этот раздел?

Ответы

Ответ 1

Это не особенность языка C, а особенность компилятора/компоновщика и рабочей системы, работающей вместе.

При компиляции такого кода происходит следующее:

  • Компилятор помещает строку в раздел данных только для чтения.

  • Линкеров собирает все данные в таких разделах только для чтения и помещает их в один сегмент. Этот сегмент находится в исполняемом файле и помечен атрибутом "только для чтения".

  • Теперь появляется исполняемый загрузчик операционной системы. Он загружает исполняемый файл (или более точно отображает его в памяти). Как только это будет сделано, загрузчик будет проходить секции и устанавливать разрешения доступа для каждого сегмента. Для сегмента данных только для чтения он скорее всего отключит выполнение кода и запись. Код (например, ваши функции) получает права выполнения, но не имеет права на запись. Обычные данные, такие как статические переменные, получают доступ для чтения и записи и т.д....

Теперь это делают современные операционные системы.

Как сказано, это не особенность языка C. Если вы скомпилируете ту же проблему для DOS, например, программа будет работать, но защита записи не будет возможна, потому что DOS-загрузчик не знает о разделах только для чтения.

Ответ 2

Исполняемые файлы содержат две части: раздел .data, содержащий глобальные переменные и секцию .text, содержащие фактический машинный код.

Строки помещаются в раздел .data. Что делает C, когда он видит "Hello world" , он помещает строку "Hello world" в сам исполняемый файл и заменяет экземпляр "Hello world" в программе адресом, где эта строка заканчивается загрузкой.

Сказав это, я не уверен, почему он доступен только для чтения - теоретически программа должна иметь возможность изменять свою собственную память.

Ответ 3

Истинная постоянная память реализуется подсистемой памяти ОС. ОС может отмечать определенные страницы как доступные только для чтения.

В двоичном выражении компилятор может указать ОС, какие части исполняемого файла должны быть помещены в страницы с записью только для чтения и чтения-записи.

Ответ 4

Пример того, как это сделать в Linux, находится на стр. 179 Advanced Linux Programming от Марка Митчелла, Джеффри Олхама и Алекса Самуила.

Ответ 5

Как отмечают другие люди, независимо от того, хранится ли содержимое константных строк в постоянной памяти, определяется операционной системой, компилятором и архитектурой чипов.

Более точно, стандарт C указывает, что цитируемые строки считаются имеющими тип "const char []" (или слова на этот счет, у меня нет стандарта под рукой).

Любой код, который пытается изменить содержимое такой строки, вызывает поведение undefined. Это означает, что буквально все может произойти в этот момент, и провайдер компилятора даже не обязан документировать, что может случиться.

На практике это означает, что программа C или С++, которая хочет быть переносной, должна избегать изменения постоянных строк.

В общем случае компилятор не позволит вам изменять содержимое переменных "const", поэтому вы можете считать, что "const" означает "только чтение" в большинстве случаев. К сожалению, существует специальное исключение для char * и const char *, в основном по историческим причинам. Это означает, что такой код выглядит следующим образом:

char *x = "Hello, World";
*x = 'h';

будет компилироваться без ошибок или предупреждений, даже если он вызывает поведение undefined.

Ответ 6

Вы можете попробовать что-то вроде

s[4] = '0';

и посмотрите, говорит ли он "привет w0rld", когда вы вызываете

puts(s);

Если это вызывает немедленное исключение сегментации или исключение предотвращения выполнения данных, то оно, вероятно, только для чтения. (Если система позволяет вам уйти от нее, это не делает ее хорошей идеей.)

Ответ 7

Когда вы пишете char s[10]="sneha"; вы выделяете 10 байтов пространства для хранения (а не память, память входит в изображение только тогда, когда вы выполняете свою программу) в своем объектном файле. Это статическое распределение памяти (во время компиляции).

Но когда вы пишете char *s="sneha";, вы не выделяете место для хранения "sneha". Он будет храниться в разделе READ ONLY. Но указатель s хранится в другом разделе в зависимости от того, где он объявлен. Но это указывает на ТОЛЬКО ДАННЫЕ ЧТЕНИЯ "sneha". Поэтому, если вы попытаетесь написать на нем, вы получите ошибку сегментации.

Например:

char *s[10]="sneha";
s[1]="N"; 
printf("%s",s);  // you expecting output sNeha, 
                 // but you get a seg fault since it is ONLY DATA