Почему я не могу конвертировать 'типа char*' в 'const чарса* константность*' в C?

следующий фрагмент кода (правильно) дает предупреждение на C и ошибку на C++ (используя gcc & g++ соответственно, протестированный с версиями 3.4.5 и 4.2.1; MSVC, похоже, не заботится):

char **a;
const char** b = a;

Я могу понять и принять это.
Решение этой проблемы на C++ состоит в том, чтобы изменить b на const char * const *, что запрещает переназначение указателей и предотвращает обход const-correctness (C++ FAQ).

char **a;
const char* const* b = a;

однако в чистом C исправленная версия (с использованием const char * const *) по-прежнему дает предупреждение, и я не понимаю, почему. Есть ли способ обойти это без использования гипсе?

уточнения:
1) Почему это создает предупреждение в C? Он должен быть полностью const-безопасным, и компилятор C++, похоже, распознает его как таковой.
2) каков правильный способ принять этот char** в качестве параметра, говоря (и наличие компилятора принудительно), что я не буду изменять символы, на которые он указывает? Например, если бы я хотел написать функцию:

void f(const char* const* in) {
  // Only reads the data from in, does not write to it
}

и я хотел вызвать его на char**, какой был бы правильный тип для параметра?

изменить: Спасибо тем, кто ответил, особенно тем, кто затронул этот вопрос и/или следил за моими ответами.

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

6 ответов


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

правила в C более просто изложены (т. е. они не перечисляют исключения, такие как преобразование char** до const char*const*). Consequenlty, это просто не допускается. Со стандартом C++ они включали больше правил, чтобы разрешить такие случаи.

В конце концов, это просто проблема в стандарте C. Я надеюсь, что следующий стандарт (или технический отчет) будет заниматься этим.


> однако, в чистом C, это все еще дает предупреждение, и я не понимаю, почему

вы уже определили проблему -- этот код не является const-правильный. "Const correct" означает, что, за исключением const_cast и C-style casts removing const, вы никогда не сможете изменить объект const с помощью этих указателей или ссылок const.

значение const-correctness -- const существует, в значительной степени, для обнаружения ошибок программиста. Если вы объявляете что-то как const, вы заявляя, что вы не думаете, что он должен быть изменен-или, по крайней мере, те, у кого есть доступ только к версии const, не должны быть в состоянии изменить его. Подумайте:

void foo(const int*);

Как объявлено, foo не имеет разрешение изменить число указывает ее аргумент.

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

char *y;

char **a = &y; // a points to y
const char **b = a; // now b also points to y

// const protection has been violated, because:

const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it 
         //     with &x which is const char* ..
         //     ..  so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const 
         //     variable.  oops!  undefined behavior!
cout << x << endl;

типы Non-const могут преобразование только в типы const в частности, чтобы предотвратить обход "const" в типе данных без явного приведения.

объекты, первоначально объявленные const, особенно особенные-компилятор может предположить, что они никогда не изменяются. Однако, если "b" может быть присвоено значение " a " без приведения, то вы можете непреднамеренно попытаться изменить переменную const. Это не только нарушит проверку, которую вы попросили компилятор сделать, чтобы запретить вам изменять эти переменные value -- это также позволит вам нарушить оптимизацию компилятора!

на некоторых компиляторах это будет печатать "42", на некоторых "43", а другие, программа будет сбой.

изменить-добавить:

HappyDude: ваш комментарий находится на месте. Либо c langauge, либо компилятор C, который вы используете, трактует const char * const * принципиально иначе, чем язык C++. Возможно, рассмотрите возможность отключения предупреждения компилятора для этой исходной строки только.

редактировать-удалить: удалены опечатка


чтобы считаться совместимым, указатель источника должен быть const на непосредственно переднем уровне косвенности. Таким образом, это даст вам предупреждение в GCC:

char **a;
const char* const* b = a;

но это не так:

const char **a;
const char* const* b = a;

альтернативно, вы можете бросить его:

char **a;
const char* const* b = (const char **)a;

вам понадобится тот же бросок, чтобы вызвать функцию f (), как вы упомянули. Насколько я знаю, в этом случае нет способа сделать неявное преобразование (за исключением C++).


это раздражает, но если вы хотите добавить другой уровень перенаправления, вы часто можете сделать следующее, Чтобы нажать вниз в указатель на указатель:

char c = 'c';
char *p = &c;
char **a = &p;

const char *bi = *a;
const char * const * b = &bi;

Он имеет немного другое значение, но обычно он работоспособен, и он не использует приведение.


Я не могу получить ошибку при неявном приведении char** к const char * const *, по крайней мере, на MSVC 14 (VS2k5) и g++ 3.3.3. GCC 3.3.3 выдает предупреждение,которое я не совсем уверен, что это правильно.


Я уверен, что ключевое слово const не означает, что данные не могут быть изменены/постоянны, только что данные будут обрабатываться только для чтения. Рассматривайте это:

const volatile int *const serial_port = SERIAL_PORT;

которой действует код. Как могут volatile и const сосуществовать? Простой. volatile говорит компилятору всегда читать память при использовании данных, а const говорит компилятору создать ошибку при попытке записи в память с помощью указателя serial_port.

помогает ли const оптимизатор компилятора? Нет. Нисколько. Поскольку константа может быть добавлена и удалена из данных посредством приведения, компилятор не может выяснить, действительно ли данные const постоянны (поскольку приведение может быть выполнено в другой единице перевода). В C++ у вас также есть ключевое слово mutable, чтобы еще больше осложнить ситуацию.

char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000

что происходит, когда делается попытка записать в память, которая действительно только для чтения (например, ROM), вероятно, вообще не определена в стандарте.