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' 但让我不满意的:
- 删除 -Wcast-qual 标志。由于代码质量规则,我不能这样做。
- 添加#pragma 以关闭该部分代码周围的警告(如图here)。同样,我也不允许这样做。
- 将指针转换为整数,然后转换回指针(如 here 所示)
return (const MyType*) (uint32_t) a;
。它 非常 粗糙,但是使用 uint32_t
作为内存地址在这个项目中有先例,所以我可能不得不将它用作最后的努力。
- 编辑:@bruno 建议使用联合来回避问题。这是一个便携且相当优雅的解决方案。但是,上述代码质量规则完全禁止使用联合。
- 编辑:@Eric Postpischil 和@M.M 建议使用 (const void*) cast
return (const void*) a;
,无论 sizeof(MyType*)
的值如何,它都会起作用。遗憾的是它对目标不起作用。
感谢您的宝贵时间。
你可以做到:
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;
}
编辑:更深入地解释了问题
首先,让我从一些上下文开始:我正在编写的代码使用的是 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' 但让我不满意的:
- 删除 -Wcast-qual 标志。由于代码质量规则,我不能这样做。
- 添加#pragma 以关闭该部分代码周围的警告(如图here)。同样,我也不允许这样做。
- 将指针转换为整数,然后转换回指针(如 here 所示)
return (const MyType*) (uint32_t) a;
。它 非常 粗糙,但是使用uint32_t
作为内存地址在这个项目中有先例,所以我可能不得不将它用作最后的努力。 - 编辑:@bruno 建议使用联合来回避问题。这是一个便携且相当优雅的解决方案。但是,上述代码质量规则完全禁止使用联合。
- 编辑:@Eric Postpischil 和@M.M 建议使用 (const void*) cast
return (const void*) a;
,无论sizeof(MyType*)
的值如何,它都会起作用。遗憾的是它对目标不起作用。
感谢您的宝贵时间。
你可以做到:
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;
}