Могу ли я когда-нибудь получить доступ к нулевому адресу? - PullRequest
52 голосов
/ 04 мая 2010

Константа 0 используется как нулевой указатель в C и C ++. Но, как и в вопросе «Указатель на конкретный фиксированный адрес » , представляется возможным использовать присвоение фиксированных адресов. Есть ли когда-либо мыслимая потребность в любой системе для любой задачи низкого уровня для доступа к адресу 0?

Если есть, то как это решить, если 0 - нулевой указатель и все?

Если нет, то почему он так не нужен?

Ответы [ 17 ]

1 голос
/ 04 мая 2010

Все зависит от того, имеет ли машина виртуальную память. Системы с ним, как правило, помещают туда неописуемую страницу, что, вероятно, является привычным поведением. Однако в системах без него (обычно это микроконтроллеры в наши дни, но раньше они были гораздо более распространены), в этой области часто бывают очень интересные вещи, такие как таблица прерываний. Я помню хакинг с этими вещами еще во времена 8-битных систем; весело, а не слишком большая боль, когда вам пришлось резко перезагрузить систему и начать все сначала. : -)

0 голосов
/ 12 августа 2013

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

Код, приведенный ниже, даже не компилируется, так как NULL не определен:

int main(int argc, char *argv[])
{
    void *p = NULL;
    return 0;
}

OTOH, код нижекомпилируется, и вы можете читать и записывать адрес 0, если аппаратное обеспечение / ОС позволяет:

int main(int argc, char *argv[])
{
    int *p = 0;
    *p = 42;
    int x = *p; /* let's assume C99 */
}

Обратите внимание, я не включил ничего в приведенные выше примеры.Если мы начнем включать вещи из стандартной библиотеки C, NULL станет магически определенным.Насколько я помню, это взято из string.h.

NULL все еще не является базовой функцией C, это КОНВЕНЦИЯ многих функций библиотеки C, указывающих на недействительность указателей.Библиотека C на данной платформе определит NULL для области памяти, которая в любом случае недоступна.Давайте попробуем это на ПК с Linux:

#include <stdio.h>
int main(int argc, char *argv[])
{
        int *p = NULL;
        printf("NULL is address %p\n", p);
        printf("Contents of address NULL is %d\n", *p);
        return 0;
}

Результат:

NULL is address 0x0
Segmentation fault (core dumped)

Таким образом, наша библиотека C определяет NULL для адреса ноль, который оказывается недоступным.Но это был не компилятор C, даже не функция C-библиотеки printf(), которая специально обрабатывала нулевой адрес.Все они счастливо пытались работать с ним нормально.Именно ОС обнаружила ошибку сегментации, когда printf попытался прочитать с нулевого адреса.

0 голосов
/ 04 мая 2010

Помните, что во всех обычных случаях вы не видите конкретных адресов. Когда вы выделяете память, ОС предоставляет вам адрес этого блока памяти.

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

Таким образом, доступ к нулевому адресу на самом деле не является проблемой, потому что, когда вы следуете указателю, вам все равно, на какой адрес он указывает, только то, что он действителен:

int* i = new int(); // suppose this returns a pointer to address zero
*i = 42; // now we're accessing address zero, writing the value 42 to it

Итак, , если вам нужен нулевой адрес, он, как правило, будет работать нормально.

Значение 0 == null действительно становится проблемой, только если по какой-то причине вы обращаетесь к физической памяти напрямую. Возможно, вы пишете ядро ​​ОС или что-то в этом роде самостоятельно. В этом случае вы будете выполнять запись по определенным адресам памяти (особенно по тем, которые сопоставлены с аппаратными регистрами), и, таким образом, вам, вероятно, может потребоваться запись по нулевому адресу. Но тогда вы действительно обходите C ++ и полагаетесь на специфику вашего компилятора и аппаратной платформы.

Конечно, если вам нужно написать по адресу ноль, это возможно. Только константа 0 представляет нулевой указатель. Непостоянное целочисленное значение, равное нулю, не будет, если назначено указателю, дать нулевой указатель.

Так что вы можете просто сделать что-то вроде этого:

int i = 0;
int* zeroaddr = (int*)i;

теперь zeroaddr будет указывать на адрес ноль (*), но, строго говоря, он не будет нулевым указателем, поскольку нулевое значение не было постоянным.

(*): это не полностью правда. Стандарт C ++ гарантирует только «определяемое реализацией отображение» между целыми числами и адресами. Он может преобразовать 0 в адрес 0x1633de20` или любой другой адрес, который ему нравится. Но сопоставление обычно является интуитивно понятным и очевидным, где целое число 0 сопоставляется с нулевым адресом)

0 голосов
/ 04 мая 2010

Запись по нулевому адресу может быть выполнена, но это зависит от нескольких факторов, таких как ваша ОС, целевая архитектура и конфигурация MMU. На самом деле, это может быть полезным инструментом отладки (но не всегда).

Например, несколько лет назад во время работы над встроенной системой (с несколькими доступными инструментами отладки) у нас была проблема, которая приводила к перезагрузке. Чтобы помочь найти проблему, мы отлаживали, используя sprintf (NULL, ...); и последовательный кабель 9600 бод. Как я уже сказал, доступно несколько инструментов для отладки. С нашей настройкой мы знали, что горячая перезагрузка не повредит первые 256 байтов памяти. Таким образом, после горячей перезагрузки мы можем приостановить загрузчик и сбросить содержимое памяти, чтобы узнать, что произошло до перезагрузки.

0 голосов
/ 04 мая 2010

Если я правильно помню, в микроконтроллере AVR файл регистра отображается в адресном пространстве RAM , а регистр R0 находится по адресу 0x00. Это было сделано специально, и, по-видимому, Atmel считает, что существуют ситуации, когда удобнее обращаться к адресу 0x00 вместо явной записи R0.

В памяти программ по адресу 0x0000 имеется вектор прерывания сброса, и снова этот адрес явно предназначен для доступа при программировании микросхемы.

0 голосов
/ 04 мая 2010

Время от времени я использовал нагрузки с нулевого адреса (на известной платформе, где это гарантированно могло бы вызвать segfault), чтобы преднамеренно завершать работу с символически информативным символом в коде библиотеки, если пользователь нарушает какое-то необходимое условие, и ничего хорошего способ бросить исключение, доступное для меня. «Segfault at someFunction$xWasnt16ByteAligned» - довольно эффективное сообщение об ошибке, чтобы предупредить кого-то о том, что он сделал неправильно и как это исправить. Тем не менее, я бы не советовал заводить подобные вещи.

0 голосов
/ 04 мая 2010

C / C ++ не позволяет писать по любому адресу. Именно ОС может выдавать сигнал, когда пользователь получает доступ к какому-либо запрещенному адресу. C и C ++ гарантируют, что любая память, полученная из кучи, будет отличаться от 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...