Подтвердить что ты не робот

Почему не является законным преобразовывать "указатель на указатель на не-const" в "указатель на указатель на const",

Законно преобразовывать указатель-не-const в указатель-на-const.

Тогда почему нет права конвертировать указатель на указатель на не-const на указатель на указатель на const?

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

char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char **ps = a; // error!
4b9b3361

Ответ 1

Из стандарта:

const char c = 'c';
char* pc;
const char** pcc = &pc;   // not allowed
*pcc = &c;
*pc = 'C';                // would allow to modify a const object

Ответ 2

Игнорируя свой код и отвечая на принцип вашего вопроса, см. Эту запись в 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 *.)

Ответ 4

С++ 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 < к.  

Ответ 5

Здесь есть два правила:

  • Нет никаких неявных отливок между 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.