LINUX.ORG.RU

NULL vs 0


0

0

Что курил автор цитаты ниже?
 
http://citforum.ru/programming/c_unix/index.shtml
Хрестоматия по программированию на Си в Unix, Андрей Богатырев

Цитата:

3.5.
Почему может завершаться по защите памяти следующая программа? 
    #include <sys/types.h>
    #include <stdio.h>
    time_t t;
    extern time_t time();
          ...
    t = time(0);
    /* узнать текущее время в секундах с 1 Янв. 1970 г.*/

Ответ: дело в том, что прототип системного вызова time() это: 
            time_t time( time_t *t );

то есть аргумент должен быть указателем. Мы же вместо указателя написали в качестве аргумента 0 (типа int). На машине IBM PC AT 286 указатель - это 2 слова, а целое одно. Недостающее слово будет взято из стека произвольно. В результате time() получает в качестве аргумента не нулевой указатель, а мусор. Правильно будет написать: 

            t = time(NULL);
      либо (по определению time())
                time( &t );

а еще более корректно так: 
            t = time((time_t *)NULL);

Мораль: везде, где требуется нулевой указатель, следует писать NULL (или явное приведение нуля к типу указателя), а не просто 0. 



anonymous

И приведенная цитата, и пост по ссылке на RSDN обе неверны, afaik.

Богатырев забывает, что если мы используем константу в качестве агрумента функции или инициализатора, то она кастится к этому типу. Поэтому положить на стек два байта вместо четырех в данном случае может только совсем сломанный компилятор. Нормальный ругнется в духе "warning: passing '0' as an argument to 'time' makes pointer from integer without a cast", но работать на большинстве современных архитектур это будет нормально. Другой вопрос, что NULL и 0 в _С_ (без крестов) - это разные вещи; NULL - это машинно-зависимая константа "невалидный указатель", которая может быть равна, скажем, 0xFFFFFFFF. И (size_t)NULL может не быть равен 0L.

Чувак с RSDN написал все верно, но только для плюсов, а не для С.

Смотри, например, /usr/include/linux/stddef.h:

#undef NULL

#if defined(__cplusplus)

#define NULL 0

#else

#define NULL ((void *)0)

#endif

anonymous
()
Ответ на: комментарий от anonymous

По словам Страуструпа (см. "Design & evolution"), можно смело писать 0 вместо NULL, даже если платформа использует ненулевое представление пустого указателя:

T *p = 0;

Просто неявно преобразуется.

hbee ★★★★
()
Ответ на: комментарий от anonymous

А чё будет если хидер time.h не подключено и прототип упомянутой time() не обявлен? Выделение стека под переменные вроде произходит на этапе компиляции а не линковки.

bugmaker ★★★★☆
()

Если объединить вышесказанное, то автор книжки почти прав. Если функция получает аргумент типа void*, ее прототип не описан (т.е. компилятор не может выполнить приведение типов) и sizeof(void*) != size(int), то действительно есть разница между просто нулем и NULL в качестве аргумента этой функции.

anonymous
()
Ответ на: комментарий от hbee

Т.е. вы хотите сказать, что printf("%p", 0); и printf("%p", NULL); это одно и то же?

Как здесь компилятор догадается, что надо 0 преобразовать к NULL-у?

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

умный может, а вообще

T* x = 0;

в С++ это то же, что и

T* x = NULL;

Везде, где использубтся указатели. Для printf второй и следующие параметры передаются как int (ЕМНИП) из-за varargs.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

> умный может

Даже если это не printf а my_printf ? Честно говоря, сомневаюсь.

Просто меня смущает утверждение "можно смело писать 0 вместо NULL". Мне кажется, что (даже в С++) это верно в большинстве случаев, но всё же не всегда. Хотя конечно функции с переменным числом параметров в С++ лучше использовать пореже.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

> Хотя конечно функции с переменным числом параметров в С++ лучше использовать пореже.

Их вообще не нужно использовать. Векторы, iostreams, перегрузка, значения по умолчанию с лихвой перекрывают все возможности "...".

hbee ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.