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

问题描述 投票:4回答:2

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

  • myfile.cpp包括: fsl_clock.h

其中myfile是c ++文件,fsl_clock.h是来自NXP的纯C头文件,其中可以看到它的版本here

我的文件看起来像:

#include "fsl_clock.h"

现在我的文件中有更多的东西,但我把它清空,直到我离开了。

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

  • 使用arm交叉编译器arm-none-eabi-g++这个编译很好。
  • 使用主机(x86Linux)g ++ --version 7.3.0-16ubuntu3它工作正常
  • 使用host(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。但我真的不明白后缀16ubuntu3 vs 27ubuntu1~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

Minimal - 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;
}
c++ c ubuntu gcc g++
2个回答
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


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>宏会为您处理所有这些类型的细节,并且是定义的行为。这就是你想要和需要的东西。

© www.soinside.com 2019 - 2024. All rights reserved.