Приведение NULL к void в C++

Я наткнулся на загадочную запись:

if(ptr != (void)(NULL)) {
    //some action
}

Таким образом, он расширился до

if(ptr != (void)((void *)0)) {
    //some action
}

что выглядит как минимум странно. Есть ли в этом какое-то обоснование или это просто бессмысленно или даже неправильно? Однако он скомпилировался нормально (на linux/gcc, версию не помню).

-ИЗМЕНИТЬ-

Я проверил этот код сегодня, и вот новая информация: во-первых, приведение использовалось в макросе, и оно расширилось до

return (void)((void*)0);

внутри функции, возвращающей void. Код компилируется с использованием gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-50). Итак, эквивалентно ли это утверждение

return void;

что эквивалентно

return;

или есть что-то еще к этому?


person rubikonx9    schedule 02.03.2012    source источник
comment
Я удивлен, что это вообще скомпилировано - вы не должны иметь возможность сравнивать значения типа void. Вы уверены, что это не void *?   -  person templatetypedef    schedule 03.03.2012
comment
Абсолютно уверен, поэтому я озадачен.   -  person rubikonx9    schedule 03.03.2012
comment
да вообще не компилируется   -  person Nico    schedule 03.03.2012
comment
Не могли бы вы указать компилятор, который вы использовали? Это не компилируется в моей версии gcc.   -  person templatetypedef    schedule 03.03.2012
comment
Да, если это не скомпилируется, auto ptr = (void)((void *)0); Тогда вы что-то упускаете в своем вопросе   -  person Nico    schedule 03.03.2012
comment
Я получаю сообщение об ошибке, если пытаюсь сделать это с помощью gcc 4.3.2.   -  person Mr Lister    schedule 03.03.2012
comment
Извините, я действительно не знаю, я видел это на чьей-то машине, какой-то старый gcc, но человек, ответственный за код, был недоступен. Может быть, я смогу проверить это еще раз на следующей неделе, но я почти уверен, что так оно и было. Может я что-то не так делал или что-то еще переопределил...   -  person rubikonx9    schedule 03.03.2012
comment
Вероятно, опечатка, отсутствует *.   -  person Thomas Eding    schedule 03.03.2012
comment
Это было невозможно написать таким образом, значение типа «void» не может быть контекстуально преобразовано в «bool» или любой другой тип, если на то пошло.   -  person Nico    schedule 03.03.2012
comment
Тогда я дам ему еще один чек. Спасибо.   -  person rubikonx9    schedule 03.03.2012
comment
Так что, если вы уверены, что это не void*, возможно, это могло быть что-то очень похожее на void? Может vοіd?   -  person Mr Lister    schedule 03.03.2012
comment
@g.tsh: Итак, учитывая недавнее редактирование, похоже, что конструкция не использовалась при сравнении, а просто использовалась в возврате для функции void. Это правильно?   -  person Michael Burr    schedule 07.03.2012


Ответы (2)


Скорее всего, это должно быть приведение к недействительному указателю, который является таким же, как просто "указатель" (т. е. указатель на любой тип):

if (ptr != (void *)(NULL)) {
    ...
}

Хотя приведение излишни:

if (ptr != NULL) {
    ...
}

Ваш код даже не компилируется в моей системе (Mac с GCC 4.2.1), сообщение об ошибке, о котором сообщает GCC, — void value not ignored as it ought to be. Это ожидаемо, и потому что void означает отсутствие значения/типа вообще, и вы не можете сравнить это ни с чем другим.

Вот что говорит стандарт C99:

§6.3.2.2 недействителен

1 (Несуществующее) значение выражения void (выражение, имеющее тип void) никоим образом не должно использоваться, и к такому выражению не должны применяться неявные или явные преобразования (кроме void). Если выражение любого другого типа оценивается как пустое выражение, его значение или указатель отбрасываются. (Выражение void оценивается по его побочным эффектам.)

§6.5.9 Операторы равенства

Ограничения

2 Должен выполняться один из следующих условий:

  • оба операнда имеют арифметический тип;
  • оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов;
  • один операнд — указатель на объект или неполный тип, а другой — указатель на уточненную или неполную версию void; или
  • один операнд является указателем, а другой является константой нулевого указателя.

§6.3.2.2 уже запрещает использование void, и ограничения в §6.5.9 также будут нарушены.

Поэтому, если компилятор разрешит ptr != (void)(NULL), это нарушит стандарт C99. Это может быть просто ошибка или неправильная функция старой версии GCC.

Нашел только черновик для С89, а тут раздел void почти такой же, как и в С99. Но раздел об операторах равенства заставляет меня почесать голову. Кажется, это позволяет сравнивать с void, но это может быть ошибка в черновике или проблема с формулировкой:

§3.2.2.2 недействителен

(Несуществующее) значение выражения void (выражение, имеющее тип void) никоим образом не должно использоваться, и к такому выражению не должны применяться неявные или явные преобразования (кроме void ). Если выражение любого другого типа встречается в контексте, где требуется пустое выражение, его значение или обозначение отбрасываются. (Выражение void оценивается по его побочным эффектам.)

§3.3.9 Операторы равенства

Ограничения

Должно выполняться одно из следующих условий:

  • оба операнда имеют арифметический тип;
  • оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов;
  • один операнд является указателем на объект или неполный тип, а другой является квалифицированной или неквалифицированной версией void; или
  • один операнд является указателем, а другой является константой нулевого указателя.
person DarkDust    schedule 02.03.2012
comment
Это не объясняет, как он скомпилирован для OP. - person Mr Lister; 03.03.2012
comment
Возможно, это была ошибка/неправильная функция более ранней версии GCC, но даже это кажется сомнительным. Стандарт C99 §6.3.2.2 гласит: (Несуществующее) значение выражения void (выражение, имеющее тип void) не должно использоваться никоим образом, а неявные или явные преобразования (кроме void) не должны выполняться. применяется к такому выражению. Если выражение любого другого типа оценивается как пустое выражение, его значение или указатель отбрасываются. (Выражение void оценивается по его побочным эффектам.) Кроме того, тип void нарушил бы ограничения для оператора равенства в §6.5.9(2). - person DarkDust; 03.03.2012

Я проверил сборку для этого случая.

Имея такую ​​функцию:

void x() {
    return (void)(NULL);
}

Запуск препроцессора как: gcc x.c -E -o x.E -Wall -Wextra приводит к:

void x() {
    return (void)(((void *)0));
}

Что кажется довольно странным. Так или иначе, после компиляции с gcc x.c -o x -Wall -Wextra сборка такая (из objdump -d x):

0804841d <x>:
 804841d:   55                      push   %ebp
 804841e:   89 e5                   mov    %esp,%ebp
 8048420:   90                      nop
 8048421:   5d                      pop    %ebp
 8048422:   c3                      ret    

Это точно так же, как сборка для:

void x() {
    return;
}

Таким образом, эти два выражения эквивалентны.

Я использую gcc version 4.8.2 (2013) для 64-битного Linux, так что это не ошибка старой версии gcc.

Однако добавление -pedantic к gcc приводит к выдаче предупреждения:

предупреждение: ISO C запрещает «возврат» с выражением в функции, возвращающей void [-Wpedantic]

return (void)(NULL);

Это происходит для всех диалектов: c89, c99, gnu89, gnu99.

person rubikonx9    schedule 22.04.2015