Intereting Posts
char * адрес переменной по отношению к char адрес переменной чтение изменений stdout от небуферизованной до строки, буферизованной в каноническом режиме Как вставить нули между битами в bitmap? Переносимость битовых полей Ошибка команды Linker с кодом выхода 1 В Xcode Тип неподписанных битовых полей: int или unsigned int Функция C isupper () возвращает x с n битами, которые начинаются с позиции p, установленной в крайние правые n бит y, оставляя другие биты неизменными Является ли адрес памяти, возвращаемый malloc, всегда взаимозаменяемым указателем на другой тип? Понимание неопределенного поведения для двоичного streamа с использованием fseek (файл, 0, SEEK_END) с файлом Диапазоны типа данных с плавающей точкой в ​​C? Вызов функций C с языка ассемблера x86 Какие технические недостатки имеют VLA в стиле C99? printf вывод мусора вместо конкретных символов Как переместить курсор в первую строку в C?

константы и указатели на указатели

Я очень смущен ключевым словом const . У меня есть функция, принимающая массив строк в качестве входного параметра и функцию, принимающую переменное количество аргументов.

 void dtree_joinpaths(char* output_buffer, int count, ...); void dtree_joinpaths_a(char* output_buffer, int count, const char** paths); 

dtree_joinpaths внутренне вызывает dtree_joinpaths_a после того, как он построил массив строк из списка аргументов.

 void dtree_joinpaths(char* output_buffer, int count, ...) { int i; va_list arg_list; va_start(arg_list, count); char** paths = malloc(sizeof(char*) * count); for (i=0; i < count; i++) { paths[i] = va_arg(arg_list, char*); } va_end(arg_list); dtree_joinpaths_a(output_buffer, count, paths); } 

Но gcc компилятор сообщает мне следующее сообщение об ошибке:

 src/dtree_path.c: In function 'dtree_joinpaths': src/dtree_path.c:65: warning: passing argument 3 of 'dtree_joinpaths_a' from incompatible pointer type 

Когда я изменяю char** paths = malloc(count); to const char** paths = malloc(count); , эта ошибка больше не появляется. Я не понимаю, что

  1. Я думал, что указатель на адрес всегда может быть отброшен в указатель const, но не наоборот (это то, что происходит здесь imo).
  2. Этот пример работает: http://codepad.org/mcPCMk3f

Что я делаю неправильно, или где мое недоразумение?


редактировать

Я намерен сделать память входных данных неизменной для функции. (в этом случае параметр paths ).

Причина char ** -> const char** – это «опасное» преобразование, это следующий код:

 const char immutable[] = "don't modify this"; void get_immutable_str(const char **p) { *p = immutable; return; } int main() { char *ptr; get_immutable_str(&ptr); // <--- here is the dangerous conversion ptr[0] = 0; } 

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

C не запрещает преобразование, но gcc предупреждает вас, что это плохо. FYI, C ++ запрещает преобразование, он имеет более строгую безопасность, чем C.

Я бы использовал строковый литерал для примера, за исключением того, что строковые литералы в C «опасны» для начала - вам не разрешено изменять их, но они имеют тип array of of char а не array-of- const char . Это по историческим причинам.

Я думал, что указатель на адрес всегда может быть отправлен в указатель const

Указатель-не-const-T может быть преобразован в указатель-на-const-T. char ** -> const char** не является примером этого шаблона, потому что если T является char * то const T является char * const , а не const char * (в этот момент, вероятно, стоит не писать const слева no: write char const * и вы не будете ожидать, что он будет таким же, как T const где T - char * ).

Вы можете безопасно преобразовать char ** в char * const * и (по причинам, которые требуют немного больше, чем просто простое правило), вы можете безопасно преобразовать char ** в char const * const * .

Ключ состоит в том, что указатель не является константой. Чтобы объявить указатель const, используйте char *const ptr; или объявить const-указатель на указатель const, char *const *const ptr; , const char **ptr – указатель на указатель на const char .

На самом деле, если есть функция, которая принимает const char ** и вы передаете char **, это может привести к проблемной ситуации и наоборот.

В вашем конкретном случае вы ожидаете, что память неизменна, но она не является неизменной и может измениться в любое время. В многопоточной среде вы ожидаете, что эта память будет streamобезопасной, и пока она живет в стеке или куче, вам не нужен мьютекс для доступа к нему.

Все это ориентировано на предотrotation ошибок, но если вы уверены, что это не приведет к ошибке, вы можете просто направить указатель на const char **.

Вы не можете передавать char ** в const char ** потому что компилятор не может гарантировать корректность const .

Предположим, что у вас есть следующий код (и он скомпилирован):

 void foo(const char **ppc, const char* pc) { *ppc = pc; // Assign const char* to const char* } void bar() { const char c = 'x'; char* pc; foo(&pc, &c); // Illegal; converting const char* to const char**. Will set p == &c *pc = 'X'; // Ooops! That changed c. } 

См. Здесь для того же примера без вызовов функций.