Каков особый статус значения 0 в c ++? - PullRequest
3 голосов
/ 26 мая 2020

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

Согласно этому - https://en.cppreference.com/w/cpp/language/integer_literal, тип целочисленных литералов либо int, long int, long long int, unsigned int, unsigned long int или unsigned long long int, с возможными исключениями, определяемыми реализацией c, если значение литерала не вписывается ни в одно из вышеперечисленных. Ни один из этих типов не может быть преобразован в void *, если только значение литерала не равно 0.

Различные компиляторы обрабатывают это по-разному. Например, рассмотрим следующие преобразования:

void g(void * p){}

void f(){
    int i = 0;
    void * p;
    // p = i; // Fails. Also for other integral types.

    p = 0; // Works. Also for 00, 0x0 and 0b0. Also when adding `u` and `l` suffixes.
    g(0); // Also works.    
    // g(1); // Fails.  

    // Amazingly, even this seems to work with gcc, icc and msvc, but not with clang:
    void * x = static_cast<int>(0);
    // These works for icc and msvc, but fails with gcc and clang
    p = static_cast<int>(0);
    g(static_cast<int>(0));
}

Что происходит «под капотом», что позволяет компиляторам выполнять эти int -> void * преобразования?


Редактировать : Конкретно вопрос в том, что об этом говорится в стандарте?

Ответы [ 2 ]

6 голосов
/ 26 мая 2020

Вопрос в том, почему это разрешено в соответствии со стандартом

Потому что должен быть способ express нулевого указателя. Разработчик языка C выбрал, что 0 будет нулевым. Разработчик C ++ решил быть совместимым с C, и поэтому 0 является константой нулевого указателя.

Позже в C ++ 11 nullptr было введено как новое ключевое слово. Интегральные константы нулевого указателя не могут быть заменены, потому что это нарушит обратную совместимость, поэтому эти различные способы express null сосуществуют. Нет причин использовать 0 в качестве нулевого указателя, если вам не нужно поддерживать системы до C ++ 11.

и, в частности, что разрешено

Standard говорит (последний черновик):

[conv.ptr] Константа нулевого указателя - это целочисленный литерал ([lex.icon]) с нулевым значением или prvalue типа std :: nullptr_t. Константа нулевого указателя может быть преобразована в тип указателя; результатом является значение нулевого указателя этого типа ([basi c .compound]), которое отличается от любого другого значения указателя на объект или типа указателя на функцию. Такое преобразование называется преобразованием нулевого указателя. Два значения нулевого указателя одного и того же типа должны сравниваться как равные. Преобразование константы нулевого указателя в указатель на тип с квалификацией cv является однократным преобразованием, а не последовательностью преобразования указателя, за которым следует квалификационное преобразование ([conv.qual]). Константа нулевого указателя интегрального типа может быть преобразована в prvalue типа std :: nullptr_t. [Примечание: результирующее значение prvalue не является значением нулевого указателя. - конец примечания]


Что происходит «под капотом», которое позволяет компиляторам выполнять эти преобразования int-> void *?

Компилятор анализирует источник. Грамматика говорит, что 0 - литерал. Компилятор рассматривает это как литерал 0 и, как таковой, позволяет преобразовать его в любой тип указателя в соответствии со стандартом.


// Amazingly, even this seems to work with gcc, icc and msvc, but not with clang:
void * x = static_cast<int>(0);

Это плохо сформировано, начиная с C ++ 11. Когда неправильно сформированная программа компилируется, это обычно либо потому, что

  1. это расширение языка, либо
  2. это ошибка компилятора, либо
  3. правильно сформирована в более старой версии языка, и компилятор нацелен на это

В этом случае, вероятно, это расширение языка.

// These works for icc and msvc, but fails with gcc and clang
p = static_cast<int>(0);
g(static_cast<int>(0));

Они также плохо сформированы, так как C ++ 11 . Я недостаточно знаю о i cc и msv c, чтобы сказать вам, являются ли эти случаи преднамеренными. Я рекомендую проверить их документацию по этому поводу.

3 голосов
/ 26 мая 2020

Что происходит «под капотом», что позволяет компиляторам выполнять эти int -> void * преобразования?

Magi c (или просто специальное правило, если Вы предпочитаете). Литерал 0 (а также 0u, 0l и другие варианты) особенный. Только когда эти точные токены появляются в источнике, компилятор считает преобразование в указатель действительным. Это не значение 0, это токен.

[conv.ptr] (выделено мной)

1 A константа нулевого указателя - это целочисленный литерал с нулевым значением или prvalue типа std​::​nullptr_­t. Константа нулевого указателя может быть преобразована в тип указателя; результатом является значение нулевого указателя этого типа, которое можно отличить от любого другого значения указателя на объект или типа указателя на функцию. Такое преобразование называется преобразованием нулевого указателя .

Целочисленный литерал имеет точное значение, как описано в [lex.icon] . Это спецификация токенов, которые могут появиться в исходном коде нашей программы. Приведенный выше абзац говорит нам, какой из этих токенов имеет особое значение.


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

6.3.2.3 Указатели

3 Целочисленное постоянное выражение со значением 0 или такое выражение, приведенное к типу void *, называется константой нулевого указателя. Если константа нулевого указателя преобразуется в тип указателя, результирующий указатель, называемый нулевым указателем, гарантированно не будет сравниваться с указателем на любой объект или функцию.

И то же самое было верно в C ++ до Проблема 903 CWG была решена в правило, которое мы имеем сегодня в C ++.

Если некоторые компиляторы смешивают свои C и C ++ logi c или не уловили разрешение отчета о дефектах, оно объяснит ваши наблюдения.

...