Приведение указателя const void к массиву указателей const char правильно в C

Asked
Viewd11896

6

У меня есть кусок кода C, который выглядит так:

 const char (*foo)[2] = bar();
 

Теперь bar() - это функция, которая возвращает (const void *). Как правильно преобразовать этот указатель const? Код выдает это предупреждение от GCC:

 "initialization discards qualifiers from pointer target type".   
 

Вот некоторые из моих неудачных попыток:

 const char (*foo)[2] = (const char *)bar();
const char (*foo)[2] = (const void **)bar();
 

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

РЕДАКТИРОВАТЬ: Было предложено:

 const char (*foo)[2] = (const char (*)[2])bar();
 

Кажется, что это правильно, но GCC выдает следующее предупреждение:

 "cast discards qualifiers from pointer target type"   
 

что почти идентично исходному предупреждению.

РЕДАКТИРОВАТЬ 2: Хорошо, я думаю, что понял. Настоящая проблема здесь - это определение ( const void * ) для bar(). const в определении (const char( * )[2]) относится к элементам массива, а не к указателю на массив. Это определение типа по сути является массивом, который, когда он представлен указателем void, является не const. Реальный ответ заключается в том, что ( const void * ) теряет свою cons-сущность при преобразовании в (const char ( * )[2]).

  • Проблема заключается в необходимости преобразования в массив типа const (а не в массив const).Первое невозможно в C (хотя они могут быть одинаковыми, отсутствие подъема, возможно, является ошибкой в стандарте).См. Мой ответ ниже по соответствующим ссылкам.

    Jed08 ноября 2009, 16:10
  • Какую версию gcc и какие предупреждения вы используете?

    CB Bailey08 ноября 2009, 11:24
  • В заголовке вашего вопроса написано «массив константных указателей на символы», но вы присваиваете указатель на массив из двух символов.Можете уточнить, какой именно бар должен возвращаться?

    CB Bailey08 ноября 2009, 11:19
  • Должно быть новым в 4.4.1, я не получаю предупреждения с 4.4.0 -Wall -Wextra.

    CB Bailey08 ноября 2009, 11:29

5 ответов

6

Некоторые другие указали правильное приведение, но оно генерирует ложное предупреждение а>. Это предупреждение связано с возможной ошибкой в стандарте C, или (в зависимости от вашей интерпретации) случай, который GCC должен рассматривать специально. Я считаю, что квалификатор const можно безопасно и однозначно поднять до типа массива. Вы можете сбросить это предупреждение с помощью -Wno-cast-qual, но, конечно, это исключит предупреждения для случаев, которые вас действительно волнуют.

Чтобы уточнить, тип const char (*)[2] означает «указатель на массив (длина 2) из ​​const char». Массив не имеет маркировки const, только элементы массива. По сравнению с типом const void * компилятор замечает, что последний является указателем на const, а первый - нет, таким образом генерируя предупреждение. Стандарт C не дает возможности пометить массив как const, даже если массив const будет эквивалентен массиву const.

  • @haccks Выдает предупреждение с gcc -Wcast-qual.

    Jed13 февраля 2016, 01:45
1

Замечание, вам не нужны размеры массива:

 const void *bar() { 
    static const char a[10] = "abcdefghij";
    return &a[4];
}

int main() {
    const char (*foo)[2] = (const char (*)[])bar();
    return 0;
}
 

Поскольку некоторым может быть трудно это прочитать:

 cdecl> explain const char (*foo)[2]
declare foo as pointer to array 2 of const char
 
  • Массивы обычно передаются указателями на их первый элемент, поэтому const char(*foo)[2] может указывать на массив массивов из 2 символов.Или более знакомый пример: char* завершенная строка обычно возвращается как char(*)[], а не 12321.

    CB Bailey08 ноября 2009, 12:15
  • Если он знает , что возвращаемый указатель фактически возвращает указатель на массив массивов из 2 символов, а не просто указатель на массив из 2 символов, то, сохраняя размеры массива, он можетвыполните арифметические действия с указателем на foo.

    CB Bailey08 ноября 2009, 11:32
  • Думаю, хороший момент, но это было бы полезно только в том случае, если бы у него был двумерный массив этих вещей, выделенный в непрерывном блоке памяти, и он знает из какого-то другого источника границы этого двумерного массива относительно возвращаемого указателя.В этом случае маловероятно.С другой стороны, пропуск измерения, вероятно, ничего ему не даст, я полагаю, это в основном пустяки.Кстати, он возвращает указатель на массив из 2 символов, а не на «массив массивов из 2 символов».Это будет const char (*foo)[][2] или, как говорит cdecl: объявить foo как указатель на массив массива 2 const char

    Robert S. Barnes08 ноября 2009, 11:50
3

Попробуйте:

 const char (*foo)[2] = (const char (*)[2])bar();
 

Изменить , но если bar возвращает указатель на массив констант из символьных указателей в качестве подсказок в заголовке вашего вопроса, не должно быть необходимости в приведении, если вы присваиваете переменную этого типа:

 char* const* foo = bar();
 
3

Проблема с предупреждением в последней версии трансляции имеет исторические корни. Вы знаете, что язык C (а также C ++) правильно запрещает преобразование T** -> const T**. Это правильно, поскольку разрешение этого преобразования откроет путь для некоторых тонких нарушений правил корректности констант (их можно найти в любом уважающем себя FAQ).

Однако язык C также запрещает преобразование T** -> const T* const*. Это отличается от C ++, который позволяет это преобразование. (Это преобразование не нарушает константную корректность.) Это долгое время считалось «дефектом дизайна» в спецификации языка C. Этот дефект был «исправлен» в C ++, но продолжает сохраняться в C (даже в C99). Честно говоря, понятия не имею, почему это осталось без изменений в C99. Одним из "последствий" этого дефекта (или, точнее, такого подхода к лечению константной корректности) является то, что в языке C преобразование T (*)[N] -> const T (*)[N] также остается запрещенным, даже если оно не несет в себе неотъемлемых угроз константной корректности.

Я не могу воспроизвести предупреждение, которое вы получаете с моей версией GCC. Но если вы это понимаете, то это просто еще одно следствие той же идеологии. Если учесть, что преобразование было запрошено явным оператором приведения, предупреждение GCC совершенно неоправданно. Вы можете попытаться обойти это предупреждение, используя цепное приведение

 const char (*foo)[2] = (const char (*)[2]) (void *) bar();
 
0
 const char (*foo)[2] = (const char (*)[2])bar();