GCC:铸造const的指针为const阵列的指针的typedef与-Wcast-QUAL抛出警告

问题描述 投票:5回答:3

编辑:问题深入here解释更多(谢谢@Eric Postpischil)。这似乎是在海湾合作委员会的错误。

首先,让我先从一些背景:我在写使用的API,我不能更改代码,在GCC的版本我无法改变,与编译标志我不能删除,而当我米,完成它必须有精确的零警告或编译指示。

编辑:没有工会无论是。

EDIT2:假设构建系统还采用了-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与其他不相关手头的问题一起。

The problem

考虑下面的代码:

#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;
}

与海湾合作委员会和-Wcast-QUAL标志编译,该代码会抛出以下警告:

警告:投丢弃“常量”指针目标类型[-Wcast-QUAL]返回(常量的MyType *)限定符;

编辑:澄清,错误的是在这条线:

return (const MyType*) a;

The cause of the problem

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

The question

什么是指示编译器都应该被视为一个指向常量数据的干净的方式?

Discarded and potential solutions

以下是我已经找到,但给我留下不满意的几个“解决方案”:

  • 卸下-Wcast-QUAL标志。我不能这样做,由于代码质量规则。
  • 添加的#pragma关闭围绕代码的那部分的警告(如图here)。同样我不能这样做。
  • 投的指针的整数,然后转换回一个指针(如图所示herereturn (const MyType*) (uint32_t) a;。这是非常简陋,但使用uint32_t内存地址在这个项目的先例,所以我可能不得不使用它作为最后的努力。
  • 编辑:使用工会侧步的问题@bruno建议。这是一种便携式和相当优雅的解决方案。然而,上述代码质量规则彻头彻尾禁止使用工会。
  • 编辑:@Eric Postpischil和@ M.M使用(常量无效*)投return (const void*) a;,无论哪个会工作sizeof(MyType*)价值的建议。遗憾的是它并没有对目标工作。

感谢您的时间。

c arrays gcc const typedef
3个回答
2
投票

这是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可以用作4个uint32_t的阵列。这将违反Ç别名规则。你在问题中显示的代码似乎是一个样品,而不是实际的代码,所以我们不能肯定,但你应该考虑这一点。


1
投票

你可以这样做:

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 $ 

1
投票

如果实在不行,你可能会喜欢用uintptr_t hammer,实施提供了它。它是由C11标准可选:

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

    return (const MyType*) uip;
}
© www.soinside.com 2019 - 2024. All rights reserved.