[英]Compiling with different versions of gcc on ubuntu yields different results
所以我有一個像這樣的項目設置:
myfile.cpp
包括:
fsl_clock.h
其中MYFILE是一個C ++文件和fsl_clock.h是從NXP純C頭文件,其中它的一個版本可以看出這里
我的文件看起來像:
#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
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編譯,但它確實產生了我在編譯器上解釋的錯誤。 鏈接在這里
Minimal - Wandbox與在線GDB示例完全相同的代碼,但這實際上顯示了我得到的相同錯誤: 這里
最小的示例代碼
#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;
}
編譯器在編譯時無法評估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 ++都是如此。 由於您的代碼在技術上是未定義的行為,因此它可能會隨機適用於某些編譯器。 大多數針對您希望實際使用這種代碼的平台的編譯器將在運行時執行代碼時執行您期望的操作。
但是,對於編譯時評估, <csstddef>
的offsetof
處理所有這些類型的細節,並且是已定義的行為。 這就是你想要和需要的東西。
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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.