#include // Every variable must have somewhere to store its value. // // In this example, main() has a local variable n. // When main() calls f(...), the function f(...) has // both a parameter p and a local variable m. // // Each of these variables has its own memory location. // // We can use the address-of operator '&' to obtain // the memory address of a variable. For example, // // int n{ 42 }; // // std::cout << n; // Prints the value 42. // std::cout << &n; // Prints the address of n. // // An address is usually displayed as a hexadecimal // number, such as 0x7ffda123456c. The actual address // will depend on the system and the particular run. // // When one function calls another, the system usually // creates a new stack frame for the called function. // This frame typically contains storage for local // variables and may also contain parameters and // other information needed to execute the function. // // On many systems, the call stack grows toward lower // memory addresses. However, C++ does not specify // the direction in which the stack grows or the // arrangement of variables within a stack frame. // // Run this program and record the three addresses. // // Questions: // // 1. Which address is the largest? Which is the // smallest? // // 2. How does the address of n compare with the // addresses of p and m? // // 3. Does this agree with your understanding of // how the call stack works? // // 4. Run the program several times. Do the addresses // change? Do their relative positions change? // // 5. Does changing the value of n change its address? // // Exercise: // // Write another function // // void g( double x ); // // Give g(...) a local variable y, also of type double. // // Print the addresses of both x and y. // // Call g(...) once from main() and once from f(...). // Compare the addresses in both cases. // // For an additional experiment, call g(...) twice // in succession from main(). Are the same addresses // used on both calls? // Function declarations void f( int p ); void g( double x ); int main(); // Function definitions int main() { int n{ 42 }; std::cout << "Entering main()..." << std::endl; std::cout << " The value of n is " << n << std::endl; std::cout << " The address of n is " << &n << std::endl; f( n ); // Exercise: Call g(...) here. std::cout << "...returning from main()." << std::endl; return 0; } void f( int p ) { std::cout << " Entering f()..." << std::endl; std::cout << " The value of p is " << p << std::endl; std::cout << " The address of p is " << &p << std::endl; int m{ 91 }; std::cout << " The value of m is " << m << std::endl; std::cout << " The address of m is " << &m << std::endl; // Exercise: Call g(...) here. std::cout << " ...returning from f()." << std::endl; } void g( double x ) { // Exercise: Declare a local variable y, // initialize it, and print the addresses // of x and y. } // Hint: // // The address-of operator '&' returns the address // of a variable. // // For example: // // double y{ 3.14 }; // // std::cout << &y << std::endl; // // The function g(...) can be called from both // main() and f(...), just as f(...) is called // from main(). // // When comparing addresses, remember that a // function's local variables normally cease to // exist when that function returns. A later call // may reuse the same memory. // One possible implementation of g(...): // // void g( double x ) { // double y{ 3.14 }; // // std::cout << " Entering g()..." << std::endl; // // std::cout << " The address of x is " // << &x << std::endl; // // std::cout << " The address of y is " // << &y << std::endl; // // std::cout << " ...returning from g()." // << std::endl; // } // // Add the following call inside f(...): // // g( 2.71828 ); // // Add the following calls inside main(): // // g( 1.41421 ); // g( 1.41421 );