c中的严格别名和数组

Posted

技术标签:

【中文标题】c中的严格别名和数组【英文标题】:Strict aliasing and arrays in c 【发布时间】:2014-11-22 12:41:53 【问题描述】:

我有这样的功能:

static void doSomething(int16_t array[256], int16_t mask, uint8_t skip)
    uint16_t storage = array[skip];
    uint64_t* array1=(uint64_t*)(array);
    uint64_t mask1 =0;
    uint16_t* Lmask=(uint16_t*)&mask1;
    Lmask[0]=mask;
    Lmask[1]=mask;
    Lmask[2]=mask;
    Lmask[3]=mask;
    int k;
    for (k =0 ; k < 64; k++) 
        array1[k]&=mask;
        array[skip]=storage;
        if(hasZero(array1[k]))
        ...
        
    

它应该接受一个 16 位整数数组,对其应用掩码并检查它是否包含一个等于 0 且不在跳过位置的 16 位整数,如果是,则执行某些操作。在使用优化 -O2 之前一切正常(-O1,-Os 正常工作)。

该函数被调用了数百万次,因此它不能使用 16 位掩码和 16 位数组,因为它的有效性。我想,问题是,这段代码违反了严格的别名规则。有什么方法可以对编译器说,array1 和 array 使用相同的内存位置,因此在评估 if 语句之前它不能遗漏array[skip]=storage;(我尝试了联合,但是没有成功,它确实做到了和现在一样)?或者有没有其他方法可以做到这一点,它不会违反这个规则?

【问题讨论】:

但是你真的需要氧气吗? 不幸的是,整个项目都是使用 -O2 编译的,我无能为力。 你的文字是g++,但你的q被标记为c 如果使用了g++,那么它是C++代码,应该为这个站点标记为c++ 现在我已经在 gcc 上对其进行了测试,它表现出完全相同的行为,因此我将 g++ 部分从正文中删除,以免与它混淆。 【参考方案1】:

是的,这违反了严格的别名使用uint16_tuint64_t 左值来读取相同的存储区域。

gcc 的快速修复方法是使用-fno-strict-aliasing

一个可靠的解决方法是重写代码以不包含任何别名违规。起初这可能看起来更神秘,但理论上编译器会看到正在发生的事情并在您编写正确的代码时生成最佳汇编。

【讨论】:

是的,我想这样做,但是如何在保留 64 位数字的工作的同时做到这一点?如果我把它排除在外,它当然会更慢,我想避免这种情况。 “它当然会更慢” - 在做出这个假设之前对其进行基准测试(或比较组装) 好吧,我之前尝试过,但它似乎比预期的要慢。 可能你的代码不正确;也许用您尝试询问如何加速的代码开始一个新问题 然而,这留下了最初的问题,是否可以指示编译器不要将其排除在外,如果可能的话,在代码中,因为我不是进行最终编译的人。

以上是关于c中的严格别名和数组的主要内容,如果未能解决你的问题,请参考以下文章

c++ 模板化抽象基类数组,不违反严格别名规则

C++ 中的简单存储类和严格的别名

从空字节数组转换为结构指针可能会违反严格的别名?

_Bool类型和严格别名

探究C/C++ typedef的秘密

在不破坏严格别名的情况下有效地生成字节缓冲区