2015-04-25 41 views
6

在C++中,指針值可能是編譯時常量。這是真的,否則,非類型模板參數和constexpr將不能使用指針。然而,就我所知,靜態存儲的函數和對象的地址在鏈接時是已知的(至少),而不是編譯時。以下是一個例證:關於正在編譯時常量的指針值的困惑

的main.cpp

#include <iostream> 

template <int* p> 
void f() { std::cout << p << '\n'; } 

extern int a; 

int main() { 
    f<&a>(); 
} 

a.cpp

我只是想知道如何編譯main.cppa地址可能被稱爲。我希望有人能向我解釋這一點。

特別是考慮這個

template <int* p, int* pp> 
constexpr std::size_t f() { 
    return (p + 1) == (pp + 7) ? 5 : 10; 
} 

int main() { 
    int arr[f<&a, &b>()] = {}; 
} 

應該如何爲arr存儲分配?

加號:這個機制似乎相當強大。即使啓用Randomized Base Address,也可以獲得正確的輸出。

+3

在G ++,變量地址被佔用的錯位到函數名的變量名:http://coliru.stacked-crooked.com/a/ee352366c870c010 – dyp

+0

@dyp有道理! – Lingxi

+0

@dyp鏈接器將最終調整模板實例的二進制代碼中的地址值「p」。我可以這樣理解嗎? – Lingxi

回答

4

編譯器在編譯時不需要知道&a,它不需要知道函數地址的值。

想一下這樣:編譯器將以&a爲參數實例化你的函數模板,並生成「目標代碼」(無論它用於傳遞給鏈接器的任何格式)。目標代碼看起來像(當然不會,但你的想法):

func f__<funky_mangled_name_to_say_this_is_f_for_&a>__: 
    reg0 <- /* linker, pls put &std::cout here */ 
    reg1 <- /* hey linker, stuff &a in there ok? */ 
    call std::basic_stream::operator<<(int*) /* linker, fun addr please? */ 
    [...] 

如果實例f<b&>,假設b是另一個全球性的靜態,編譯器做同樣的事情:

func f__<funky_mangled_name_to_say_this_is_f_for_&b>__: 
    reg0 <- /* linker, pls put &std::cout here */ 
    reg1 <- /* hey linker, stuff &b in there ok? */ 
    call std::basic_stream::operator<<(int*) /* linker, fun addr please? */ 
    [...] 

當你的代碼調用調用這類原因:

fun foo: 
    call f__<funky_mangled_name_to_say_this_is_f_for_&a>__ 
    call f__<funky_mangled_name_to_say_this_is_f_for_&b>__ 

哪個確切功能調用的錯位功能名稱進行編碼。 生成的代碼不取決於&a&b的運行時間值。 編譯器知道在運行時會出現這種情況(你這麼說),這就是它所需要的。它會讓鏈接器填充空白(或者如果您未能兌現承諾,就會對你大喊)。


爲了您除了我怕我不看好constexpr規則不夠熟悉,但兩種編譯器我有告訴我,這個功能將在運行時進行評估,其中,據他們說,使代碼不符合。 (如果他們是錯誤的,則上面的回答是,至少,不完整的。)

template <int* p, int* pp> 
constexpr std::size_t f() { 
    return (p + 1) == (pp + 7) ? 5 : 10; 
} 

int main() { 
    int arr[f<&a, &b>()] = {}; 
} 

鐺3.5在C++ 14個標準符合模式:

$ clang++ -std=c++14 -stdlib=libc++ t.cpp -pedantic 
t.cpp:10:10: warning: variable length arrays are a C99 feature [-Wvla-extension] 
    int arr[f<&a, &b>()]; 
     ^
1 warning generated. 

GCC G ++ 5.1,相同模式:

$ g++ -std=c++14 t.cpp -O3 -pedantic 
t.cpp: In function 'int main()': 
t.cpp:10:22: warning: ISO C++ forbids variable length array 'arr' [-Wvla] 
    int arr[f<&a, &b>()]; 
+0

那麼這個'template constexpr std :: size_t f(){return(p + 1)==(pp + 7)? 5:10; }',我使用返回的值作爲數組定義中的邊界? – Lingxi

+0

然後如何分配數組的存儲'int arr [f <&a, &b>()] = {};'? – Lingxi

+0

有趣的問題。不能讓它在C++ 11模式下編譯,但(clang ++和g ++告訴我這是一個VLA)。與C++ 14模式相同(g ++ 5.1/clang ++ 3.5) – Mat

1

據我所知,靜態存儲和函數的變量在編譯時只是作爲符號表中的符號/佔位符存儲。當地方持有人獲得解決時,它處於鏈接階段。

編譯器輸出機器代碼,使佔位符保持不變。然後鏈接程序用各自的內存位置替換變量/函數的佔位符。所以在這種情況下,如果你只編譯main.cpp而沒有編譯a.cpp並鏈接它,你必然會遇到鏈接器錯誤,你可以在這裏看到http://codepad.org/QTdJCgle(我只編譯main.cpp)