Подтвердить что ты не робот

Почему передача char ** как const char ** генерирует предупреждение?

Я получаю это предупреждение:

note: expected ‘const char **’ but argument is of type ‘char **’

Теперь я передаю аргументы, переведя их в const char **. Есть ли другой способ избавиться от него?

4b9b3361

Ответ 1

Короткий ответ

Можете ли вы безопасно отображать char ** до const char**? Нет. (В любом случае, это безопасно), и причина гораздо более тонкая, чем вы думаете. Можете ли вы избавиться от него по-другому? Конечно. Загрузите массив значений const char* из ваших значений char* и передайте их вместо этого. (или изменить прототип вызываемого, но это обманывает = P).

Рассмотрим следующий код, который по существу делает все, что вам нужно, за исключением вызова функции. Отмеченная строка демонстрирует эквивалентную точку трансляции

const char *s = "Test";
char *p = NULL;
char **pp = &p;             // Put address of our pointer in our pointer-to-pointer.
const char **cpp = pp;      // Here: assigning  char** to const char**
*cpp = s;                   // perfectly legal; pp and s both finish "char const"
*p = 0;                     // ru ro raggy

Требуется некоторое время, чтобы действительно посмотреть на это, и, по общему признанию, я тоже этого не видел. @sheu проделала прочную работу по поиску около 24 часов, прежде чем я действительно подумал об этом достаточно долго, чтобы понять, что он был прав все время (и я фактически поддержал этот ответ, прежде чем писать этот). Тогда я подумал, что он ошибался в то же время, когда думал, что его ответ не применим. Оказывается, мы оба ошибались в этом прыжке, потому что он был прав в первый раз, я ошибся во второй раз, а теперь... тьфу.

В VS2012 и VS2010 обе отмеченная строка будет отмечать ошибку без трансляции. clang будет скомпилировать его с предупреждением на C, но разрешить его (что мне показалось удивительным). Учитывая, что вам действительно нужно выйти из своего счастливого места, чтобы сломать его, но он по-прежнему не работает.

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


Длинная диатриба на указателях и константах

Предупреждение состоит в том, что char ** и const char ** не эквивалентны (duh). Чтобы быть правдой, вы можете исправить прототип (вызываемый) или исправить вызывающего абонента (путем загрузки массива const char * и передачи этого). Но можете ли вы безопасно прибегать к первому ко второму? Ммм....

Помните, что по стандарту const он сразу переходит к элементу слева. Объявление его на самом левом из типа данных - это тонкость, которую поддерживает язык, но часто вводит путаницу или проблемы. Как правило, если const появляется в крайнем левом углу объявления непосредственно перед типом, оно применяется к типу данных; не последующий указатель (если есть). Когда он появляется справа от всего, он применим к части объявления слева-слева, будь то часть типа данных или часть указателя, но независимо от того, что она применима только к одной части.

Далее следует множество примеров:

Без указаний:

const char ch;    // const character. must be initialized.
char const ch;    // same as above

Single-Косвенность:

char *p;               // p is mutable, *p is mutable
const char *p;         // p is mutable, *p is const
char const *p;         // same as above.
char *const p;         // p is const, *p is mutable, must be initialized.
char const *const p;   // p is const, *p is const, must be initialized.

Double Indirection:

char **p;        // ptr-to-ptr-to-char
                 // p, *p, and **p are ALL mutable

const char **p;  // ptr-to-ptr-to-const-char
                 // p and *p are mutable, **p is const

char const **p;  // same as above

char *const *p;  // ptr-to-const-ptr-to-char
                 // p is mutable, *p is const, **p is mutable.

char **const p;  // const-ptr-to-ptr-to-char
                 // p is const, *p is mutable, **p is mutable.
                 // must be initialized.

const char **const p;  // const-ptr-to-ptr-to-const-char
                       // p is const, *p is mutable, **p is const.
                       // must be initialized.

char const **const p;  // same as above

char const *const *p;  // ptr-to-const-ptr-to-const-char
                       // p is mutable, *p is const, **p is const.

const char *const *p;  // same as above.

char *const *const p;  // const-ptr-to-const-ptr-to-char
                       // p is const, *p is const, **p is mutable.
                       // must be initialized.

И, конечно, кто может уйти из дома без...

char const *const *const p;   // const-ptr-to-const-ptr-to-const-char
                              // everything is const.
                              // must be initialized.

const char *const *const p;   // same as above

Итак, как это влияет на ваш вопрос? При компиляции этого кода на C без приведения вы получите предупреждение компилятора (или ошибку при компиляции с помощью -Werror). При компиляции на С++ вы просто ошиблись, потому что подпись параметра не совпадает. Но почему?

Поскольку они не имеют прямой эквивалентности:

const char **p;  // ptr-to-ptr-to-const-char
                 // p and *p are mutable **p is const

char **p;        // ptr-to-ptr-to-char
                 // p, *p, and **p are all mutable

При компиляции с clang точное предупреждение в C указано как:

main.c: 15: 9: Передача char ** в параметр типа const char ** отбрасывает квалификаторы во вложенных типах указателей.

VS2010 и VS2012, с другой стороны, бросают ошибку:

ошибка C2440: 'initializing': невозможно преобразовать из 'char **' в 'const char **'

Кажется странным, но VS на самом деле более корректен (чудеса никогда не прекращаются).

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

Ответ 2

edit. Я даже ответил на неправильный вопрос. Мой ответ совершенно не имеет значения! Игнорируйте меня, пожалуйста.

отредактируйте 2: после того, как вопрос с джентльменом задает вопрос, выясняется, что мой ответ действительно имеет значение. C'est la vie.

Это забавный бит C, что имеет смысл, если вы достаточно серьезно думаете об этом.

В основном, преобразование:

char** ptr;
const char** const_ptr;
const_ptr = ptr;  // <-- BAD!

не разрешено.

Почему, спросите вы? "Я делаю вещи более const! Это, очевидно, хорошо!"


Хорошо, подумайте об этом. Если это разрешено, то:

const char c = 'A';
char* ptr;
const char** const_ptr = &ptr;  // <-- ILLEGAL, but what if this were legal?
*const_ptr = &c;
*ptr = 'B';  // <- you just assigned to "const char c" above.

БАМ, ты мертв. Итак... нет: -)

Ответ 3

Предупреждение сообщает вам, что функция, которую вы вызываете, ожидает данный параметр как const char**, но вы передаете параметр char**. Чтобы избавиться от этого предупреждения, вы могли

  • действительно перейдите в const char**
  • передайте свой параметр в const char** (как в настоящее время)
  • измените прототип функции так, чтобы функция ожидала char**