34

How does the compiler know where in memory the square root will be before the program is executed? I thought the address would be different everytime the program is executed, but this works:

constexpr double(*fp)(double) = &sqrt;
cout << fp(5.0);

Is it because the address is relative to another address in memory? I don't think so because the value of fp is large: 0x720E1B94.

15
  • The linkernel does this job not the compiler. Linker figures out where things are located Commented Jul 16, 2016 at 15:55
  • It is never the real address Commented Jul 16, 2016 at 15:56
  • @EdHeal That is still before execution, and I don't understand why the value can be the same for every execution?! Commented Jul 16, 2016 at 15:57
  • @Coolwater pointer of a function does not change between calls, why should it? Commented Jul 16, 2016 at 15:58
  • 2
    @SHH: That difference is not relevant unless the program is recompiled every time it is run. Commented Jul 16, 2016 at 16:09

4 Answers 4

32

At compile time, the compiler doesn't know the address of sqrt. However, you cannot do anything at compile time with a constexpr function pointer that would allow you to access that pointer's address. Therefore, a function pointer at compile time can be treated as an opaque value.

And since you can't change a constexpr variable after it has been initialized, every constexpr function pointer can be boiled down to the location of a specific function.

If you did something like this:

using fptr = float(*)(float);

constexpr fptr get_func(int x)
{
  return x == 3 ? &sqrtf : &sinf;
}

constexpr fptr ptr = get_func(12);

The compiler can detect exactly which function get_func will return for any particular compile time value. So get_func(12) reduces down to &sinf. So whatever &sinf would compile to is exactly what get_func(12) would compile to.

Sign up to request clarification or add additional context in comments.

3 Comments

This does not explain how the compiler knows what the value of &sinf is. Only @hyde's answer explains that.
@fishinear the compiler doesn't have to know the actual value. It only has to know that it's a constant value that cannot change during execution. This allows it to directly reference either the sqrtf or the sinf symbol in the produced executable: it allows elimination of the branch and call-through-pointer for constant parameters.
@Giel Exactly. The compiler does not need to know it, but the final code definitely needs to know it, and that was the OP's question. The answer is of course that the value of the symbol is determined and filled in during linking, which is not explained in this answer.
18

Address value is assigned by a linker, so the compiler does not know the exact address value.

cout << fp(5.0); 

This works because it is evaluated at run-time after exact address has been resolved.

In general, you cannot use the actual value (address) of constexpr pointer because it is not known at compile-time.

Bjarne Stroustrup's C++ Programming language 4th edition mentions:

10.4.5 Address Constant Expressions

The address of a statically allocated object (§6.4.2), such as a global variable, is a constant. However, its value is assigned by the linker, rather than the compiler, so the compiler cannot know the value of such an address constant. That limits the range of constant expressions of pointer and reference type. For example:

   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

1 Comment

Note that constexpr const char* p3 = p1 + 2; is fine as far as I can tell. p1 + 2 is a value that represents the address of a subobject with static storage duration, and is a valid constant expression; there's no requirement for a complete object in this case. You can't use it as a non-type template argument, but that's a different story. I can't find anything in the Standard that would make such construct ill-formed; this applies to C++11 and later versions. All compilers that I've tested accept it. See also this answer.
9

How does the compiler know where in memory the square root will be before the program is executed?

The tool chain gets to decide where it puts the functions.

Is it because the address is relative to another address in memory?

If the produced program is either relocatable or position independent then yes, that's the case. If the program is neither, then the address can even be absolute.

Why would the exact same memory spots be available next time the program is run?

Because the memory space is virtual.

1 Comment

Well the space is virtual, but ASLR is also commonly used today.
8

It's simple.

Consider how compiler knows the address to call in this code:

puts("hey!");

Compiler has no idea of the location of puts, and it also doesn't add a runtime lookup for it (that'd be rather bad for performance, though it is actually what virtual methods of classes need to do). The possibility of having a different version of dynamic library at runtime (not to mention address space layout randomization even if it is the exact same library file) makes sure the build time toolchain linker doesn't know it either.

So it's up to the dynamic linker to fix the address, when it starts the compiled binary program. This is called relocation.

Exact same thing happens with your constexpr: compiler adds every place in the code using this address to the relocation table, and then dynamic linker does its job every time the program starts.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.