Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> NULL 
:(
    Опции темы
GQU
Дата 23.8.2014, 10:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 13.2.2011

Репутация: нет
Всего: нет



Почему NULL сделали как ((void *)0), а не просто 0 ??
PM MAIL   Вверх
feodorv
Дата 23.8.2014, 23:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(GQU @  23.8.2014,  11:25 Найти цитируемый пост)
Почему NULL сделали как ((void *)0), а не просто 0 ?? 

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

Так в чём же проблема с передачей нулевого указателя в функцию с переменным числом аргументов?

Допустим, мы имеем функцию с переменным числом аргументов:
Код
void func( ... );


Чем же нехорош вызов этой функции в виде
Код
func( "aaa", "bbb", 0);
по сравнению с
Код
func( "aaa", "bbb", NULL);


В первом случае 0 трактуется как "целое число", во втором - как "указатель". Если размеры (sizeof) целого числа и указателя различаются (а в эпоху 16-битных процессоров это было повальной практикой), то в стек вызова func поступает разное количество байт: в случае "0" - 2 нулевых байта, в случае "(void *) 0" - честных 4 нулевых байта для 16-битной системы. И получается, что в первом случае в стек не докладывается 2 нужных нулевых байта, которые func всё равно изымает из стека, но которые будут мусорными и содержать с огромной вероятностью отнюдь не нули, что приведет к появлению в процессе выполнения func случайного ненулевого указателя, что в свою очередь приведет, скорее всего, к краху программы (это не говоря даже о неправильном размере сдвига стекового указателя на 2 байта в первом случае в противовес 4-м байтам во втором).

Для 32-битных систем, для которых sizeof(int) равно sizeof(void *) (не знаю ни одной 32-х битной ОС, для которой это не так, но допускаю, что такие могут существовать), такой проблемы не существует.

Однако, казалось бы, проблема возрождается вновь с появлением 64-битных систем, для которых sizeof(int) есть 4, а sizeof(void *) есть 8. Но принимая во внимание заодно задачу корректного выравнивания данных в 64-битных системах в стек вызова подпрограммы помещается 8 байт для любого аргумента (если его sizeof, конечно, меньше или равен 8 байтам), поэтому для func 0 и (void *) 0 будут выглядеть одинаково. 

Впрочем, хотя NULL, определенный как "(void *) 0", выглядит наследием древнего 16-битного прошлого, о его предназначении знать надо  smile 


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GQU
Дата 24.8.2014, 15:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 13.2.2011

Репутация: нет
Всего: нет



что то я вас не пойму
Цитата

в случае "0" - 2 нулевых байта, в случае "(void *) 0" - честных 4 нулевых байта для 16-битной системы

так на 16ти битных машинах указатель, как и int занимает 16 бит, а не 32, хотя нет, там как то по другому все..

Это сообщение отредактировал(а) GQU - 24.8.2014, 16:15
PM MAIL   Вверх
feodorv
Дата 24.8.2014, 16:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(GQU @  24.8.2014,  16:55 Найти цитируемый пост)
так на 16ти битных машинах указатель, как и int занимает 16 бит, а не 32 

Ничего подобного. На 16-битных машинах указатель представлял собой два 16-битных значения. При этом на 16-битных ОС первое представляло собой базовый адрес, второе - так называемый индекс. Итоговый адрес в адресном пространстве процесса получался путем сдвига базового значения на 4 позиции влево и приплюсовывания индекса, что-то вроде такого:
Код
(DS << 4) + SI

Таким образом можно было адресовать до 1 мегабайта памяти (что и было в DOS, например).


Иными словами, то, что на 16-битных системах sizeof(int) был 2, совсем не означает, что и sizeof(void *) обязан быть 2. Реальный режим smile 




Это сообщение отредактировал(а) feodorv - 24.8.2014, 21:55


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GQU
Дата 24.8.2014, 16:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 13.2.2011

Репутация: нет
Всего: нет



Цитата

func( "aaa", "bbb", 0);

Цитата

Но принимая во внимание заодно задачу корректного выравнивания данных в 64-битных системах в стек вызова подпрограммы помещается 8 байт для любого аргумента 

допустим поместили в стек 4 нулевых байта для INT на 64 битной системе, а ОС помещает 8 байт, так не факт что остальные нулевые, хотя наверно они нулями как раз и заполняются или единицами для отрицательных чисел

Раз начали говорить про указатели, спрошу, чтобы не создавать еще одну тему
NULL это реальный нулевой адрес или нет??

Цитата


И получается, что в первом случае в стек не докладывается 2 нужных нулевых байта

мне кажется эти 2 байта и не нужны, потому что когда функция вызывается, компилятор знает типы параметров, он знает, что 0 - это INT и не пойдет искать еще 2 байта,хотя нет, в прототипе же будет написано, что это указатель smile , хотя нет, в том то и прикол, что в прототипе функции с переменным числом аргументов не будет параметров

Это сообщение отредактировал(а) GQU - 24.8.2014, 17:54
PM MAIL   Вверх
feodorv
Дата 24.8.2014, 18:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(GQU @  24.8.2014,  17:38 Найти цитируемый пост)
хотя наверно они нулями как раз и заполняются или единицами для отрицательных чисел

Именно. В 64-х битных системах можно (на ассемблере) поместить в стек 4-х байтовую величину со сдвигом указателя на 8 байт, но смысла с в этом мало. Компиляторы все равно генерят код, в котором 4-х байтовое значение расширяется до 8-ми байтовой величины. Для знакового int оставшиеся 32 бита заполняются знаком, для беззнакового int - просто нулями.


Цитата(GQU @  24.8.2014,  17:38 Найти цитируемый пост)
компилятор знает типы параметров

Компилятор - да, знает. Но об этом не знает функция func. В момент выполнения она надеется увидеть в стеке 4 полноценных байта адреса, а не два обрезанных.


Цитата(GQU @  24.8.2014,  17:38 Найти цитируемый пост)
NULL это реальный нулевой адрес или нет??

А что имеется в виду под реальным? Значение 0 в C/C++ принято присваивать тем указателям, которые (как бы) никуда не указывают. Потом, сравнивая значение указателя с 0, можно посмотреть, рабочий это указатель или нет. Например:
Код

  const char *str = NULL;
  ...
  if( condition ) str = "";
  ...
  if( str == NULL ) str = &buffer[20];
  ...

Вместо удобного 0 (а команды процессора, заносящие 0 в свои регистры, очень короткие и быстрые), можно было бы договориться о другом значении, например, ((void *) -1), но вот было принято вполне понятное решение о нуле. 


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

ptr = strdup(string) + 1;
*ptr++ = '\0';

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


Была у меня ситуация с VDD (то есть Virtual DOS Device под виндами, это вызов 32-х битной среды из 16-битной), когда запись по нулевому адресу прошла (в 32-х битном окружении). Но там, видимо, такой защиты не было предусмотрено, а жаль  smile Так что нулевой адрес - обычный адрес с точки зрения пространства процесса, но в связи с особым смыслом, придаваемым этому значению API, область вокруг нулевого байта (в обе стороны) намеренно закрывается для работы с ней.


Так что ответ на Ваш вопрос будет таким: адрес реальный (при условии переданной процессу первой страницы памяти), но не предназначенный для использования (то есть для чтения/записи). 


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GQU
Дата 24.8.2014, 19:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 13.2.2011

Репутация: нет
Всего: нет



т.е для каждой программы свой процесс, для каждого процесса свой нулевой адрес?
Цитата

В случае нехватки памяти  ptr будет указывать на первый байт процесса. Если бы не упомянутая защита, баг мог бы привести к непредсказуемым последствиям.

Цитата

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

так не факт, что если мы выйдем за пределы кучи, то попадем на эти страницы недоступные для записи/чтения

Это сообщение отредактировал(а) GQU - 24.8.2014, 19:35
PM MAIL   Вверх
feodorv
Дата 24.8.2014, 21:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(GQU @  24.8.2014,  20:28 Найти цитируемый пост)
т.е для каждой программы свой процесс, для каждого процесса свой нулевой адрес?

Да, если не предприняты специальные меры.


Цитата(GQU @  24.8.2014,  20:28 Найти цитируемый пост)
так не факт, что если мы выйдем за пределы кучи, то попадем на эти страницы недоступные для записи/чтения

Гм, что подразумевается под "выходом за пределы кучи"?
Не факт, но если где-то возник NULL и некорректное с ним обращение, то с очень высокой вероятностью. Отлавливание багов - дело весьма занимательное, и я пока не знаю ни одного способа избежать их со 100% вероятностью.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GQU
Дата 24.8.2014, 21:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 13.2.2011

Репутация: нет
Всего: нет



Цитата

Область вокруг нулевого байта (в обе стороны) намеренно закрывается для работы с ней

закрываются адреса, которые уходят в минус? smile 
Как такое может быть?
Цитата

Более того, даже несколько первых (и последних) страниц процесса намеренно делают недоступными для чтения/записи, чтобы сказывались ошибки программирования

тут я не могу понять

Это сообщение отредактировал(а) GQU - 24.8.2014, 21:35
PM MAIL   Вверх
feodorv
Дата 24.8.2014, 21:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(GQU @  24.8.2014,  22:33 Найти цитируемый пост)
Как такое может быть? 

Никогда не встречали такого кода:
Код
s[-1] = ' ';
???

А сколько будет (NULL - 1)?

То есть что напечатает следующий код:
Код
printf( "%p\n", NULL-1);



--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GQU
Дата 24.8.2014, 21:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 13.2.2011

Репутация: нет
Всего: нет



Цитата

То есть что напечатает следующий код:
printf( "%p\n", NULL-1);

FFFFFFFF
тогда получается нулевой байт не первый в выделенном процессе

Это сообщение отредактировал(а) GQU - 24.8.2014, 21:52
PM MAIL   Вверх
feodorv
Дата 24.8.2014, 22:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(GQU @  24.8.2014,  22:52 Найти цитируемый пост)
тогда получается нулевой байт не первый в выделенном процессе

В смысле? В смысле зацикленности адресации памяти в силу ограниченности числа бит, выделенных на указатель? Ну так и unsigned int обладает тем же свойством))) Но поскольку отсчет идет все-таки от нулевого байта, то он-таки первый. Но спереди и сзади него тоже есть байты, этого не отнять. Только сзади находится как раз FFFFFFFF-ый smile 


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GQU
Дата 24.8.2014, 22:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 13.2.2011

Репутация: нет
Всего: нет



Цитата

printf( "%p\n", NULL-1);

так это адрес нашего процесса или другова?
или к другим процессам мы вообще доступа не имеем?

PS: не увидел сообщения выше, не обновил страничку

Цитата

 Более того, даже несколько первых (и последних) страниц процесса намеренно делают недоступными для чтения/записи, чтобы сказывались ошибки программирования

ну тут таже фишка, как и в unsigned int, а то я понять не мог

Это сообщение отредактировал(а) GQU - 24.8.2014, 22:21
PM MAIL   Вверх
feodorv
Дата 24.8.2014, 22:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(GQU @  24.8.2014,  23:04 Найти цитируемый пост)
так это адрес нашего процесса или другова?

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


Цитата(GQU @  24.8.2014,  23:04 Найти цитируемый пост)
или к другим процессам вы вообще доступа не имеем?

В современных ОС процессы отделены друг от друга, но существуют развитые способы межпроцессного взаимодействия. И, действительно, иногда приходится иметь дело с адресами другого процесса (что не означает, что в нашем процессе нет этого же адреса, но уже нашего). Все пользовательские процессы похожи друг на друга, всем выделяется адресное пространство с номерами байт 0-FFFFFFFF (для 32-х битных систем), но только это адресное пространство разное для разных процессов. Плоская модель памяти smile 


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
GQU
Дата 29.8.2014, 22:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 13.2.2011

Репутация: нет
Всего: нет



Цитата

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

что значит некорректное обращение с кучей
можете это на примере полной программы, я что то не могу врубиться
если бы не упомянутая защита, врятли бы указатель указывал на первый байт процесса, в случае некорректного обращения с кучей smile 
Цитата

адрес реальный (при условии переданной процессу первой страницы памяти)

А что, процессу могут и не передать первую страницу памяти?
Цитата

 Более того, даже несколько первых (и последних) страниц процесса намеренно делают недоступными для чтения/записи, чтобы сказывались ошибки программирования

Это же оно и есть?
Неиспользуемая зона, для перехвата обращений по нулевым указателям

Это сообщение отредактировал(а) GQU - 29.8.2014, 23:53
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

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


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1091 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.