2015-09-16 3 views
-2

Дано:Как C защищает память указателей?

int a = 1; 
int *p = &a; 
int b = -1; 
*++p = 2; 

Есть что-нибудь, что мешает Ъ от того, перезаписаны от -1 до 2, если в по-видимому, не маловероятном случае два а и б были написаны рядом друг с другом в памяти?

+9

C не гарантирует защиту памяти. Вот почему это называется * неопределенным поведением *. – Kninnug

+0

Нижняя линия - это segfault, которая защищает * другие * процессы. * «В маловероятном случае два a и b были написаны рядом друг с другом в памяти?» * Вы будете переписывать ** что-то **. –

+1

@WeatherVane: Этот стандарт не требуется. У большинства ЦП нет такой защиты. – Olaf

ответ

5

Предполагая, что вы имели в виду последнюю строку, которая должна быть *++p = 2, нет, ничего не защищает память указателей. Изменение указателя на точку вне объекта, который он (изначально) указывает на, а затем разыменовывает, приводит к неопределенному поведению, и все может произойти.

В коде, как написано, *(a++) не применяет оператор разыменования к типу без указателей, что является нарушением ограничений, поэтому требуется, чтобы произвести время компиляции диагностики и не компилируется ...

+0

Да, увеличение, что я имел в виду. – Bren

1
*++p = 2; 

Сказал, что это недопустимо, и оператор вызывает неопределенное поведение. Компилятору разрешено останавливать перевод с ошибкой, если он обнаруживает его, но нет требования от C, чтобы обнаружить такие ошибки (которые на самом деле действительно трудно обнаружить).

+0

Это была типовая проблема. – Bren

+0

@Bren вы имели в виду '* ++ p', а не' * (p ++) '. – ouah

+0

Спасибо, не понимал, что изменит ситуацию, вернемся к K & R для меня. – Bren

9

Как C защищает память указателей?

Это не так.

Есть что-нибудь, что мешает Ъ от того, перезаписаны от -1 до 2 если в маловероятном случае, если два а и б были написаны рядом друг с другом в памяти?

No.

И две переменные, являющиеся «рядом друг с другом в памяти» не вряд ли вообще.

C дает вам возможность вызвать множество проблем, и вы не будете осторожны.

1

Нет, ничего остановит вас в этом. Арифметика указателя хорошо определена, это означает, что вы можете переместить указатель в любом направлении, чтобы указать на другое место. Но с этой настройкой вы не можете быть уверены, какую память вы на самом деле изменяете. Потому что во время компиляции оптимизаторы могут перемещать вашу переменную в другое место.

Как и в вашем случае, если все обтекаемое и прямое. Ваш порядок объявления переменных - это a, p и b. Предположим, что (a и b) получает x и p получает y байтов памяти в зависимости от вашей машинной архитектуры и ОС.

Ваша память должна выглядеть следующим образом:

________ __________________ 
| (a) x | (p) y | (b) x | 
|________|_________|________| 

, что означает, что ++ р будет оценивать р + SizeOf (р). Если «p» указывает на начало «a», это означает, что увеличение его на (y) байтов оставило бы «p» в области памяти «y» (при условии, что y> = x) или в «x» в противном случае. Таким образом, вы фактически модифицируете память p, а не b.

P.S: Мощность, предоставляемая C, должна быть лучше всего оставлена ​​опытными программистами на С.

+0

«Арифметика указателя хорошо определена» - нет. Если арифметика приводит к указателю, который находится за пределами (+1) объекта, на который указывает исходный указатель ... результирующее значение не определено. –

0

Если вы поместите a, b переменные в отдельные страницы (например, с помощью mmap), то mprotect вызова может быть использован для установки PROT_READ защиты на странице, которая содержит переменную b.

0

В вашем примере b может быть переписан.

Для меня, используя GDB Я понимаю, что обе переменные размещаются друг за другом в памяти:

(gdb) x &a 
0xffffcb80: 0x00000001 
(gdb) x &b 
0xffffcb84: 0x00000002 

Однако это не гарантируется.

С приращением в int * увеличивает свой адрес, sizeof(int) мы достигаем следующий int который для меня содержит b уже.

Код ...

int a = 1; 
int *p = &a; 
int b = -1; 
*++p = 2; 
printf("b: %i\n", b); 

поэтому печатает:

b: 2 

C не имеет никакой защиты памяти для своей собственной памяти процесса. Вы можете написать такой код, но вы можете столкнуться с проблемой: как только вы увеличиваете свой int *, вы вызываете undefined behavior, потому что вы оставляете фактический размер переменной. Вы никогда не должны делать предположения о таких местах переменных, как это, потому что это может быть недействительным, так что все может случиться, и вам не понравится нестабильность, которую он приносит.

Смежные вопросы