Confusion about pointer values ​​that are compilation constants

In C ++, pointer values ​​can be compile time constants. This is true, otherwise the template parameters of the non-type type and constexpr will not work with pointers. However, as far as I know, the addresses of functions and objects of static storage are known (at least) at the time of binding, and not compilation time. The following is an illustration:

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; 

I'm just curious to know how one would know the address of a when compiling main.cpp . I hope someone can explain this to me a little.

In particular, consider this

 template <int* p, int* pp> constexpr std::size_t f() { return (p + 1) == (pp + 7) ? 5 : 10; } int main() { int arr[f<&a, &b>()] = {}; } 

How to allocate storage for arr ?

PLUS: This mechanism seems pretty reliable. Even when I turned on "Randomized Base Address" , the correct result will be obtained.

+6
source share
2 answers

The compiler does not need to know the value of &a at compile time more than it needs the value of the function addresses.

Think of it this way: the compiler will create your function template using &a as a parameter and generate "object code" (in whatever format it uses to pass to the linker). The code of the object will look (well it will not, but you get the idea):

 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? */ [...] 

If you instantiate f<b&> , assuming b is another global static, the compiler does the same:

 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? */ [...] 

And when your code calls any of them:

 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>__ 

What exact function to call is encoded in the name of the changed function. The generated code is independent of the runtime value of &a or &b . The compiler knows that at runtime there will be such things (you said it that way) that all that is needed. This will allow the linker to fill in the blanks (or yell at you if you have not kept your promise).


For your addition, I’m afraid that I am not well aware of the constexpr rules, but two compilers, I will say that this function will be evaluated at runtime, which, in their opinion, makes the code inappropriate, (If they are wrong, then the answer is higher, at least incomplete.)

 template <int* p, int* pp> constexpr std::size_t f() { return (p + 1) == (pp + 7) ? 5 : 10; } int main() { int arr[f<&a, &b>()] = {}; } 

clang 3.5 in C ++ 14 standards compliance mode:

 $ 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, the same mode:

 $ 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>()]; 
+4
source

As far as I know, static storage variables and functions are stored simply as symbols / placeholders in the symbol table at compile time. It is in the linking phase when seat owners are allowed.

The compiler outputs the machine code while maintaining the integrity of the placeholders. The linker then replaces the variable / function placeholders with the appropriate memory cells. Thus, in this case, if you simply compile main.cpp without compiling a.cpp and linking to it, you will definitely encounter a linker error, as you can see here http://codepad.org/QTdJCgle (I compiled main. cpp)

+1
source

Source: https://habr.com/ru/post/985950/


All Articles