在程序执行之前,编译器如何知道平方根在内存中的位置?我认为每次执行程序时地址都会不同,但这样做有效:
constexpr double(*fp)(double) = &sqrt;
cout << fp(5.0);
是因为地址是相对于内存中的另一个地址吗?我不这么认为,因为fp
的值很大:0x720E1B94。
答案 0 :(得分:24)
在编译时,编译器不知道sqrt
的地址。但是,您无法在编译时使用constexpr函数指针执行任何操作,该指针允许您访问该指针的地址。因此,编译时的函数指针可以视为不透明值。
由于在初始化之后你无法改变constexpr变量,所以每个constexpr函数指针都可以归结为特定函数的位置。
如果你做了这样的事情:
using fptr = float(*)(float);
constexpr fptr get_func(int x)
{
return x == 3 ? &sqrtf : &sinf;
}
constexpr fptr ptr = get_func(12);
编译器可以准确检测哪个函数get_func
将为任何特定的编译时间值返回。因此get_func(12)
缩减为&sinf
。所以无论&sinf
编译到什么,都是get_func(12)
编译到的。
答案 1 :(得分:16)
地址值由链接器分配,因此编译器不知道确切的地址值。
cout << fp(5.0);
这是有效的,因为它在确切地址解析后的运行时进行评估。
通常,您不能使用constexpr
指针的实际值(地址),因为它在编译时是未知的。
Bjarne Stroustrup的C ++编程语言第4版提及:
10.4.5地址常量表达式
静态分配对象(第6.4.2节)的地址(例如全局变量)是常量。但是,它的值由链接器而不是编译器分配,因此编译器无法知道此类地址常量的值。这限制了指针和引用类型的常量表达式的范围。例如:
constexpr const char∗ p1 = "asdf"; constexpr const char∗ p2 = p1; // OK constexpr const char∗ p2 = p1+2; // error : the compiler does not know the value of p1 constexpr char c = p1[2]; // OK, c==’d’; the compiler knows the value pointed to by p1
答案 2 :(得分:6)
在程序执行之前,编译器如何知道平方根在内存中的位置?
工具链决定将功能放在何处。
是因为地址是相对于内存中的另一个地址吗?
如果生成的程序是relocatable或position independent,那么是的,就是这种情况。如果程序既不是,那么地址甚至可以是绝对的。
为什么下次运行程序时可以使用完全相同的内存点?
因为内存空间是virtual。
答案 3 :(得分:4)
很简单。
考虑编译器如何知道要在此代码中调用的地址:
puts("hey!");
编译器不知道puts
的位置,也没有为它添加运行时查找(这对于性能来说相当糟糕,尽管它实际上是虚拟方法课程需要做)。在运行时拥有不同版本的动态库的可能性(更不用说地址空间布局随机化,即使它是完全相同的库文件)也可以确保构建时工具链链接器也不知道它。
因此,当它启动编译的二进制程序时,由dynamic linker来修复地址。这称为重定位。
与constexpr
完全相同的事情:编译器使用此地址将代码中的每个位置添加到重定位表,然后动态链接器在每次程序启动时执行其工作。