2008-09-16 3 views
55

Следующий фрагмент кода (правильно) дает предупреждение в C и сообщение об ошибке в C++ (с помощью GCC & г ++ соответственно, протестирована с версиями 3.4.5 и 4.2.1, MSVC, кажется, не волнует):Почему я не могу преобразовать 'char **' в 'const char * const *' в C?

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

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

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

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

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

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

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

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

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

+0

Я в замешательстве относительно вашего вопроса: «Как мне заставить C не предупредить меня?» или это «Как мне заставить C++ не бросать ошибку компиляции?» Или, может быть, это не так. – user7116 2008-09-16 22:55:12

+0

Я согласен с шестизначными переменными, этот вопрос нуждается в дополнительных разъяснениях. – 2008-09-16 23:00:18

+0

Вопрос: «Почему я не могу преобразовать« char ** »в« char * const *? »? – Kevin 2008-09-16 23:05:53

ответ

53

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

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

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

11

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

Вы уже определили проблему - этот код не сопз-правильно. «Const correct» означает, что, за исключением const_cast и C-style cast, удаляющих 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; 

неконстантных может конвертировать только сопзЬ типы, в частности, способы предотвращения обхода «const» в типе данных без явного приведения.

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

В некоторых компиляторах это напечатает «42», на некоторых «43» и другие, программа выйдет из строя.

Edit-дополню:

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

Edit-удаления: удалена опечатка

0

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

test.c:

#include <stdlib.h> 
#include <stdio.h> 
void foo(const char * const * bar) 
{ 
    printf("bar %s null\n", bar ? "is not" : "is"); 
} 

int main(int argc, char **argv) 
{ 
    char **x = NULL; 
    const char* const*y = x; 
    foo(x); 
    foo(y); 
    return 0; 
} 

Вывод с компиляции как код C: кл/ТС/W4/Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter 
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

Вывод с кодом компиляции как C++: кл/TP/W4/Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter 
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

Выход с НКУ: НКУ -Wall test.c

test2.c: In function `main': 
test2.c:11: warning: initialization from incompatible pointer type 
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type 

Выход с г ++: г ++ -Wall test.c

не выход

0

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

const volatile int *const serial_port = SERIAL_PORT; 

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

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

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

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

9

Чтобы считаться совместимым, указатель источника должен быть 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++).

1

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

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

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

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

Смежные вопросы