所以我有一个类似这样的项目设置:
myfile.cpp
,其中包括:
fsl_clock.h
myfile是c ++文件,而fsl_clock.h是NXP提供的纯C头文件,可以在其中看到其版本here
我的文件如下:
#include "fsl_clock.h"
现在我的文件中还有更多东西,但是我清空了它,直到剩下那一堆为止。
以下是我尝试过的编译结果:
arm-none-eabi-g++
可以很好地进行编译。 7.3.0-16ubuntu3
可以正常工作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
与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
最低-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;
}
答案 0 :(得分:2)
reinterpret_cast
和C样式强制转换在编译时无法由编译器求值,当它们创建随后要取消引用的指针时尤其如此。 enum
常量需要在编译时获取值。在这种情况下,我将使用整数值CCM_BASE
和offsetof
。
#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)