promelectronshchic 0 16 февраля, 2011 Опубликовано 16 февраля, 2011 · Жалоба Здравствуйте, помогите разобраться... В многих проекта объявляется константа #define NULL (void*)0... Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Xenia 37 16 февраля, 2011 Опубликовано 16 февраля, 2011 · Жалоба Здравствуйте, помогите разобраться... В многих проекта объявляется константа #define NULL (void*)0... Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем? Чтобы такой нулевой указатель компилятор принимал в качестве аргумента функций и не ругался на него. Нулевой указатель частенько применяется в аргументах функций, когда данный параметр опускается (в смысле не нужен). Типа пустая затычка :). А для некоторых функций такой параметр может означать, что вместо нулевого адреса они могут взять адрес по умолчанию или из каких-то разумных соображений. Типа того, что если укажешь адрес буфера, то тебе в него что-то насыплют, а если не укажешь, но ничего не насыпют :). В последнем случае NULL работает как признак затребования опционных данных, которые отпускаются, когда под них приготовлена ёмкость. NULL - отсутствие такой ёмкости, отказ от опционных данных. Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 0 (цифру) автоматичеcки приводить к типу void*. Тогда определение NULL становится ненужным. А точнее, нужным только в чистом языке C. Хотя все-таки приятнее уметь отличать на глаз цифру 0 от нулевого указателя. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 69 16 февраля, 2011 Опубликовано 16 февраля, 2011 · Жалоба Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 0 (цифру) автоматичеcки приводить к типу void*. Тогда определение NULL становится ненужным. А точнее, нужным только в чистом языке C. Хотя все-таки приятнее уметь отличать на глаз цифру 0 от нулевого указателя. Из стандарта C99: 6.3.2.3 Pointers ... 3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.55) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function. ... 55) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.17. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
sergeeff 1 16 февраля, 2011 Опубликовано 16 февраля, 2011 · Жалоба /* Define NULL pointer value */ #ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 69 16 февраля, 2011 Опубликовано 16 февраля, 2011 · Жалоба Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем? Например, чтобы компилятор ругнулся, если NULL используется инициализации переменной, не являющейся указателем, типа "int e = NULL;". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
andrew_b 15 17 февраля, 2011 Опубликовано 17 февраля, 2011 · Жалоба Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 0 (цифру) автоматичеcки приводить к типу void*. Гм, а раньше что, нельзя было? Вы читали Страуструпа? Он рекомендует использовать именно 0 вместо NULL. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Twen 0 17 февраля, 2011 Опубликовано 17 февраля, 2011 · Жалоба Нулевой указатель частенько применяется в аргументах функций, когда данный параметр опускается (в смысле не нужен). Не совсем понятно выражение - нулевой указатель, правильнее сказать указатель типа void, который указывает на ячейку памяти с нулевым адресом, правильно? Я правильно понял, это делается для того, чтобы передать в качестве параметра функции нулевой адрес: #define NULL ((void*)0) void x(char *y) { if(y==0)... else... } void main(void) { x((char*)NULL); } Или второй вариант: #define NULL 0 void x(char *y) { if(y==0)... else... } void main(void) { x((char*)NULL); } В результате я получил два идентичные результата... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 17 февраля, 2011 Опубликовано 17 февраля, 2011 · Жалоба Что мне всегда нравилось в определяющих Си стандартах - так это полное отсутствие логики. Как в данном случае - NULL это с какого-то бодуна всегда 0 и ничего более. Куда катится мир? :crying: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 123 17 февраля, 2011 Опубликовано 17 февраля, 2011 · Жалоба Что мне всегда нравилось в определяющих Си стандартах - так это полное отсутствие логики.Если их не читать - то так и будет казаться. По определению Стандарта NULL - указатель, который не указывает ни на один объект. Не "нулевой указатель", не "указатель на ноль", а именно "указатель, который указывает в никуда". Конкретное его значение отдано на откуп компилятору. В результате я получил два идентичные результата...Конечно. А что вы ожидали, сделав явное приведение типа x((char*)NULL)? Вы сделали явное приаведение типа - компилятор умывает руки. В языке C void * неявно приводится к указателю на любой тип и указатель на любой тип неявно приводится к void *. Поэтому в функции memcpy(), memset() и тому подобные можно передавать указатель на любые данные без явного приведения типов. И поэтому результат malloc() можно присвоить указателю на любой тип без явного приведения. Если бы вы написали x(NULL) - вы получили бы то, что ожидается. В С++ разрешено только неявное приведение любого указателя к void *. И там добавлено неявное преобразование константы 0 к указателю на любой тип, тоже в смысле "указатель, который указывает в никуда". Какое именно значение будет занесено в указатель в результате приведения константы 0 не оговаривается. А макрос NULL в C++ изначально был введен только для совместимости исходников с С. В новом стандарте его вводят снова - с константой 0 возникают неоднозначности c перегрузкой операторов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 17 февраля, 2011 Опубликовано 17 февраля, 2011 · Жалоба По определению Стандарта NULL - указатель, который не указывает ни на один объект. Не "нулевой указатель", не "указатель на ноль", а именно "указатель, который указывает в никуда". Конкретное его значение отдано на откуп компилятору. Я о C99, aaarrr приводил цитату. Да, указатель, который указывает в никуда - это единственно правильная формулировка. И если, допустим, мне в арме надо отловить обращение в никуда (как запись так и чтение), я 0 использовать не буду. Равно как и буду писАть if(ptr != NULL) вместо if(ptr) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Xenia 37 17 февраля, 2011 Опубликовано 17 февраля, 2011 · Жалоба Да, указатель, который указывает в никуда - это единственно правильная формулировка. И если, допустим, мне в арме надо отловить обращение в никуда (как запись так и чтение), я 0 использовать не буду. Равно как и буду писАть if(ptr != NULL) вместо if(ptr) Вы видите только одну сторону вопроса, но есть и другая. Дело в том, что контроль типов никто не отменял - а это очень важная и нужная функция компилятора! Т.к. стоит только отменить контроль типов в аргументах функций, то количество ошибок возрастет в разы. Если же все мы согласны с тем, что типы указателей компилятор должен проверять (например, не допуская подстановки указателя на char вместо указателя на int), то должны понимать и то, что подстановка числа вместо указателя - гораздо бОльшая вольность, чем подмена указателей на разные типы данных. Уже только потому, что указатели разных типов - это все-таки адреса областей памяти, а числа - совершенно другая ипостась. Подразумевается, что по умолчанию программист не знает точных адресов, где будут располагаться программные объекты (исключения встречаются, но общая стратегия этого требует), а потому число, введенное вместо адреса, - обычно ошибка подсчета порядкового номера аргумента в параметрах функции, когда они по недосмотру "сползают". А если уж программист делает такое потенциально опасное действие, как подстановка числового адреса, то не будет бедой, если он оформит свое желание явным переопределением типа. Если разрешено подставлять вместо указателя 0, то почему нельзя 5, или 176, или любое алгебраическое выражение? Надо понимать, что, идя по пути легализации нуля, как допустимого указателя, мы ступаем на очень шаткий мостик. Нет разумных доводов в пользу того, что число 176 можно поставить в качестве указателя, а элемент массива M нельзя, поскольку то и другое - числа. Но стоит только разрешить M в качестве указателя, то начнет возникать путаница между M и &M, аналогичная тому, как случается такая путаница между именами и указателями на строковые массивы (многие долбят перед его именем &, когда само имя - уже указатель). По этим причинам, допущение подстановки нуля вместо указателя - не самая лучшая идея. И здесь специальный объект NULL - хороший выход из этого положения, т.к. его введение не нарушает строгости контроля типов и повышает дисциплину программирования. Ведь не даром же наиболее жесткая критика языка С/С++ раздается из-за разрешенных операций с указателями, позволяющими писать в произвольное место памяти. Это много серьезнее, чем безобидный goto :). Однако и в этом случае использовать выражение if(ptr) вполне допустимо, т.к. здесь указатель приводится к логическому типу, а не наоборот. Такое приведение типов по своей природе безвредно. Как, впрочем, и превращение указателя в число. А вот превращение числа в указатель - крайне опасная вещь. Ведь запись по случайному указателю может разрушить структуру памяти всего приложения, а то и операционной системы. Тогда как неправильное число в переменной - не так уж и страшно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться