GCC:使用-Wcast-qual引发警告,将常量指针转换为数组typedef的常量指针

时间:2019-02-03 11:29:57

标签: c arrays gcc const typedef

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

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

编辑:也没有工会。

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以及与当前问题不相关的其他标志。

问题

考虑以下代码:

#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标志进行编译,此代码将引发以下警告:

  

警告:强制转换会从指针目标类型[-Wcast-qual]中丢弃“ const”限定词     return(const MyType *)a;

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

return (const MyType*) a;

问题原因

我知道问题的根本原因是typedef类型MyType,它实际上是一个数组。可悲的是,我没有修改这个typedef的奢侈,也没有API函数myapi_foo及其参数类型的可疑选择。 老实说,我不真正理解为什么为何编译器对此强制转换不满意,因此澄清起来实在值得欢迎。

问题

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

废弃的和潜在的解决方案

以下是我找到的一些“解决方案”,但令我不满意:

  • 删除 -Wcast-qual 标志。由于代码质量规则,我无法做到这一点。
  • 添加#pragma可以关闭该部分代码的警告(如here所示)。同样,我不允许这样做。
  • 将指针转换为整数,然后转换回指针(如here所示)return (const MyType*) (uint32_t) a;非常很粗糙,但是使用uint32_t作为内存地址在该项目中是先例,因此我可能不得不将其用作最后的尝试。
  • 编辑:@bruno建议使用联合来回避问题。这是一个可移植且相当优雅的解决方案。但是,上述代码质量规则完全禁止使用联合。
  • 编辑:@Eric Postpischil和@ M.M建议使用(const void *)强制转换return (const void*) a;,无论sizeof(MyType*)的值如何,它都可以工作。遗憾的是,它对目标不起作用。

谢谢您的时间。

3 个答案:

答案 0 :(得分: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实现中,MyTypeuint32_t的数组,将需要四字节对齐,但是a仅需要一字节对齐。根据C 2018 6.3.2.3 6,如果a未正确对齐MyType,则转换结果未定义。

此外,此代码建议将uint_t数组a用作四个uint32_t的数组。这将违反C别名规则。您在问题中显示的代码似乎只是示例,而不是实际的代码,因此我们不能确定,但​​是您应该考虑一下。

答案 1 :(得分: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和pointer的各自大小(在int和pointer之间都没有强制转换),该方法都可以工作,但是我不确定这是否非常优雅; / p>

具有 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 $ 

答案 2 :(得分:1)

如果没有任何效果,则您可以使用uintptr_t hammer(如果实现可以提供)。根据C11标准,它是可选的:

    public class MessageService extends FirebaseMessagingService {


    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

        if(remoteMessage.getData().isEmpty())
            showNotification(remoteMessage.getNotification().getTitle(),remoteMessage.getNotification().getBody(),remoteMessage.getData().get("url"));
        else
            showNotification(remoteMessage.getData().get("title"),remoteMessage.getData().get("body"),remoteMessage.getData().get("url"));
    }

    private void showNotification(String title, String body, String url) {

        Intent resultIntent = new Intent(this, MainActivity.class);
        resultIntent.putExtra("url",url);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addNextIntentWithParentStack(resultIntent);
        PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

        //Uri sound =Uri.parse("android.resource://" + getApplicationContext().getPackageName() + "/" + R.raw.notification);
        //Uri sound = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + "/raw/notification");
        Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + File.pathSeparator + File.separator + getPackageName() + "/raw/notification.mp3");
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        String NOTIFICATION_CHANNEL_ID = "com.example.alibapir.test";

        NotificationCompat.Builder notificationBuilder =  new NotificationCompat.Builder(this,NOTIFICATION_CHANNEL_ID);
        notificationBuilder.setAutoCancel(true)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher_ali_bapir_round)
                .setSound(sound)
                .setContentTitle(title)
                .setContentText(body)
                .setContentInfo("info")
                .setContentIntent(resultPendingIntent);

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            NotificationChannel notificationChannel= new NotificationChannel(NOTIFICATION_CHANNEL_ID,"Notification",NotificationManager.IMPORTANCE_DEFAULT);
            notificationChannel.setDescription("Ali Bapir notification");
            notificationChannel.enableLights(true);
            notificationChannel.setLightColor(Color.BLUE);
            notificationChannel.setVibrationPattern(new long[]{0,1000,500,1000});
            notificationChannel.enableLights(true);
            notificationChannel.setSound(sound , new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_ALARM).build());
            notificationManager.createNotificationChannel(notificationChannel);
        }



        notificationManager.notify(new Random().nextInt(), notificationBuilder.build());
    }
}