Почему я не могу конвертировать 'типа 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-корректность (C++ FAQ).



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


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



уточнения:

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

2) каков правильный способ принять этот символ** в качестве параметра, говоря (и имея компилятор принудительно), что я не буду изменять символы, на которые он указывает?
Например, если я хотел написать функцию:



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


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



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



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

770   6  

6 ответов:

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

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

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

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

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

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

void foo(const int*);

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

Если вы не уверены, почему код, который вы опубликовали, не является const-правильным, рассмотрите следующий код, только немного отличающийся от кода 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. Это не только нарушит проверку, которую вы попросили компилятор сделать, чтобы запретить вам изменять эти переменные значение -- это также позволит вам сломать оптимизацию компилятора!

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

Edit-add:

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

Edit-delete: удалены опечатка

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

Comments

    Ничего не найдено.