Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is the address of a local variable a constexpr?

In Bjarne Stroustrup's book "The C++ Programming Language (4th Edition)" on p. 267 (Section 10.4.5 Address Constant Expressions), he uses a code example where the address of a local variable is set to a constexpr variable. I thought this looked odd, so I tried running the example with g++ version 7.3.0 and was unable to get the same results. Here is his code example verbatim (although slightly abridged):

extern char glob;

void f(char loc) {
    constexpr const char* p0 = &glob; // OK: &glob's is a constant
    constexpr const char* p2 = &loc;  // OK: &loc is constant in its scope
}

When I run this, I get:

error: ‘(const char*)(& loc)’ is not a constant expression

Is something happening with g++ that I'm not aware of, or is there something more to Bjarne's example?

like image 553
johnnyodonnell Avatar asked Apr 16 '19 00:04

johnnyodonnell


4 Answers

An earlier printing of Bjarne Stroustrup's book "The C++ Programming Language (4th Edition)" on p. 267 has the error outlined in the OP's question. The current printing and electronic copies have been "corrected" but introduced another error described later. It now refers to the following code:

constexpr const char* p1="asdf";

This is OK because "asdf" is stored in a fixed memory location. In the earlier printing the book errs here:

void f(char loc) {
    constexpr const char* p0 = &glob; // OK: &glob's is a constant
    constexpr const char* p2 = &loc;  // OK: &loc is constant in its scope
}

However, loc is not in a fixed memory location. it's on the stack and will have varying locations depending on when it is called.

However, the current 4th edition printing has another error. This is the code verbatim from 10.5.4:

int main() {
    constexpr const char* p1 = "asdf";
    constexpr const char* p2 = p1;      // OK
    constexpr const char* p3 = p1+2;    // error:  the compiler does not know the value of p1
}

This is wrong. The compiler/linker does know the value of p1 and can determine the value of p1+2 at link time. It compiles just fine.

like image 140
doug Avatar answered Oct 23 '22 18:10

doug


It appears that the example from section 10.4.5 provided in my hard-copy of the "The C++ Programming Language (4th Edition)" is incorrect. And so I've concluded that the address of a local variable is not a constexpr.

The example appears to have been updated in some pdf versions as seen here:

enter image description here

like image 41
johnnyodonnell Avatar answered Oct 23 '22 17:10

johnnyodonnell


This answer tries to clarify why the address of a local variable can't be constexpr by analysing an example for the x86-64 architecture.

Consider the following toy function print_addr(), which displays the address of its local variable local_var and call itself recursively n times:

void print_addr(int n) {
   int local_var{};
   std::cout << n << " " << &local_var << '\n';

   if (!n)
      return; // base case

   print_addr(n-1);  // recursive case
}

A call to print_addr(2) produced the following output on my x86-64 system:

2 0x7ffd89e2cd8c
1 0x7ffd89e2cd5c
0 0x7ffd89e2cd2c

As you can see, the corresponding addresses of local_var are different for each call to print_addr(). You can also see that the deeper the function call, the lower the address of the local variable local_var. This is because the stack grows downwards (i.e., from higher to lower addresses) on the x86-64 platform.

For the output above, the call stack would look like the following on the x86-64 platform:

                |     . . .     |
Highest address ----------------- <-- call to print_addr(2) 
                | print_addr(2) |    
                -----------------
                | print_addr(1) |
                -----------------
                | print_addr(0) | <-- base case, end of recursion
Lowest address  ----------------- Top of the stack

Each rectangle above represents the stack frame for each call to print_addr(). The local_var of each call is located in its corresponding stack frame. Since the local_var of each call to print_addr() is located in its own (different) stack frame, the addresses of local_var differ.

To conclude, since the address of a local variable in a function may not be the same for every call to the function (i.e., each call's stack frame may be located in a different position in memory), the address of such a variable can't be determined at compile time, and therefore can't be qualified as constexpr.

like image 3
ネロク・ゴ Avatar answered Oct 23 '22 16:10

ネロク・ゴ


Just to add to other answers that have pointed out the mistake, C++ standard only allows constexpr pointers to objects of static-storage duration, one past the end of such, or nullptr. See [expr.const/8] specifically #8.2;

It's worth noting that:

  • string-literals have static-storage duration:
  • Based on constraints in declaring extern variables, they'll inherently have static-storage duration or thread local-storage duration.

Hence this is valid:

#include <string>

extern char glob;
std::string boom = "Haha";

void f(char loc) {
    constexpr const char* p1 = &glob;
    constexpr std::string* p2 = nullptr;
    constexpr std::string* p3 = &boom;
}
like image 2
WhiZTiM Avatar answered Oct 23 '22 16:10

WhiZTiM