GCC:使用 -Wcast-qual 将 const 指针转换为数组 typedef 的 const 指针会抛出警告

GCC: Casting const pointers to const pointer of array typedef with -Wcast-qual throws warning

编辑:更深入地解释了问题(谢谢@Eric Postpischil)。这似乎是 GCC 中的一个错误。

首先,让我从一些上下文开始:我正在编写的代码使用的是 API 我无法更改,在 GCC 版本上我无法更改,带有编译标志我是不允许删除,当我完成它时必须有精确的零警告或#pragmas。

编辑:也没有工会。

EDIT2:假设构建系统也使用 -Wall -ansi -pedantic 和阳光下的所有其他警告。 我明天会确认 GCC 版本,但我相当确定它不会高于 GCC 7。同时我正在使用 GCC 6.3 进行测试。

EDIT3:我将问题标记为 'answered'。为了完整起见,我在下面添加了更多信息:

我检查了正在使用的编译器版本,它不是很好。我们正在使用 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 标志编译,此代码将抛出以下警告:

warning: cast discards ‘const’ qualifier from pointer target type [-Wcast-qual] return (const MyType*) a;

编辑:澄清一下,错误在这一行:

return (const MyType*) a;

问题原因

我知道问题的根本原因是 typedef 类型 MyType,它实际上是一个数组。遗憾的是,我没有修改此 typedef 的奢侈,也没有修改 API 函数 myapi_foo 及其可疑的参数类型选择。 老实说,我真的不明白为什么 编译器对这个转换如此不满意,所以非常欢迎澄清。

问题

什么是最干净向编译器指示所有内容都应被视为指向常量数据的指针的方法?

废弃的和潜在的解决方案

以下是我找到的一些 'solutions' 但让我不满意的:

感谢您的宝贵时间。

你可以做到:

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

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

w.c 是您修改后的文件 :

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 $ 

这是GCC bug 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 别名规则。您在问题中显示的代码似乎是示例,而不是实际代码,因此我们无法确定,但您应该考虑这一点。

如果没有任何效果,您可能想使用 uintptr_t hammer,如果实现提供的话。它在 C 11 标准中是可选的:

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

    return (const MyType*) uip;
}