2015-04-25 34 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)