Перейти к содержанию
    

Банальный вопрос...(void*)0

Здравствуйте, помогите разобраться...

В многих проекта объявляется константа #define NULL (void*)0...

Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Здравствуйте, помогите разобраться...

В многих проекта объявляется константа #define NULL (void*)0...

Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем?

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

 

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

 

Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 0 (цифру) автоматичеcки приводить к типу void*. Тогда определение NULL становится ненужным. А точнее, нужным только в чистом языке C. Хотя все-таки приятнее уметь отличать на глаз цифру 0 от нулевого указателя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 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.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Зачем это делается? Мы как бы приводим тип указателя на 0 к типу void, зачем?

Например, чтобы компилятор ругнулся, если NULL используется инициализации переменной, не являющейся указателем, типа "int e = NULL;".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хотя сейчас появилось довольно много С++ компиляторов, которые способны обычный 0 (цифру) автоматичеcки приводить к типу void*.
Гм, а раньше что, нельзя было? Вы читали Страуструпа? Он рекомендует использовать именно 0 вместо NULL.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Не совсем понятно выражение - нулевой указатель, правильнее сказать указатель типа 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);

}

 

В результате я получил два идентичные результата...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что мне всегда нравилось в определяющих Си стандартах - так это полное отсутствие логики. Как в данном случае - NULL это с какого-то бодуна всегда 0 и ничего более. Куда катится мир? :crying:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Что мне всегда нравилось в определяющих Си стандартах - так это полное отсутствие логики.
Если их не читать - то так и будет казаться. По определению Стандарта NULL - указатель, который не указывает ни на один объект. Не "нулевой указатель", не "указатель на ноль", а именно "указатель, который указывает в никуда". Конкретное его значение отдано на откуп компилятору.

 

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

 

В С++ разрешено только неявное приведение любого указателя к void *. И там добавлено неявное преобразование константы 0 к указателю на любой тип, тоже в смысле "указатель, который указывает в никуда". Какое именно значение будет занесено в указатель в результате приведения константы 0 не оговаривается. А макрос NULL в C++ изначально был введен только для совместимости исходников с С. В новом стандарте его вводят снова - с константой 0 возникают неоднозначности c перегрузкой операторов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

По определению Стандарта NULL - указатель, который не указывает ни на один объект. Не "нулевой указатель", не "указатель на ноль", а именно "указатель, который указывает в никуда". Конкретное его значение отдано на откуп компилятору.

Я о C99, aaarrr приводил цитату.

Да, указатель, который указывает в никуда - это единственно правильная формулировка. И если, допустим, мне в арме надо отловить обращение в никуда (как запись так и чтение), я 0 использовать не буду. Равно как и буду писАть if(ptr != NULL) вместо if(ptr)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, указатель, который указывает в никуда - это единственно правильная формулировка. И если, допустим, мне в арме надо отловить обращение в никуда (как запись так и чтение), я 0 использовать не буду. Равно как и буду писАть if(ptr != NULL) вместо if(ptr)

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

 

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

 

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

 

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...