31

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.

Coolwater
  • 703
  • 8
  • 15
  • The linkernel does this job not the compiler. Linker figures out where things are located – Ed Heal Jul 16 '16 at 15:55
  • It is never the real address – Arunmu Jul 16 '16 at 15:56
  • @EdHeal That is still before execution, and I don't understand why the value can be the same for every execution?! – Coolwater Jul 16 '16 at 15:57
  • @Coolwater pointer of a function does not change between calls, why should it? – W.F. Jul 16 '16 at 15:58
  • @W.F. When an executable file is run, its content is put in the memory. Why would the exact same memory spots be available next time the program is run? – Coolwater Jul 16 '16 at 16:00
  • @Coolwater method calls are not inlined at least not all of them... see the concept of a call stack... – W.F. Jul 16 '16 at 16:02
  • I think you are confusing the meaning of constexpr. constexpr means that the expression will be evaluated by compiler at compile time. It does not mean that the expression will have same value at all runs. – SHH Jul 16 '16 at 16:03
  • 2
    @SHH: That difference is not relevant unless the program is recompiled every time it is run. – Benjamin Lindley Jul 16 '16 at 16:09
  • @BenjaminLindley what about address space layout randomization? [Here](http://coliru.stacked-crooked.com/a/8da3f7e788c50656) you can see value of constexpr pointer change between runs without recompilation. – Revolver_Ocelot Jul 16 '16 at 16:41
  • @Revolver_Ocelot: Then the compiler didn't know the value, did it? – Benjamin Lindley Jul 16 '16 at 16:42
  • @BenjaminLindley it doesn't know representation of the value. It knows that `fp` points to `sqrt`. – Revolver_Ocelot Jul 16 '16 at 16:46
  • @Revolver_Ocelot: Then it can be said to hold the same value. The value being "the address of sqrt". – Benjamin Lindley Jul 16 '16 at 16:47
  • @Revolver_Ocelot: I would say that both statements are wrong, at least when it comes to pointers. To evaluate something is to find its value. So if you evaluate something, by definition, you know its value. If by "value", we mean the bits that make up an object (which we usually do), then the compiler in fact does not evaluate pointer expressions at compile time. If by "value" we are referring to a higher level entity, like a function, or a string literal, then the compiler does evaluate them, and both statements are correct. – Benjamin Lindley Jul 16 '16 at 17:00
  • I am confused by this declaration. Does the syntax declare a constexpr pointer, or a pointer to a constexpr function? – amon Jul 17 '16 at 07:09
  • @amon I think whether or not a function is constexpr doesn't change the pointer type needed for it. – Coolwater Jul 17 '16 at 07:36

4 Answers4

31

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    This does not explain how the compiler knows what the value of `&sinf` is. Only @hyde's answer explains that. – fishinear Jul 17 '16 at 11:06
  • 4
    @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 Mar 13 '18 at 09:27
  • @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. – fishinear Mar 13 '18 at 17:26
17

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
Community
  • 1
  • 1
SHH
  • 3,226
  • 1
  • 28
  • 41
  • 4
    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](http://stackoverflow.com/a/17660391/4326278). – bogdan Jul 16 '16 at 18:46
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.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 3
    Well the space is virtual, but [ASLR](https://en.wikipedia.org/wiki/Address_space_layout_randomization) is also commonly used today. – Ruslan Jul 16 '16 at 17:22
7

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.

hyde
  • 60,639
  • 21
  • 115
  • 176