Почему C++ позволяет мне назначить const char для const char *?!
к моему удивлению, это компилирует:
const char* c_str()
{
static const char nullchar = '';
return nullchar;
}
и это ввело ошибку в мой код. К счастью, я его поймал.
это намеренно C++, или ошибка компилятора? Есть ли причина, по которой тип данных активно игнорируется?
Он работал в Visual C++ 2010 и GCC, но я не понимаю, почему это должно работать, учитывая очевидное несоответствие типов данных. (Тег static тоже не нужно.)
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 может использоваться любым типом для установки пустого значения, независимо если это массив, указатель или переменная.