关于指针值是编译时常量的困惑
Confusion about pointer values being compile-time constatns
在 C++ 中,指针值可以是编译时常量。这是真的,否则,非类型模板参数和 constexpr
将无法使用指针。但是,据我所知,静态存储的函数和对象的地址(至少)在 link 时而不是编译时是已知的。下面是一个例子:
main.cpp
#include <iostream>
template <int* p>
void f() { std::cout << p << '\n'; }
extern int a;
int main() {
f<&a>();
}
a.cpp
int a = 0;
我只是想知道在编译 main.cpp
时如何知道 a
的地址。我希望有人能给我解释一下。
尤其要考虑这个
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
的存储应该如何分配?
PLUS: 这个机制看起来相当稳健。即使我启用 Randomized Base Address,也会获得正确的输出。
据我所知,静态存储和函数的变量在编译时只是作为symbols/place 持有者存储在符号table 中。解析占位符时处于链接阶段。
编译器输出保持占位符完整的机器代码。然后链接器用它们各自的内存位置替换变量/函数的占位符。所以在这种情况下,如果你只是编译 main.cpp 而没有编译 a.cpp 并链接它,你一定会遇到链接器错误,正如你在这里看到的 http://codepad.org/QTdJCgle (我编译 [=仅 16=])
编译器在编译时不需要知道&a
的值,就像它不需要函数地址的值一样。
可以这样想:编译器将使用 &a
作为参数实例化您的函数模板并生成 "object code"(以它用于传递给链接器的任何格式)。目标代码看起来像(它不会,但你明白了):
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>()] = {};
}
C++14 标准符合模式中的 clang 3.5:
$ 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>()];
在 C++ 中,指针值可以是编译时常量。这是真的,否则,非类型模板参数和 constexpr
将无法使用指针。但是,据我所知,静态存储的函数和对象的地址(至少)在 link 时而不是编译时是已知的。下面是一个例子:
main.cpp
#include <iostream>
template <int* p>
void f() { std::cout << p << '\n'; }
extern int a;
int main() {
f<&a>();
}
a.cpp
int a = 0;
我只是想知道在编译 main.cpp
时如何知道 a
的地址。我希望有人能给我解释一下。
尤其要考虑这个
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
的存储应该如何分配?
PLUS: 这个机制看起来相当稳健。即使我启用 Randomized Base Address,也会获得正确的输出。
据我所知,静态存储和函数的变量在编译时只是作为symbols/place 持有者存储在符号table 中。解析占位符时处于链接阶段。
编译器输出保持占位符完整的机器代码。然后链接器用它们各自的内存位置替换变量/函数的占位符。所以在这种情况下,如果你只是编译 main.cpp 而没有编译 a.cpp 并链接它,你一定会遇到链接器错误,正如你在这里看到的 http://codepad.org/QTdJCgle (我编译 [=仅 16=])
编译器在编译时不需要知道&a
的值,就像它不需要函数地址的值一样。
可以这样想:编译器将使用 &a
作为参数实例化您的函数模板并生成 "object code"(以它用于传递给链接器的任何格式)。目标代码看起来像(它不会,但你明白了):
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>()] = {};
}
C++14 标准符合模式中的 clang 3.5:
$ 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>()];