Что такое «ошибка присвоения нулевого указателя»? - PullRequest
12 голосов
/ 04 мая 2010

Один из вопросов собеседования по указателям C здесь следующий: что такое ошибка назначения нулевого указателя?

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

Ответы [ 5 ]

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

http://www.faqs.org/qa/qa-3786.html

Назначение указателя NULL является ошибкой во время выполнения. Это происходит по разным причинам, одна из которых состоит в том, что ваша программа пыталась получить доступ к недопустимому месту в памяти.Неверное местоположение означает, что это местоположение находится либо в адресном пространстве операционных систем, либо в пространстве памяти других процессов.В stdio.h значение NULL определяется как 0. Поэтому, когда ваша программа пытается получить доступ к 0-му расположению, операционная система убивает вашу программу из-за ошибки назначения во время выполнения, поскольку 0-е расположение находится в адресном пространстве операционной системы, а операционная система не разрешает доступ к ее адресу.пробел в пользовательской программе.

Пример кода:

int* ptr = NULL;  
*ptr = 3;

Объяснение:
Почти на каждой системе адрес 0зарезервированоСистема не позволит вам писать в это место.Если вы попытаетесь, вы получите исключение времени выполнения (нарушение прав доступа, ошибка сегментации и т. Д.).

2 голосов
/ 24 июня 2013

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

Давайте рассмотрим случай компилятора TC, этот компиляторпомещает четыре нулевых байта внизу сегмента данных и уведомление об авторских правах TC.TC также использует местоположение DS: 0000, нижнюю часть сегмента данных в качестве расположения нулевых указателей.Таким образом, присвоение значения этому нулевому указателю фактически изменило бы четыре байта и, возможно, испортило бы уведомление об авторском праве.

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

Итак, я думаю, что это не просто нулевой указатель, а любой указатель, который становится диким, если вы пытаетесь получить доступ к некоторым ключевым областям, вас приветствует NullОшибка назначения указателя.

1 голос
/ 22 сентября 2014

Это ошибка времени выполнения, возникающая при попытке указать недопустимое пространство памяти, обычно адрес 0, зарезервированный для ОС.

1 голос
/ 19 января 2012

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

void CopyMessage(char *p)
{
    strcpy(p, "welcome");
}

void main()
{
   char *src;
   CopyMessage(src);
}
0 голосов
/ 02 сентября 2016

2018-10-15: переписать

Мое намерение в этом ответе дополняет самые основные понятия нулевого указателя. Это простейшее определение, которое перечислено во многих местах, - это когда каждому базовому значению, указывающему на адрес, присваивается значение «0» или значение NULL, поскольку нулевая страница, 0-я ячейка памяти являются частью адресного пространства операционной системы, а операционная система не позволяет доступ к его адресному пространству по программе пользователя. В таких случаях прекомпилятор или компилятор может генерировать ошибку, или ошибка может генерироваться самой операционной системой во время выполнения как нарушение доступа к памяти.

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

Большинство языков высокого уровня и компиляторов могут препятствовать этому при соответствующем приведении типа, указании базы параметров и отсутствии умственных просчетов при индексации. «C» как язык, без указания самых строгих параметров компилятора, особенно подвержен этим типам ошибок, а также менее сложным машинным компиляторам или языкам программирования, которые сегодня встречаются в «карманных» процессорах.

Однако с момента появления компьютеров и языков программирования концепция ошибки нулевого указателя расширилась и теперь включает любой указатель, который указывает на 0-е местоположение в любой защищенной ячейке памяти. Но особенно в контексте того, как область памяти, которая может быть использована для указания на любую область памяти, может быть непреднамеренно перезаписана, чтобы содержать нулевое значение. И здесь я рассматриваю эту концепцию из-за того, что я назвал «ошибкой 1», которая возникает, когда программистам приходится переключаться между опционной базой «0» или опционной базой «1». Это проблема подсчета, когда мы начинаем наш подсчет с «0» или «1», как в:

Option Base 0
[0,1,2,..,9] or 

Option Base 1 
[1,2,3,...,10]

для массива из 10 элементов. Ошибка 1 может привести к неправильному расчету, в результате чего указатель на первую ячейку памяти «до» массива

Option Base 1 
0[1,2,3,...,10]
^
|Last memory location of another variable space 'before' this variable space

или первая позиция «после» массива, который по определению выходит за пределы.

Option Base 0 
[0,1,2,...,9]10
             ^
             |First memory location of another variable after this variable

Но, когда речь идет о программировании, которое использует прямой доступ к памяти, как в исходном машинном коде любого языка, ошибка 1 может быть трагической, помещая непреднамеренное значение в область памяти за пределами предполагаемого диапазона, который в случае переменной space и использование указателей - это переменная space до или после намеченной переменной, которая при инициализации или очистке массива создает «ноль» или 0 в нежелательном месте, и особенно, если это массив указателей, ошибка нулевого указателя в непредусмотренном переменная. Это, конечно, зависит от структуры пространства переменных и / или типа. Это может быть особенно проблематично, если переменная или другое адресное пространство хранилища вложено в код. Как я заявлял ранее, многие компиляторы языка высокого уровня могут обойти большую часть ошибок такого типа; но при написании определенных подпрограмм в машинном коде по любой причине, которая будет сочтена необходимой, необходимо проявить особую осторожность, чтобы убедиться, что база опций явно определена и соблюдается практикой, если не соглашением компилятора.

Во-первых, программисты признают необходимость того, чтобы и программа, и области хранения были четко определены и чтобы ничто без явного согласия не могло изменить даже один бит данных. Это очень важно в отношении нулевого указателя, поскольку 0-я ячейка памяти в области нулевой страницы операционной системы часто используется для хранения стека, который представляет собой ячейки памяти, помещенные в стек для операции возврата. Независимо от того, выдвигает ли системный вызов адрес для операции возврата (выводит адрес возврата с того места, где была прервана система) из-за прерываний, способных маскировать или не маскировать, или потому, что программист хочет выдвинуть данные или область памяти чтобы позже выскочить из этого стека. Это охраняемая территория. Как и любой указатель на действительный адрес памяти, не нужно писать в неправильное место, 0-е место особенно восприимчиво, если оно перезаписано, потому что переменные часто равны нулю или имеют значение 0 из начального состояния включения и, следовательно, переменную который не был явно определен после включения питания или был преднамеренно инициализирован, вероятно, будет равен нулю.

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

Как я уже говорил, это описание выходит за рамки общепринятого определения ошибки нулевого указателя, но, тем не менее, оно может давать нулевой указатель, хотя чаще приводит к другим ошибкам или вообще ни к чему. Он часто не дает никаких указаний на свое существование, кроме «если или когда» программа не работает должным образом.

Здесь я приведу дополнительные нетрадиционные примеры и определения потенциальных источников ошибок присваивания нулевого указателя, вместо определения традиционного понимания, которое является скорее ошибкой в ​​соглашении по программированию, чем ошибкой в ​​логике программирования.

Этот тип ошибки (неопределенный или нулевой) встречается гораздо реже. Но современное программирование «карманных» процессоров с использованием настольных устройств, таких как Arduino, Raspberry PI, AMD или любого другого компьютера на чиповом программировании, существует во множестве форм, многие из которых сегодня так же просты, как и прошлые годы, этот нулевой указатель Проблема все еще существует сегодня и может возникнуть даже в самых сложных системах. Кроме того, компании, которые создают свои собственные переменные или структуры данных, вероятно, также являются наиболее вероятными людьми, которые сейчас видят эту ошибку типа. Намерение - показать примеры, которые могут помочь в распознавании.

Как было определено в прежние времена, вскоре было признано, что условия, которые приводят к ошибкам нулевого указателя, могут также привести к ошибкам, когда значение указателя было непреднамеренно изменено. Затем, в качестве переменной, которая используется в качестве указателя и была перезаписана без намерения или знания программиста, которая может быть нулевой, но также может иметь любое другое значение. Итак, мы обнаружили, что проблема, которая может создать нулевой указатель, также может создать ненулевой указатель. Пустой указатель - это особый случай, при котором OFTEN создает систематическое сообщение об ошибке; но когда те же условия заставляют указатель принимать случайное или неопределенное значение вместо исходного адреса, в котором должны находиться данные, он теперь содержит нулевой или неизвестный адрес, что приводит к перемещению или сохранению данных в недопустимом или нежелательном месте, потенциально перезаписать и повредить этот код или данные.

Большинство будет справедливо утверждать, что это равно НЕ ошибка нулевого указателя; и они полностью 100% ПРАВИЛЬНО ! Однако корни этой ошибки, как правило, приводят к странным, часто встречающимся ошибкам нулевого указателя, потому что чаще указатели будут содержать «ноль»! Цель этого упражнения в определении состоит в том, чтобы указать, как создание нулевых указателей может также привести к проблеме, которая, по-видимому, не указывает на исходный источник этой проблемы. Итак, в концепции нет указателя на проблему. Из-за связи с созданием нечетных проблем с нулевым указателем, и в этом случае последующее отсутствие данных, указывающих на источник ошибки, потому что указатель был NOT null и вместо этого был 'undefined' старые таймеры, которые перешли от нисходящего программирования к объектно-ориентированному программированию, управляемому событиями, распознают эту взаимосвязь и распознают этот тип «нулевой» ошибки указания, которая, по-видимому, не имеет определяемого источника.

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

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

В более простой архитектуре или средах программирования. Он может ссылаться на любой код, который непреднамеренно заканчивает тем, что создает нулевые значения как указатели, или создает ошибку, которая в любом случае останавливает выполнение, например, перезапись байта в стеке возврата, перезапись кода, код, который случайно сохраняет «0» в неверном месте, в существующем коде или просто как данные в неправильном месте, а не просто как адрес.

Итак, хотя приведенные выше примеры отлично работают, чтобы определить пример нулевого указателя. Таким образом, мы расширяем концепцию: нулевой указатель - это любой указатель, который используется в качестве указателя переменной, а расположение адреса этой переменной по любой из множества причин теперь содержит «нулевое» или ANY непреднамеренное значение, которое заставляет его указывать на нежелательную область памяти независимо от того, как она туда попала, а не только на ошибки в логике программирования или математические ошибки вычислений. IOW, а не просто 0 в указателе; более конкретно, нулевое или неопределенное значение в любой ячейке памяти, где , чтобы ячейка памяти не была конкретной целью и при других обстоятельствах имела ДРУГУЮ цель, для которой она теперь будет выполняться!

Итак, наконец, можно получить ошибку нулевого указателя, и при исследовании указатель обнаруживает, что он содержит ноль; но не может найти код, который поместил значение NULL в указатель или присвоил его. Это самое широкое определение ошибки присваивания нулевого указателя, и это абсолютно худший вариант ошибки нулевого указателя. Когда это происходит в большой программе, это часто приводит к смерти программы, потому что, если ошибка существовала в предыдущих версиях, но выполняла запись в непреднамеренные области памяти (которые позволяли программе функционировать или IOW), которая была ранее доступна, но не была выделена в В более ранних версиях ошибка оставалась незамеченной, пока программа не расширилась, и теперь, когда ранее неиспользуемая область памяти содержала новый код ИЛИ данные, которые позволяют старой ошибке генерировать случайные ошибки в новом коде или повреждать данные!

Например: в более ранней версии неверное значение адреса вызывает запись данных за пределы определенных переменных пространств, но остается незамеченным для нескольких версий, потому что данные записываются и читаются, а также программа и все остальное «появляется» ОК! Но по мере расширения программы новый код теперь существует относительно в том же относительном адресном пространстве, что и память, в которой исходная старая ошибка неправильно записывала в неправильную область памяти, и никто не заметил, будь то один байт или целый блок данных. ! Но теперь там существует новый код. И когда программа запускает этот конкретный код, сегодня или завтра, как называется любая функция, которая его содержит, новые данные повреждаются старой необнаруженной ошибкой.

Найти «оригинальную» ошибку, существовавшую годом ранее, сейчас почти невозможно, если не полностью, найти.

Логика администратора и разработчика обычно диктует, зачем мне искать, мы знаем, что код работал и работал отлично для последних нескольких версий. Но, теперь, часть нового кода не работает, основные части сломаны. Мы смотрим и смотрим, и никто ничего не находит. Как будто ошибки не существует, и все же она существует. Что вызывает это, кто подозревает код, написанный годами ранее? Нулевые указатели и многие другие ошибки также вызваны этим. С пониманием и хорошим редактором, который может непосредственно проверять код, соответствующие мониторы следят за измененными областями памяти, которые вызывают остановку, чтобы определить, какой код выполняется в нужное время, даже это можно найти.

Возможно, этот ответ неуместен и будет полезнее, если его перечислить в другом месте. Модератор? Возможно, это вызывает обеспокоенность, потому что этот «ответ» не считается уместным, потому что есть некоторая озабоченность по поводу использования в вопросе слова «назначение», означающего намеренно назначенное. Однако концепция уступки не требует знаний или согласия. Алгоритм или код может назначать значения. Нулевые указатели могут быть созданы многими способами, пример здесь, вероятно, является наиболее распространенным за пределами традиционного понимания того, что вызывает ошибку, которая, кажется, указывает на никуда. Я лично видел, как такие ошибки убивают целые строки продукта. В конце концов грохот толпы пользователей расстроился из-за продолжающегося существования нефиксированных ошибок в их любимом продукте; старый продукт утилизируется, а новый занимает его место ... созданный с нуля.

Похоже, эта информация имеет место "а" и включена в качестве ответа. Приветствуются положительные, полезные отзывы или предложения, а не простое отрицательное голосование. Если кому-то захочется это прокомментировать, возможно, стоит провести мета-обсуждение?

Приветствие.

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