GCC: приведение константных указателей к константному указателю массива typedef с предупреждением -Wcast-qual throws - PullRequest
0 голосов
/ 03 февраля 2019

РЕДАКТИРОВАТЬ: проблема объясняется более подробно здесь (спасибо @Eric Postpischil).Кажется, это ошибка в GCC.

Сначала позвольте мне начать с некоторого контекста: код, который я пишу, использует API, который я не могу изменить, в версии GCC, которую я не могу изменить,с флагами компиляции мне не разрешено удалять, и когда я закончу с этим, у него должно быть ровно ноль предупреждений или # прагм.

РЕДАКТИРОВАТЬ: также нет союзов.Система сборки также использует -Wall -ansi -pedantic и любые другие предупреждения под солнцем.Я подтвердлю версию GCC завтра, но я почти уверен, что она не выше GCC 7. Тем временем я тестирую с GCC 6.3.

EDIT3: я отмечаю проблему как «отвеченную».Для полноты картины я добавляю немного дополнительной информации ниже:

Я проверил используемую версию компилятора, и она не очень хороша.Мы используем Mingw, и gcc.exe --version сообщает мне, что это GCC 3.4.5.

Кроме того, флаги компиляции включают wall wextra wcast-qual wpointer-arith wconversion wsign-conversion наряду с другими, которые не имеют отношения к рассматриваемой проблеме.

Проблема

Рассмотрим следующий код:

#include "stdio.h"
#include "stdint.h"

typedef uint32_t MyType[4];

const MyType* foo(const uint8_t* a)
{
    return (const MyType*) a;
}

void myapi_foo(const MyType* d) {}

int main()
{
    uint8_t a[4*sizeof(uint32_t)];

    const MyType* b = foo((const uint8_t*) a);

    myapi_foo(b);

    return 0;
}

Скомпилированный с GCC и флагом -Wcast-qual, этот код выдаст следующее предупреждение:

предупреждение: приведение отбрасывает квалификатор 'const' из целевого типа указателя [-Wcast-qual] return (const MyType *) a;

EDIT: чтобы уточнить, ошибка в этой строке:

return (const MyType*) a;

Причина проблемы

Я знаю, что основной причиной проблемы является typedef type MyType, который на самом деле является массивом.К сожалению, я не могу позволить себе роскошь изменять этот typedef, а также функцию API myapi_foo и сомнительный выбор типа параметра.Честно говоря, я не совсем понимаю, , почему компилятор так недоволен этим исполнением, поэтому разъяснения более чем приветствуются.

Вопрос

Что будет самый чистый способ указания компилятору, что все должно рассматриваться как указатель на постоянные данные?

Отклоненные и потенциальные решения

Вот несколько «решений», которые янашел, но оставил меня неудовлетворенным:

  • Снимите флаг -Wcast-qual .Я не могу этого сделать из-за правил качества кода.
  • Добавьте #pragma, чтобы отключить предупреждение вокруг этой части кода (как показано здесь ).Точно так же мне не разрешено это делать.
  • Привести указатель к целому числу, затем привести обратно к указателю (как показано здесь ) return (const MyType*) (uint32_t) a;.Это очень грубо, но использование uint32_t в качестве адресов памяти имеет прецедент в этом проекте, поэтому мне, возможно, придется использовать его в качестве последней попытки.
  • EDIT: @bruno предложил использовать объединениеобойти проблему.Это портативное и довольно элегантное решение.Однако вышеупомянутые правила качества кода прямо запрещают использование союзов.
  • EDIT: @Eric Postpischil и @MM предложили использовать (const void *) приведение return (const void*) a;, которое будет работать независимо от значения sizeof(MyType*).К сожалению, это не работает на цель.

Спасибо за ваше время.

Ответы [ 3 ]

0 голосов
/ 03 февраля 2019

Это ошибка GCC 81631 .GCC не может распознать приведение к const MyType * и сохраняет квалификатор const.Это может быть связано с тем, что в этом «указателе на массив из четырех const uint32_t» GCC выполняет проверку того, является ли массив const, или чем элементы массива const.

В некоторыхВерсии GCC, включая 8.2, обходной путь должен измениться:

return (const MyType*) a;

на:

return (const void *) a;

Более радикальное изменение, которое, вероятно, будет работать в большем количестве версий, должно использовать:

return (const MyType *) (uintptr_t) a;

Примечание о преобразовании и псевдониме:

Возможно, проблема в том, что этот код передает a функции, которая преобразует его в const MyType *:

uint8_t a[4*sizeof(uint32_t)];

const MyType* b = foo((const uint8_t*) a);

Во многих реализациях C MyType, являющийся массивом uint32_t, потребует четырехбайтовое выравнивание, но a потребует только однобайтовое выравнивание.Согласно C 2018 6.3.2.3 6, если a неправильно выровнено для MyType, результат преобразования не определен.

Кроме того, этот код предполагает, что массив uint_t aможет использоваться как массив из четырех uint32_t.Это нарушило бы правила псевдонимов C.Код, который вы показываете в вопросе, выглядит как образец, а не как реальный код, поэтому мы не можем быть уверены, но вы должны это учитывать.

0 голосов
/ 03 февраля 2019

Если ничего не работает, вы можете использовать uintptr_t молот , если это дает имплментация.Стандарт C11 является необязательным:

const MyType* foo(const uint8_t* a)
{
    uintptr_t uip = (uintptr_t) a; 

    return (const MyType*) uip;
}
0 голосов
/ 03 февраля 2019

Вы можете сделать это:

const MyType* foo(const uint8_t* a)
{
    union {
      const uint8_t* a;
      const MyType* b;
    } v;

    v.a = a;
    return v.b;
}

wc , являющийся вашим измененным файлом:

pi@raspberrypi:/tmp $ gcc -pedantic -Wall -Wcast-qual w.c
pi@raspberrypi:/tmp $ 

Это работает независимо от компилятора (нет # pragma) или соответствующий размер int и указателя (без приведения между int и указателем), но я не уверен, что это очень элегантно; -)

Странно иметь этот foo функция и одновременно компилируется с Wcast-qual, это противоречиво


Редактировать, если вы не можете использовать union , вы также можете сделать это

const MyType* foo(const uint8_t* a)
{
    const MyType* r;

    memcpy(&r, &a, sizeof(a));
    return r;
}

Сборник:

pi@raspberrypi:/tmp $ gcc -pedantic -Wall -Wcast-qual w.c
pi@raspberrypi:/tmp $ 
...