Почему 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 может использоваться любым типом для установки пустого значения, независимо если это массив, указатель или переменная.