Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Банальный вопрос...(void*)0
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
promelectronshchic
Здравствуйте, помогите разобраться...
В многих проекта объявляется константа #define NULL (void*)0...
Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем?
Xenia
Цитата(promelectronshchic @ Feb 16 2011, 19:33) *
Здравствуйте, помогите разобраться...
В многих проекта объявляется константа #define NULL (void*)0...
Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем?

Чтобы такой нулевой указатель компилятор принимал в качестве аргумента функций и не ругался на него.

Нулевой указатель частенько применяется в аргументах функций, когда данный параметр опускается (в смысле не нужен). Типа пустая затычка sm.gif. А для некоторых функций такой параметр может означать, что вместо нулевого адреса они могут взять адрес по умолчанию или из каких-то разумных соображений. Типа того, что если укажешь адрес буфера, то тебе в него что-то насыплют, а если не укажешь, но ничего не насыпют sm.gif. В последнем случае NULL работает как признак затребования опционных данных, которые отпускаются, когда под них приготовлена ёмкость. NULL - отсутствие такой ёмкости, отказ от опционных данных.

Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 0 (цифру) автоматичеcки приводить к типу void*. Тогда определение NULL становится ненужным. А точнее, нужным только в чистом языке C. Хотя все-таки приятнее уметь отличать на глаз цифру 0 от нулевого указателя.
aaarrr
Цитата(Xenia @ Feb 16 2011, 19:58) *
Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 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
Код
/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL    0
#else
#define NULL    ((void *)0)
#endif
#endif

aaarrr
Цитата(promelectronshchic @ Feb 16 2011, 19:33) *
Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем?

Например, чтобы компилятор ругнулся, если NULL используется инициализации переменной, не являющейся указателем, типа "int e = NULL;".
andrew_b
Цитата(Xenia @ Feb 16 2011, 19:58) *
Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 0 (цифру) автоматичеcки приводить к типу void*.
Гм, а раньше что, нельзя было? Вы читали Страуструпа? Он рекомендует использовать именно 0 вместо NULL.
Twen
Цитата
Нулевой указатель частенько применяется в аргументах функций, когда данный параметр опускается (в смысле не нужен).


Не совсем понятно выражение - нулевой указатель, правильнее сказать указатель типа 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
Что мне всегда нравилось в определяющих Си стандартах - так это полное отсутствие логики. Как в данном случае - NULL это с какого-то бодуна всегда 0 и ничего более. Куда катится мир? crying.gif
Сергей Борщ
QUOTE (_Pasha @ Feb 17 2011, 09:29) *
Что мне всегда нравилось в определяющих Си стандартах - так это полное отсутствие логики.
Если их не читать - то так и будет казаться. По определению Стандарта NULL - указатель, который не указывает ни на один объект. Не "нулевой указатель", не "указатель на ноль", а именно "указатель, который указывает в никуда". Конкретное его значение отдано на откуп компилятору.

QUOTE (Twen @ Feb 17 2011, 09:20) *
В результате я получил два идентичные результата...
Конечно. А что вы ожидали, сделав явное приведение типа x((char*)NULL)? Вы сделали явное приаведение типа - компилятор умывает руки. В языке C void * неявно приводится к указателю на любой тип и указатель на любой тип неявно приводится к void *. Поэтому в функции memcpy(), memset() и тому подобные можно передавать указатель на любые данные без явного приведения типов. И поэтому результат malloc() можно присвоить указателю на любой тип без явного приведения. Если бы вы написали x(NULL) - вы получили бы то, что ожидается.

В С++ разрешено только неявное приведение любого указателя к void *. И там добавлено неявное преобразование константы 0 к указателю на любой тип, тоже в смысле "указатель, который указывает в никуда". Какое именно значение будет занесено в указатель в результате приведения константы 0 не оговаривается. А макрос NULL в C++ изначально был введен только для совместимости исходников с С. В новом стандарте его вводят снова - с константой 0 возникают неоднозначности c перегрузкой операторов.
_Pasha
Цитата(Сергей Борщ @ Feb 17 2011, 10:56) *
По определению Стандарта NULL - указатель, который не указывает ни на один объект. Не "нулевой указатель", не "указатель на ноль", а именно "указатель, который указывает в никуда". Конкретное его значение отдано на откуп компилятору.

Я о C99, aaarrr приводил цитату.
Да, указатель, который указывает в никуда - это единственно правильная формулировка. И если, допустим, мне в арме надо отловить обращение в никуда (как запись так и чтение), я 0 использовать не буду. Равно как и буду писАть if(ptr != NULL) вместо if(ptr)
Xenia
Цитата(_Pasha @ Feb 17 2011, 11:13) *
Да, указатель, который указывает в никуда - это единственно правильная формулировка. И если, допустим, мне в арме надо отловить обращение в никуда (как запись так и чтение), я 0 использовать не буду. Равно как и буду писАть if(ptr != NULL) вместо if(ptr)

Вы видите только одну сторону вопроса, но есть и другая. Дело в том, что контроль типов никто не отменял - а это очень важная и нужная функция компилятора! Т.к. стоит только отменить контроль типов в аргументах функций, то количество ошибок возрастет в разы.

Если же все мы согласны с тем, что типы указателей компилятор должен проверять (например, не допуская подстановки указателя на char вместо указателя на int), то должны понимать и то, что подстановка числа вместо указателя - гораздо бОльшая вольность, чем подмена указателей на разные типы данных. Уже только потому, что указатели разных типов - это все-таки адреса областей памяти, а числа - совершенно другая ипостась. Подразумевается, что по умолчанию программист не знает точных адресов, где будут располагаться программные объекты (исключения встречаются, но общая стратегия этого требует), а потому число, введенное вместо адреса, - обычно ошибка подсчета порядкового номера аргумента в параметрах функции, когда они по недосмотру "сползают". А если уж программист делает такое потенциально опасное действие, как подстановка числового адреса, то не будет бедой, если он оформит свое желание явным переопределением типа.

Если разрешено подставлять вместо указателя 0, то почему нельзя 5, или 176, или любое алгебраическое выражение? Надо понимать, что, идя по пути легализации нуля, как допустимого указателя, мы ступаем на очень шаткий мостик. Нет разумных доводов в пользу того, что число 176 можно поставить в качестве указателя, а элемент массива M[i] нельзя, поскольку то и другое - числа. Но стоит только разрешить M[i] в качестве указателя, то начнет возникать путаница между M[i] и &M[i], аналогичная тому, как случается такая путаница между именами и указателями на строковые массивы (многие долбят перед его именем &, когда само имя - уже указатель).

По этим причинам, допущение подстановки нуля вместо указателя - не самая лучшая идея. И здесь специальный объект NULL - хороший выход из этого положения, т.к. его введение не нарушает строгости контроля типов и повышает дисциплину программирования. Ведь не даром же наиболее жесткая критика языка С/С++ раздается из-за разрешенных операций с указателями, позволяющими писать в произвольное место памяти. Это много серьезнее, чем безобидный goto sm.gif.

Однако и в этом случае использовать выражение if(ptr) вполне допустимо, т.к. здесь указатель приводится к логическому типу, а не наоборот. Такое приведение типов по своей природе безвредно. Как, впрочем, и превращение указателя в число. А вот превращение числа в указатель - крайне опасная вещь. Ведь запись по случайному указателю может разрушить структуру памяти всего приложения, а то и операционной системы. Тогда как неправильное число в переменной - не так уж и страшно.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.