2015-02-28 2 views
2

Вот предупреждение компиляции у меня есть:ожидаемый ожидаемый «Const символ **», но аргумент типа «символ **»

src/Debugger.c:219:52: warning: passing argument 2 of ‘Debugger_Command[i].Callback’ from incompatible pointer type 
       Debugger_Command[i].Callback(argc, argv); 
                ^
src/Debugger.c:219:52: note: expected ‘const char **’ but argument is of type ‘char **’ 

Вот соответствующий исходный код:

/* Definition */ 
typedef void (*Debugger_Callback_t)(int argc, char const * argv[]); 
typedef struct tagDebugger_Command_t { 
    /* ... */ 
    Debugger_Callback_t Callback; /**< Callback */ 
} Debugger_Command_t; 
Debugger_Command_t const Debugger_Command[] = { /* ... */ } 

/* Use of the callback where the warning occurred */ 
char *argv[DEBUGGER_ARG_COUNT]; 
Debugger_Command[i].Callback(argc, argv); 

Что не так с передачей не const-переменной в качестве параметра const? Насколько я понимаю, это необходимо, чтобы строка не была изменена внутри функции. Итак, почему возникает предупреждение?

Компилятор: GCC версии 4.9.2 (GCC) на Windows/Cygwin

ответ

6

Потому что технически вы можете разорвать обещание, передав неконстантную указатель на константный указатель параметра.

«Опасно отнимать константный-Несс»

http://c-faq.com/ansi/constmismatch.html

Вот содержание приведенной выше ссылке:

Вы можете использовать указатель на T (для любой тип T), где ожидается указатель на const-T. Однако правило (явное исключение), которое допускает небольшие несоответствия в квалифицированных типах указателей, не применяется рекурсивно, а только на верхнем уровне. (const char ** является указателем на указатель на const-char, и поэтому исключение не применяется.)

Причина, по которой вы не можете присвоить значение char ** указателю const char ** несколько неясным. Учитывая, что спецификатор const существует вообще, компилятор хотел бы помочь вам сохранить ваши обещания не изменять значения const. Вот почему вы можете назначить char * для const char *, но не наоборот: явно безопасно «добавить» константу к простому указателю, но было бы опасно забрать его. Однако предположим, что вы выполнили следующую более сложную серию заданий:

const char c = 'x'; /* 1 */ 
char *p1;    /* 2 */ 
const char **p2 = &p1; /* 3 */ 
*p2 = &c;    /* 4 */ 
*p1 = 'X';    /* 5 */ 

В строке 3 мы задаём полукокс ** к константному гольцу **. (Компилятор должен жаловаться.) В строке 4 мы присваиваем const char * const char *; это явно легально. В строке 5 мы модифицируем то, что указывает символ *, - это должно быть законным. Однако p1 заканчивается, указывая на c, который является константой. Это произошло в строке 4, потому что * p2 действительно был p1. Это было установлено в строке 3, которая является назначением формы, которая запрещена, и именно поэтому строка 3 запрещена.

Назначение символа char ** const char ** (как в строке 3, так и в исходном вопросе) не является непосредственной опасностью. Но это создает ситуацию, в которой обещание p2 - то, что значение в конечном счете не будет изменено, не может быть сохранено.

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

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

+0

Это довольно хороший пример, который вы мне дадите.Я думаю, что кастинг - мое единственное решение здесь. – Phong

+1

Я уверен, что все согласятся с тем, что опасно отбирать сочувствие. Однако этот случай является добавлением константы. В любом случае, связанный FAQ дает ответ. Было бы полезно привести суть этого ответа в этот ответ, чтобы этот ответ не был ответом «только для ссылок». –

+0

@CraigMcQueen: Спасибо за комментарий. (Я могу опаздывать), но я обновил ответ, чтобы включить содержание ссылки – Phong

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