在ubuntu上使用不同版本的gcc编译会产生不同的结果

时间:2019-02-14 12:59:49

标签: c++ c ubuntu gcc g++

所以我有一个类似这样的项目设置:

  • myfile.cpp,其中包括:
    • fsl_clock.h

myfile是c ++文件,而fsl_clock.h是NXP提供的纯C头文件,可以在其中看到其版本here

我的文件如下:

#include "fsl_clock.h"

现在我的文件中还有更多东西,但是我清空了它,直到剩下那一堆为止。

以下是我尝试过的编译结果:

  • 使用手臂交叉编译器arm-none-eabi-g++可以很好地进行编译。
  • 使用主机(x86Linux)g ++ --version 7.3.0-16ubuntu3可以正常工作
  • 使用主机(x86Linux)g ++ --version 7.3.0-27ubuntu1~18.04会产生很多奇怪的错误。

我得到的错误是这样的:

device/MIMX8MQ6_cm4.h8856:51: error 'reinterpret_cast<CMM_Type*>(808976384)' is not a constant expression

其中的代码行是纯C语言,如下所示:

kCLOCK_RootM4 = (uint32_t)(&(CCM)->ROOT[1].TARGET_ROOT)

CCM定义为:

#define CCM_BASE (0x30380000u)
#define CCM ((CCM_Type*)CCM_BASE)

因此,看起来较新的g ++ 7.3.0-27ubuntu1~18.04(也许正确)正在以C样式的包含标头代码执行c ++事情(例如reinterpret_cast)。较旧的编译器7.3.0-16ubuntu3的行为方式不同-编译正常。

谁能说出两个编译器之间的区别是什么,为什么一个起作用而另一个却不起作用?两种编译器gnu g ++具有相同的g ++版本7.3.0。但是我不太了解后缀16ubuntu327ubuntu1~18.04以及为什么这可能会改变行为...

注意现在,我知道对于我的主机版本,我真的不希望在主机版本中包含特定于主板的代码,但这是另一回事。目前,我对理解为什么两个编译器之间存在差异的兴趣更强。

更新

对于主机构建,编译器行如下所示:

g++ -w -Isource/drivers -Isource/board -Isource/device -m32 -g -std=c++11 -c source/myfile.cpp -o out.o 

CMM_Type(必须将其原件埋在恩智浦网站中而必须手动复制)看起来像(请注意其缩写,因为要复制的内容太多-但其结构是uint32_t的结构):

typedef struct {
   volatile uint32_t GPR0;
   volatile uint32_t GPR0_SET;
   struct {
      :
   } PLL_CTRL[39];
      :
   struct {
      volatile uint32_t TARGET_ROOT;
      volatile uint32_t TARGET_ROOT_SET;
      volatile uint32_t TARGET_ROOT_CLR;
         :
   } ROOT[142];
} CCM_Type;

最小示例-在线GDB 我做了一个最小的例子-它不能与在线GDB一起编译,但是确实会产生我在编译器中解释的错误。链接为here

最低-Wandbox 与在线GDB示例完全相同的代码,但这实际上显示了我得到的相同错误:here

最小示例代码

#include <stdint.h>

typedef struct {
    struct {
        volatile uint32_t TARGET_ROOT;
    } ROOT[4];
} CCM_Type;

#define CCM_BASE (0x30380000u)
#define CCM ((CCM_Type *)CCM_BASE)

typedef enum _clock_root_control
{
    kCLOCK_RootM4 = (uint32_t)(&(CCM)->ROOT[1].TARGET_ROOT)
} clock_root_control_t;

int main()
{
    return 0;
}

2 个答案:

答案 0 :(得分:2)

reinterpret_cast和C样式强制转换在编译时无法由编译器求值,当它们创建随后要取消引用的指针时尤其如此。 enum常量需要在编译时获取值。在这种情况下,我将使用整数值CCM_BASEoffsetof

#include <cstddef>

typedef enum _clock_root_control
{
    kCLOCK_RootM4 = CCM_BASE + offsetof(CCM_Type, ROOT[1].TARGET_ROOT)
} clock_root_control_t;

您知道,在原始示例中所有取消引用的指针都是没有意义的,因为您只是使用&运算符来获取地址。但是,没关系。解引用必须仍然有效并且可执行,以便编译器在编译时对其进行评估。您正在使用的地址对编译器毫无意义。谁知道那里有什么,即使它指的是映射页面?当然,编译C ++程序也不应该在编译器的内存中造成随机破坏。

对于C或C ++都是如此。由于您的代码在技术上是未定义的行为,因此对于某些编译器,它可能会随机工作。而且大多数将目标指向要在其上实际使用这种代码的平台的编译器,将在运行时执行代码时达到预期的效果。

但是,为了进行编译时评估,offsetof中的<csstddef>宏会为您处理所有这些种类的详细信息,并定义为行为。这就是您想要和需要的。

答案 1 :(得分:2)

typedef enum _clock_root_control
{
    kCLOCK_RootM4 = (uint32_t)(&(CCM)->ROOT[1].TARGET_ROOT)
} clock_root_control_t;

此代码在C或C ++中均无效。

在C ++中,枚举器必须是一个常量表达式。常量表达式不能包含reinterpret_cast。从指针到整数的C样式转换等效于reinterpret_cast

在C中,枚举器必须是整数常量表达式。整数常量表达式不能包含指针操作数。

解决此问题的一种方法是将表达式替换为等效的常量表达式,例如

CCM_BASE + offsetof(CCM_Type, ROOT[1].TARGET_ROOT)

Live example