Почему C++ позволяет мне назначить const char для const char *?​!


к моему удивлению, это компилирует:

const char* c_str()
{
    static const char nullchar = '';
    return nullchar;
}

и это ввело ошибку в мой код. К счастью, я его поймал.

это намеренно C++, или ошибка компилятора? Есть ли причина, по которой тип данных активно игнорируется?
Он работал в Visual C++ 2010 и GCC, но я не понимаю, почему это должно работать, учитывая очевидное несоответствие типов данных. (Тег static тоже не нужно.)

8 70

8 ответов:

как вы его определили,nullchar - Это целочисленное константное выражение со значением 0.

стандарт C++03 определяет константу нулевого указателя как: "константа нулевого указателя является целочисленным константным выражением (5.19) rvalue целочисленного типа, которое вычисляет нуль.- Короче говоря, ваш nullchar - это константа нулевого указателя, то есть она может быть неявно преобразована и назначена практически любому указателю.

обратите внимание, что все эти элементы необходимы для этого однако неявное преобразование работает. Например, если вы использовали '' вместо '', или если бы у вас было не указано const классификатор для nullchar, вы не получите неявное преобразование-ваше задание не удалось бы.

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

это старая история: она восходит к С.

нет null ключевое слово в C. константа нулевого указателя в C либо:

  • интегральное постоянное выражение со значением 0, например 0,0L,'' (помните, что char является целочисленным типом),(2-4/2)
  • такое выражение, приведенное к void*, как (void*)0,(void*)0L,(void*)'',(void*)(2-4/2)

The NULLмакрос (не ключевое слово!) расширяется до такой константы нулевого указателя.

в первом проекте C++ в качестве константы нулевого указателя было разрешено только интегральное константное выражение. Недавно std::nullptr_t был добавлен в C++.

в C++, но не в C, a const переменная целочисленного типа инициализируется с неотъемлемой константным выражением является неотъемлемой константное выражение:

const int c = 3;
int i;

switch(i) {
case c: // valid C++
// but invalid C!
}

так const char инициализируется выражением '' указатель на null константа:

int zero() { return 0; }

void foo() {
    const char k0 = '',
               k1 = 1,
               c = zero();
    int *pi;

    pi = k0; // OK (constant expression, value 0)
    pi = k1; // error (value 1)
    pi = c; // error (not a constant expression)
}

и вы думаете, что это не звуковой дизайн языка?


обновлено для включения соответствующих частей стандарта C99... Согласно п. 6.6.6...

An целочисленное константное выражение должен иметь целочисленный тип и должен иметь только операнды это целочисленные константы, константы перечисления, символьные константы,sizeof выражения, результаты которых являются целочисленными константами и плавающими константы, которые являются непосредственные операнды приведений. Операторы приведения в целочисленном константном выражении должны быть только преобразование арифметических типов в целочисленные типы, за исключением как часть операнда в sizeof оператор.

некоторые разъяснения для C++-только программисты:

  • C использует термин " константа "для того, что программисты C++ знают как"литерал".
  • В C++, sizeof всегда время компиляции константа; но C имеет массивы переменной длины, поэтому sizeof иногда не константа времени компиляции.

затем мы видим, как государства §6.3.2.3.3...

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


чтобы узнать, сколько лет этой функции, см. идентичные зеркальные части в стандарт C99...

§6.6.6

An целочисленное константное выражение должен иметь целочисленный тип и должен иметь только операнды, которые являются целочисленными константами, константами перечисления, символьными константами, sizeof выражения, результаты которых являются целочисленными константами, и плавающие константы, которые являются непосредственными операндами приведений. Операторы приведения в целочисленном константном выражении должны преобразовывать только арифметические типы в целочисленные типы, кроме как в составе операнда в sizeof оператора.

§6.3.2.3.3

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

nullchar является (время компиляции -) постоянное выражение, со значением 0. Так что это честная игра для неявного преобразования в нулевой указатель.

более подробно: я цитирую из проект стандарта 1996 года здесь.

char является целочисленным типом. nullchar является const, поэтому это (время компиляции) интегральное константное выражение, согласно разделу 5.19.1:

5.19 постоянные выражения [expr.константный]

1 в нескольких местах C++ требует выражений, которые оцениваются в inte- gral или константа перечисления ... Неотъемлемой константное выражение может включать в себя ... постоянные переменные ...

кроме того, nullchar имеет значение 0, что позволяет неявно преобразовать его в указатель, как в разделе 4.10.1:

4.10 преобразования указателя [усл.ptr]

1 интегральное постоянное выражение ( expr.константный) rvalue целочисленного типа это значение равно нулю (называется константой нулевого указателя) может быть con- вертится к типу указателя.

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


обновлено с соответствующими частями (новее) C++03 норматив... Согласно п. 5.19.1...

An интегральное константное-выражение может включать только литералы (2.13), перечислители,const переменные или статические элементы данных целочисленных или перечислительных типов, инициализированные постоянными выражениями (8.5), параметры шаблона без типов целочисленных или перечислительных типов и sizeof выражения.

затем мы смотрим на §4.10.1...

A константа нулевого указателя является целочисленным константным выражением (5.19) rvalue целочисленного типа, которое равно нулю. Константа нулевого указателя может быть преобразована в тип указателя; результатом является нулевой указатель значение этого типа и отличается от любого другого значения указателя на объект или указателя на тип функции. Два значения нулевого указателя одного и того же типа должны сравниваться равными.

он компилируется по той же причине, что и этот компилируется

const char *p = 0; // OK

const int i = 0;
double *q = i; // OK

const short s = 0;
long *r = s; // OK

выражения справа имеют тип int и short, в то время как инициализируемый объект является указателем. Вас это удивляет?

в языке C++ (а также В C) интегральные постоянные выражения (ICEs) со значением 0 имеют особый статус (хотя ICEs определяются по-разному в C и c++). Они квалифицируются как константы нулевого указателя. Когда они используются в указателе контексты, они неявно преобразуются в нулевые указатели соответствующего типа.

тип char является целым типом, не сильно отличается от int в этом контексте, так

Он не игнорирует тип данных. Это не ошибка. Он использует преимущество const, которое вы туда помещаете, и видит, что его значение на самом деле является целым числом 0 (char-это целочисленный тип).

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

причины, по которым вы хотите, чтобы нулевой указатель имел некоторое значение указателя, которое "указывает в никуда" и может быть проверено (т. е. вы можете сравнить a нулевой указатель на целое число 0, и вы получите true в ответ).

Если вы отбросите const, вы получите сообщение об ошибке. Если вы поместите туда double (как и во многих других нецелочисленных типах; я думаю, исключения - это только типы, которые могут быть преобразованы в const char* [через перегрузку операторов преобразования]), вы получите ошибку (даже без const). И так далее.

все дело в том, что в этом случае ваша реализация видит, что вы возвращаете нулевой ptr константа; которую можно преобразовать в тип указателя.

похоже, что многие реальные ответы на этот вопрос оказались в комментариях. Подводя итог:

  • стандарт C++ позволяет const переменные интегрального типа, рассматриваемые как " интегральные постоянные выражения.- Но почему? Вполне возможно обойти вопрос о том, что C позволяет только макросам и перечислениям занимать место интегрального константного выражения.

  • переход (по крайней мере) до C89, интегральное постоянное выражение со значением 0 неявно преобразуется в (любой тип) нулевой указатель. И это часто используется в коде C, где NULL нередко #define ' d как (void*)0.

  • возвращаясь к K&R, буквальное значение 0 используется для представления нулевых указателей. Это соглашение используется повсюду, с таким кодом, как:

    if ((ptr=malloc(...)) {...} else {/* error */}
    

есть автоматическая рассылка. если вы хорошо запустите эту программу:

#include <stdio.h>
const char* c_str()
{
    static const char nullchar = '';
    return nullchar;
}

int main()
{
    printf("%d" , sizeof(c_str()));
    return 0;
}

выход хорошо быть 4 на моем компьютере - > размер указателя.

компилятор автоматически отбрасывает. обратите внимание, по крайней мере gcc дает предупреждение (я не знаю о VS)

Я думаю, что это может быть тот факт, что нулевой символ является общим между типами. То, что вы делаете, - это установка нулевого указателя при возврате нулевого символа. Это не удалось бы, если бы был использован любой другой символ, потому что вы не передаете адрес символа указателю, а значение символа. Null-это допустимый указатель и символьное значение, поэтому нулевой символ может быть установлен как указатель.

короче говоря, null может использоваться любым типом для установки пустого значения, независимо если это массив, указатель или переменная.