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

Поиск:

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


Новичок



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

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



Насколько мне извесно в C NULL - это указатель на нулевой адрес, поэтому и определен ((void *)0) и не в коем случае нулевой значение какого-то объекта. В плюсах NULL оставили из-за совместимости, и не рекомендуют им пользоваться, а вместо него писать 0. Или же самому на свой страх и риск определить NULL и следить за его использованием. а то что
Цитата

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


Чем же нехорош вызов этой функции в виде
Выделить всёкод C++
1:
    
func( "aaa", "bbb", 0);
по сравнению с
Выделить всёкод C++
1:
    
func( "aaa", "bbb", NULL);

так здесь нужно смотреть на объявление функции, и компилятор должен по правилам приведения привести NULL к нужному типу, так что каких-то глюков с стеке быть не должно!!!
По поводу нулевого указателя, так это скорее всего больше условность, обусловленная средой выполнения. Думаю в низкоуровневом программировании есть возможность использования адреса 0х00000000. 
Насчет
Цитата

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

правильно выразиться для каждого процесса свой нулевой адрес. Опять таки, зависит от среды выполнения. Процесс может работать как в виртуальном адресном пространстве с контролем доступа, здесь у каждого процесса свое виртуальное адресное пространство так и в реальном адресном пространстве(системные процесс например) - здесь один нулевой адрес, и 0-1 = последний адрес а адресном пространстве цпу.
Вообще, вся работа с памятью будет зависить от среды выполнения. Для высокоуровневого программирования указатель на 0  - это неинициализированный указатель, его использование вызовет ошибку. Также рекомендуется поменьше арифметики с адресами, так как после некоторых опяраций поведение программы непредсказуемы, например 0-1 может вызвать исключение, и если его не обработать - то крах процесса.    
PM MAIL   Вверх
feodorv
Дата 30.8.2014, 00:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(GQU @  29.8.2014,  23:57 Найти цитируемый пост)
что значит некорректное обращение с кучей

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


Цитата(GQU @  29.8.2014,  23:57 Найти цитируемый пост)
можете это на примере полной программы, я что то не могу врубиться

Нарушить целостность кучи возможно, но специально прямо щас я это сделать не смогу. Просто в некоторых случаях HeapAlloc возвращает NULL, если куча разрушена, хотя свободная память в необходимом объеме все же есть. Тогда 
Цитата(feodorv @  24.8.2014,  19:22 Найти цитируемый пост)
ptr = strdup(string) + 1;

указатель ptr будет указывать на байт под номером один в адресном пространстве процесса.


Цитата(GQU @  29.8.2014,  23:57 Найти цитируемый пост)
если бы не упомянутая защита, врятли бы указатель указывал на первый байт процесса, в случае некорректного обращения с кучей smile 

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


Цитата(GQU @  29.8.2014,  23:57 Найти цитируемый пост)
А что, процессу могут и не передать первую страницу памяти?

Уф. Изначально процессу передаются не все страницы памяти. Вернее, не только лишь все, мало какие. А в нашем случае оговариваемые страницы резервируются, но не передаются процессу.


ЗЫ В C/C++ принято все отсчитывать от нуля. И очень сложно в Ваших вопросах адекватно воспринимать фразы типа "первый байт", "первая страница".


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


Эксперт
****


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

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



Цитата(Merlin_ua @  30.8.2014,  00:46 Найти цитируемый пост)
так здесь нужно смотреть на объявление функции

Объявление функции (кстати, я его уже приводил):
Цитата(feodorv @  24.8.2014,  00:25 Найти цитируемый пост)
void func( ... );



Цитата(Merlin_ua @  30.8.2014,  00:46 Найти цитируемый пост)
компилятор должен по правилам приведения привести NULL к нужному типу, так что каких-то глюков с стеке быть не должно!!!

Пожалуйста, из указанного объявления функции приведите аргумент 0 (а не упоминаемый Вами NULL) к нужному виду:
Код
  func( 0 );



Цитата(Merlin_ua @  30.8.2014,  00:46 Найти цитируемый пост)
так это скорее всего больше условность

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


Цитата(Merlin_ua @  30.8.2014,  00:46 Найти цитируемый пост)
Думаю в низкоуровневом программировании есть возможность использования адреса 0х00000000. 

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


Цитата(Merlin_ua @  30.8.2014,  00:46 Найти цитируемый пост)
Для высокоуровневого программирования указатель на 0  - это неинициализированный указатель

Это как раз инициализированный указатель, инициализированный значением 0. Кстати, где Вы учились? 


Цитата(Merlin_ua @  30.8.2014,  00:46 Найти цитируемый пост)
его использование вызовет ошибку

Его разыменовывание вызовет ошибку. А использовать я его могу:
Код
if( (s = strdup( string )) == NULL ) return NULL;



Цитата(Merlin_ua @  30.8.2014,  00:46 Найти цитируемый пост)
системные процесс например

Давайте разберёмся с пользовательскими, пожалуйста. Пример, когда запись по адресу NULL прошла в пользовательском процессе, я уже описывал.


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


Новичок



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

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



Цитата

Объявление функции (кстати, я его уже приводил):
Цитата(feodorv @  24.8.2014,  00:25 Найти цитируемый пост)
void func( ... );

Понял.  В данном случае у Вас функция с переменным/незвестным колличеством и типом аргументов. В таких случаях вся ответственность на то, что попадает в стек и как его оттуда извлекать на программисте. А насчет
Цитата

Пожалуйста, из указанного объявления функции приведите аргумент 0 (а не упоминаемый Вами NULL) к нужному виду:
Выделить всёкод C++
1:
    
  func( 0 );

так 0 по умолчанию это int На платформе IA32 с компилятором от Microsoft или GCC в стек пойдет 4 байта.
Цитата

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

Вы наверное имели ввду обнуление регистров а не загрузку. Загружать в регистр по времени что 0х00000000 что 0xffffffff одинаково, а вот обнулить регистр путем вычитания самого себя - быстрее чем загрузка 0х00000000. Насчет соображений и философии тоже с Вами согласен.
Цитата

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

Не пользовательские процессы, а среда выполнения Runtime  Environment, и Вы правы, всеравно на чем набит код. Среда ограничит действия кода. И будет закрыта не только область вокруг нулевого байта. Там много к чему еще будет ограничен доступ.
Цитата

Это как раз инициализированный указатель, инициализированный значением 0. Кстати, где Вы учились? 
Его разыменовывание вызовет ошибку. А использовать я его могу:

Ошибся. Неправильно выразил мысль. Я хотел сказать, доступ к памяти, на которую ссылается нулевой указатель вызовет ошибку.
А что Вы так о моем образовании? Если у меня нет образования то что Вы хотите сказать? Что у мне нет места здесь? Или я не имею права поучаствовать в теме? Тема ж для новичков! Я не претендую на звание  высоквалифицированного программиста. Я - не волшебник, я только учусь! Ну а Вы, если имеете солидную квалификацию, поправте где я не прав, научите как правильно!
Цитата

Давайте разберёмся с пользовательскими, пожалуйста. Пример, когда запись по адресу NULL прошла в пользовательском процессе, я уже описывал. 

Так я и не против! И про запись по NULL в пользовательском процессе тоже не спорю. Я лишь хотел хотел выразить мысль, что там, где идет работа с памятью и  необходимо понимание протекающих с нею манипуляций  нужно учитывать runtime environment.
PM MAIL   Вверх
feodorv
Дата 30.8.2014, 12:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Merlin_ua @  30.8.2014,  12:40 Найти цитируемый пост)
так 0 по умолчанию это int На платформе IA32 с компилятором от Microsoft или GCC в стек пойдет 4 байта

Цитата(feodorv @  24.8.2014,  00:25 Найти цитируемый пост)
в эпоху 16-битных процессоров

Цитата(feodorv @  24.8.2014,  00:25 Найти цитируемый пост)
Для 32-битных систем ... такой проблемы не существует.

Цитата(feodorv @  24.8.2014,  00:25 Найти цитируемый пост)
Впрочем, хотя NULL, определенный как "(void *) 0", выглядит наследием древнего 16-битного прошлого, ...



Цитата(Merlin_ua @  30.8.2014,  12:40 Найти цитируемый пост)
Вы наверное имели ввду обнуление регистров а не загрузку

Конечно)))


Цитата(Merlin_ua @  30.8.2014,  12:40 Найти цитируемый пост)
Там много к чему еще будет ограничен доступ.

Цитата(feodorv @  30.8.2014,  01:43 Найти цитируемый пост)
Изначально процессу передаются не все страницы памяти. Вернее, не только лишь все, мало какие.



Цитата(Merlin_ua @  30.8.2014,  12:40 Найти цитируемый пост)
А что Вы так о моем образовании?

Так, навеяло))) Извините, если что...


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


Новичок



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

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



А что там в эпоху 16-разрядных систем? Если нетрудно - расскажите. Я в эти времена еще в школе учился на турбо-паскале на Поиске 2! Неужели стандарты тех времен не имели 0 по умолчанию? И что странного  "(void *) 0"? Хотя что-то тут есть неудобовразумительное. В некоторых технологиях оговаривают, что NULL - это не 0, и использовать вместо  NULL  - 0 запрещают (по моему встречал такую оговорку в COM). В плюсах рекомендуют не использовать NULL или определить по собственному усмотрению.
PM MAIL   Вверх
feodorv
Дата 30.8.2014, 13:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Merlin_ua @  30.8.2014,  13:33 Найти цитируемый пост)
А что там в эпоху 16-разрядных систем?

Гм. А Вы вопрос ТС читали? А последующие соображения?


Цитата(Merlin_ua @  30.8.2014,  13:33 Найти цитируемый пост)
И что странного  "(void *) 0"

А Вы в плюсах такой код пробовали:
Код

int *list = (void *) 0;

???

Цитата(Merlin_ua @  30.8.2014,  13:33 Найти цитируемый пост)
В плюсах рекомендуют не использовать NULL или определить по собственному усмотрению. 

Потому что в код C++ может быть включен заголовок, где NULL определен как "(void *) 0". Со всеми вытикающими... nullptr призван спасти ситуацию.

Это сообщение отредактировал(а) feodorv - 30.8.2014, 13:30


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


Новичок



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

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



Цитата

Гм. А Вы вопрос ТС читали? А последующие соображения?

Читал. Вы наверно имеете ввиду следующие соображения:
Цитата

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


Чем же нехорош вызов этой функции в виде
Выделить всёкод C++
1:
    
func( "aaa", "bbb", 0);
по сравнению с
Выделить всёкод C++
1:
    
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  
Цитата

так на 16ти битных машинах указатель, как и int занимает 16 бит, а не 32 

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

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


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

Здесь хочу отметить следующее:
1. В случае с void func( ... ) контроль за аргументами функции полностью возлагается на программиста, и то, что у Вас там может не докладывается 2 нужных нулевых байта или же окажется какой-то мусор - это полностью ответственность программиста.
2.Я подозреваю, в настоящей теме, под 16-битной системой подразумевается Intel8086. Так вот. здесь не стоит путать физический и эффективный адрес. Указатели используют 16-разрядный эффективный адрес. Так что и sizeof(int) и sizeof(int*) имеют 2 байта. Манипуляции с 16-разрядным адресным регистром и сегментным регистром происходят на нижних уровнях - на системном уровне ОС.
Цитата

А Вы в плюсах такой код пробовали:

В плюсах контроль над типами другой.
Цитата

Потому что в код C++ может быть включен заголовок, где NULL определен как "(void *) 0". Со всеми вытикающими... nullptr призван спасти ситуацию.

Полностью согласен. О NULL в плюсах даны конкретные рекомендации!
PM MAIL   Вверх
feodorv
Дата 31.8.2014, 00:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
1. В случае с void func( ... ) контроль за аргументами функции полностью возлагается на программиста, и то, что у Вас там может не докладывается 2 нужных нулевых байта или же окажется какой-то мусор - это полностью ответственность программиста.

Да, поэтому программист и пишет:
Код
func( "111", "2222", "3333", "4444", "55555", NULL);
а отнюдь не
Код
func( "111", "2222", "3333", "4444", "55555", 0);
Последний вариант неверный.


Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
Я подозреваю, в настоящей теме, под 16-битной системой подразумевается Intel8086.

Совсем не обязательно (по ссылке перечислено множество 16-битных процессоров). 16-битные процессоры разрабатываются до сих пор. Ну пусть будет Intel8086/Intel80286.


Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
Так вот. здесь не стоит путать физический и эффективный адрес.

Да ладно)))


Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
Указатели используют 16-разрядный эффективный адрес

Far-указатели используют как базу, так и индекс. Каждая величина является 16-битным значением. В сумме - 32 бита при 16-битном выравнивании. См. модели памяти.


Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
Так что и sizeof(int) и sizeof(int*) имеют 2 байта.

Чувствуется, что Вы никогда не программировали на 16 битах на far-указателях. Модель данных, при которой int занимает 2 байта, а указатель - 4 байта, называется I16LP32, и применялась она не только на Интелах.

Это сообщение отредактировал(а) feodorv - 31.8.2014, 01:22


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


Новичок



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

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



Цитата

Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
Я подозреваю, в настоящей теме, под 16-битной системой подразумевается Intel8086.

Совсем не обязательно (по ссылке перечислено множество 16-битных процессоров). 16-битные процессоры разрабатываются до сих пор. Ну пусть будет Intel8086/Intel80286.


Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
Так вот. здесь не стоит путать физический и эффективный адрес.

Да ладно)))


Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
Указатели используют 16-разрядный эффективный адрес

Far-указатели используют как базу, так и индекс. Каждая величина является 16-битным значением. В сумме - 32 бита при 16-битном выравнивании. См. модели памяти.


Цитата(Merlin_ua @  30.8.2014,  16:57 Найти цитируемый пост)
Так что и sizeof(int) и sizeof(int*) имеют 2 байта.

Чувствуется, что Вы никогда не программировали на 16 битах на far-указателях. Модель данных, при которой int занимает 2 байта, а указатель - 4 байта, называется I16LP32, и применялась она не только на Интелах.


Да, вы полностью правы! far указатели не использовал! Спасибо, что объяснили!
Цитата

Да, поэтому программист и пишет:
Выделить всёкод C++
1:
    
func( "111", "2222", "3333", "4444", "55555", NULL);
а отнюдь не
Выделить всёкод C++
1:
    
func( "111", "2222", "3333", "4444", "55555", 0);
Последний вариант неверный.

Почему не верный? в первом случае Вы передаете ((void *)0) во втором int. Ведь void func( ... ) говорит о том, типы и их количество не известны!
PM MAIL   Вверх
feodorv
Дата 31.8.2014, 19:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Merlin_ua @  31.8.2014,  11:43 Найти цитируемый пост)
Почему не верный?

Потому что func ожидает указатель. А не интегер. NULL служит указанием того, что список аргументов (указателей на строки) завершен. Такое было в моде одно время.


Цитата(Merlin_ua @  31.8.2014,  11:43 Найти цитируемый пост)
Ведь void func( ... ) говорит о том, типы и их количество не известны! 

Именно. Значит func как-то должна знать, что ей передают. printf, например, использует форматную не только для того, чтобы оформить вывод, но и для того, чтобы понять типы переданных ей аргументов. Но даже здесь NULL может быть полезен (пример весьма искусственен, ну да ладно):
Код

const char *fmt, *message;

if( some_condition ) 
  fmt = "%0s%s";
else
  getFormat( &fmt );

printf( fmt, 0, message); // неправильно

Последняя строка должна выглядеть так:
Код

printf( fmt, NULL, message);


В случае func с переданным ей неопределённым числом указателей на строки как-то нужно сообщить функции число этих самых указателей. Это можно было бы сделать так:
Код
void func( int count, ...);
и ориентироваться на значение count. Но в C слишком часто полагаются на дефайны, и программист мог использовать следующие конструкции:
Код

#if SOME_CONDITION
#  define DEF def, " for ", vname
#else
#  define DEF def
#endif

func( "Debug message", DEF, "bla bla bla", NULL);

не заботясь о числе аргументов, но завершая их список нулевым указателем. Когда func, выбирая указатели на строки из списка аргументов, натыкается на NULL, она понимает, что всё, список аргументов исчерпан. Когда она натыкается на 0 и ещё два каких-то случайных (скорее всего ненулевых) байта, то она продолжит обработку списка аргументов...


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


Новичок



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

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



Я понял Вашу мысль. Но я также хочу что б Вы поняли и мою. Давайте вернемся к началу.
Цитата

NULL вместо 0 нужен лишь в одном единственном случае - при передаче нулевого указателя в функцию с переменным числом аргументов. 

и здесь пояснение
Цитата

В случае func с переданным ей неопределённым числом указателей на строки как-то нужно сообщить функции число этих самых указателей. Это можно было бы сделать так:
Код

void func( int count, ...);

и ориентироваться на значение count. Но в C слишком часто полагаются на дефайны, и программист мог использовать следующие конструкции:
Код

#if SOME_CONDITION
#  define DEF def, " for ", vname
#else
#  define DEF def
#endif
func( "Debug message", DEF, "bla bla bla", NULL);

не заботясь о числе аргументов, но завершая их список нулевым указателем. Когда func, выбирая указатели на строки из списка аргументов, натыкается на NULL, она понимает, что всё, список аргументов исчерпан. Когда она натыкается на 0 и ещё два каких-то случайных (скорее всего ненулевых) байта, то она продолжит обработку списка аргументов... 

Я так понимаю, что аргументы будут выбираться до тех пор, пока не встретится последовательность из 4-х байт равных 0х00.
Тогда в случае
Код

 func( "\0\0\0\0", "Debug message", DEF, "bla bla bla", NULL);
 func( "\0\0\0\0", "Debug message", DEF, "bla bla bla", NULL);
или же 
Код

int a = 0;
int b = 1;
int c = 3;
func( a, b, c, NULL);

Вы не только потеряете аргументы, но и приведете к краху программы.
Я хочу донести мысль, что крах в случае с использованием void func( ... ) не из-за  того, что int может быть двухбайтовым а  NULL четырехбайтовым.

И мысль
Цитата

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

мне тоже не понятна. Хотя возможно это и так.
PM MAIL   Вверх
feodorv
Дата 31.8.2014, 21:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Merlin_ua @  31.8.2014,  21:26 Найти цитируемый пост)
Я хочу донести мысль, что крах в случае с использованием void func( ... ) не из-за  того, что int может быть двухбайтовым а  NULL четырехбайтовым.

И не донесете. Крах в случае намеренно некорректного программирования неизбежен. Иное дело, когда все запрограммировано на вид правильно, а оно рушится. 


Цитата(Merlin_ua @  31.8.2014,  21:26 Найти цитируемый пост)
Давайте вернемся к началу.

Был поставленный ТС вопрос: зачем (void *) 0, а не просто 0. Прозвучал ответ:
Цитата(feodorv @  24.8.2014,  00:25 Найти цитируемый пост)
NULL вместо 0 нужен лишь в одном единственном случае - при передаче нулевого указателя в функцию с переменным числом аргументов. Во всех остальных случаях NULL, определенный как ((void *)0), не нужен, его вполне может заменить простой 0.

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


В качестве примера взята функция func(...) которая работает с набором указателей на строки. Причем здесь
Цитата(Merlin_ua @  31.8.2014,  21:26 Найти цитируемый пост)
int a = 0;
int b = 1;
int c = 3;
func( a, b, c, NULL);

a, b, c - это указатели на строки? smile И почему в случае с
Цитата(Merlin_ua @  31.8.2014,  21:26 Найти цитируемый пост)
func( "\0\0\0\0", "Debug message", DEF, "bla bla bla", NULL);
будет крах программы??? И зачем вменяемый программист будет передавать в функцию такой набор аргументов?


Честно, мне лень лазить по интернету, чтобы подкрепить свои слова чужими. Вот на вскидку:
Цитата

However, an argument being passed to a function is not necessarily recognizable as a pointer context, and the compiler may not be able to tell that an unadorned 0 ``means'' a null pointer. To generate a null pointer in a function call context, an explicit cast may be required, to force the 0 to be recognized as a pointer. For example, the Unix system call execl takes a variable-length, null-pointer-terminated list of character pointer arguments, and is correctly called like this:

    execl("/bin/sh", "sh", "-c", "date", (char *)0);

If the (char *) cast on the last argument were omitted, the compiler would not know to pass a null pointer, and would pass an integer 0 instead.



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


Новичок



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

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



Цитата

И не донесете.

По всей видимости да!

Цитата

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

Определено стандартом или Ваше личное суждение?

Цитата

В качестве примера взята функция func(...) которая работает с набором указателей на строки. Причем здесь
a, b, c - это указатели на строки?

Где оговорено, что в качестве аргументов исключительно указатели на строки???
Вы действительно не понимаете что такое a, b, c или притворяетесь ???  И если Вам не понятен мой пример, так это тоже лишь иллюстрация.
Цитата

будет крах программы???

Будет! Также, как и в Вашем  случае, когда Вы не докладываете два байта с стек 
Цитата

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

так как в стеке остается куча мусора.
Цитата

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

Это похоже из той серии, где шла речь об образовании.
Цитата

Вот на вскидку:

Это что? ответ на
Цитата

Был поставленный ТС вопрос: зачем (void *) 0, а не просто 0.

или доказательство ликвидности Ваших примеров??? Выше я писал, что ликвидность лишь в частных случаях!!
PM MAIL   Вверх
feodorv
Дата 31.8.2014, 22:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Merlin_ua @  31.8.2014,  23:17 Найти цитируемый пост)
Определено стандартом или Ваше личное суждение?

Определено что?


Цитата(Merlin_ua @  31.8.2014,  23:17 Найти цитируемый пост)
Где оговорено, что в качестве аргументов исключительно указатели на строки???

То есть я обязан все разжевывать? Из примера не ясно? Ну извините.


Цитата(Merlin_ua @  31.8.2014,  23:17 Найти цитируемый пост)
Будет! Также, как и в Вашем  случае, когда Вы не докладываете два байта с стек 

Как краха программы не будет, так и потери аргументов.


Цитата(Merlin_ua @  31.8.2014,  23:17 Найти цитируемый пост)
или доказательство ликвидности Ваших примеров???

Это доказательство того, что не просто
Цитата(Merlin_ua @  31.8.2014,  23:17 Найти цитируемый пост)
Ваше личное суждение
 smile 



--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

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

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

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

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


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

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


 




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


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

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