Ответ 1
Из стандарта:
const char c = 'c';
char* pc;
const char** pcc = &pc; // not allowed
*pcc = &c;
*pc = 'C'; // would allow to modify a const object
Законно преобразовывать указатель-не-const в указатель-на-const.
Тогда почему нет права конвертировать указатель на указатель на не-const на указатель на указатель на const?
Например, почему следующий код является незаконным:
char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char **ps = a; // error!
Из стандарта:
const char c = 'c';
char* pc;
const char** pcc = &pc; // not allowed
*pcc = &c;
*pc = 'C'; // would allow to modify a const object
Игнорируя свой код и отвечая на принцип вашего вопроса, см. Эту запись в comp.lang.c FAQ: Почему я не могу передать char ** функции, которая ожидает const char **?
Причина, по которой вы не можете присвоить
char **
значение вconst char **
указателя несколько неясна. Учитывая, чтоconst
существует вообще, компилятор хотел бы помочь вам сохранить ваши обещания не изменять значенияconst
. Вот почему вы можете назначитьchar *
дляconst char *
, но не наоборот: он явно безопасен для "добавления"const
-ness к простому указателю, но было бы опасно его убрать. Однако предположим, что вы выполнили следующую более сложную серию заданий:const char c = 'x'; /* 1 */ char *p1; /* 2 */ const char **p2 = &p1; /* 3 */ *p2 = &c; /* 4 */ *p1 = 'X'; /* 5 */
В строке 3 мы присваиваем
char **
const char **
. (Компилятор должен жаловаться.) В строке 4 мы присваиваемconst char *
const char *
; это явно легально. В строке 5 мы модифицируем то, что указываетchar *
- это должно быть законным. Однакоp1
заканчивается, указывая наc
, который являетсяconst
. Это произошло в строке 4, потому что*p2
действительно былp1
. Это было установлено в строке 3, которая является назначением формы, которая запрещена, и именно поэтому строка 3 запрещена.
И как ваш вопрос помечен C++, а не C, это даже объясняет, что const
отборочные использовать вместо:
(C++ имеет более сложные правила для назначения указателей со знаком const, которые позволяют вам делать больше видов назначений без возникновения предупреждений, но все же защищать от непреднамеренных попыток изменения значений const. C++ все равно не позволит назначить
char **
const char **
, но это позволит вам уйти с назначениемchar **
вconst char * const *
.)
С тех пор, как никто не опубликовал решение, здесь:
char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char * const*ps = a; // no error!
(http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17 для почему)
С++ 11 draft standard объясняет это в примечании в разделе 4.4
, в котором говорится:
[Примечание: если программа может назначить указатель типа T ** указателю типа const T ** (то есть, если строка 1 ниже была разрешена), программа может непреднамеренно изменить объект const (как это делается в строке # 2). Например,
int main() { const char c = 'c'; char* pc; const char** pcc = &pc; // #1: not allowed *pcc = &c; *pc = 'C'; // #2: modifies a const object }
-end note]
Интересный родственный вопрос: Учитывая, что int ** p1 и const int ** p2 p1 == p2 хорошо сформирован?.
Обратите внимание, что Часто задаваемые вопросы по С++ также содержит объяснение, но мне больше нравится описание из стандарта.
Соответствующий текст, который идет с примечанием, выглядит следующим образом:
Преобразование может добавлять cv-квалификаторы на уровнях, отличных от первого в многоуровневые указатели, подчиненные следующим правилам: 56
Два типа указателей T1 и T2 аналогичны, если существует тип T и целое число n > 0 такое, что:
T1 - это cv1,0 указатель на указатель cv1,1 на ··· cv1, n-1 указатель на cv1, n Т
и
T2 - это cv2,0 указатель на указатель cv2,1 на указатель · cv2, n-1 на cv2, n T
где каждый cvi, j является const, volatile, const volatile или ничего. n-кортеж cv-квалификаторов после первого в типе указателя, например, cv1,1, cv1,2,..., cv1, n в типе указателя T1, называется cv-квалификационная подпись типа указателя. Выражение типа T1 можно преобразовать в тип T2 тогда и только тогда, когда выполняются следующие условия выполнены:
- типы указателей похожи.
- для каждого j > 0, если const находится в cv1, j, то const находится в cv2, j и аналогично для volatile.
- если cv1, j и cv2, j различны, то const находится в каждом cv2, k для 0 < k < к.
Здесь есть два правила:
T*
и U*
, если T и U - разные типы.T*
до T const *
неявно. ( "указатель на T" можно отнести к "указателю на const T" ). В С++, если T
также является указателем, это правило также может применяться к нему (цепочка).Итак, например:
char**
означает: указатель на char.
И const char**
означает: указатель на указатель на const char.
Так как указатель на char и указатель на const char - это разные типы, которые не отличаются только константой, поэтому приведение не допускается, Правильный тип приведения должен быть const указателем на char.
Итак, чтобы оставаться const корректным, вы должны добавить ключевое слово const, начиная с самой правой звездочки.
Итак char**
может быть добавлено к char * const *
и может быть добавлено к const char * const *
тоже.
Это цепочка только С++. В C это цепочка не работает, поэтому на этом языке вы не можете корректно наложить более одного уровня указателей const.