Hello! Welcome to the third lesson in our course on C pointers.
In our last session, we established that a pointer is a special kind of variable that doesn't store data directly, but instead stores the memory address of another variable. This creates a level of indirection, which is key to many powerful C features.
Today, we address the logical next question: how do we actually get the memory address of a variable to store it in a pointer? This lesson focuses on a single, fundamental operator that makes this possible. Our learning outcome is to use the address-of operator (&) to obtain memory addresses of variables. Mastering this is the first practical step in working with pointers.
The Address-of Operator: &
In C, the ampersand (&) when used as a unary operator (an operator with only one operand) is the address-of operator. It returns the memory address of the variable it is applied to.
Consider a simple variable declaration:
int ecu_status_code = 0x0A; // A status code for an ECU
This variable, ecu_status_code, resides somewhere in memory. To get its exact location, you use the & operator:
int* status_ptr; // Declare a pointer to an integer
status_ptr = &ecu_status_code; // Assign the address of ecu_status_code to the pointer
After this assignment, status_ptr no longer holds a garbage value; it now holds the specific memory address where the value 0x0A is stored.
The following reading provides a formal introduction to this operator with a clear code example and memory diagram.
The document 'Pointers and more arrays' offers a concise explanation of the address-of operator's syntax and purpose.
Please read the section titled '8.1.2. &: Address-of operator'. Pay close attention to the code snippet showing how a pointer p is initialized with the address of i, and study the accompanying memory diagram.
Visualizing the & Operator
Conceptually, you can think of your program's memory as a long street of numbered houses. Each variable lives in a house, and the address-of operator tells you the house number.

It's important to remember that the operand of the & operator must be something that actually has a memory address. This is what's known in C as an l-value (historically, something that can appear on the left side of an assignment). You can take the address of a variable (&ecu_status_code), but you cannot take the address of a literal value (&100) or the result of a temporary expression (&(x + y)), as these don't have a permanent storage location.
Why is Getting an Address Useful?
Obtaining a variable's address is the first step towards indirect manipulation. One of the most common use cases, which we will explore fully in Module 3, is passing arguments to functions.
In C, function arguments are passed by value. This means the function receives a copy of the argument, not the original variable. Any changes the function makes to its copy do not affect the caller's original variable.
Your experience with AUTOSAR middleware and OS-level APIs has likely involved functions that need to write data back into a variable you provide. For example, a function that reads a CAN message might need to populate a struct you pass to it. To allow a function to modify the original data, you don't pass the data itself; you pass its address.
A classic example is the gettimeofday system call, which fills a struct with the current time. You declare the struct and then pass its address to the function so the function can write directly into your variable's memory location.
The 'Pointers and more arrays' document contains a practical example of this with the gettimeofday function. This pattern is very common in systems programming.
Read the short section '8.1.5. Example operating system call with pointers: gettimeofday'. Notice how the address of the local struct timeval tv is passed to the function using &tv.
The & Operator and Compiler Optimizations
Here is a deeper point that is crucial for an advanced understanding of C. A common (but incorrect) assumption is that if you declare variables one after another in your code, they will be laid out sequentially in memory.
int a;
int b;
int c;
// Are &a, &b, and &c guaranteed to be contiguous? No.
The C standard gives the compiler significant freedom to optimize. The compiler might reorder variables on the stack, insert padding between them for alignment purposes, or, if a variable's value can be determined at compile time and its address is never taken, it might optimize the variable away entirely, storing its value in a CPU register or as an immediate value in machine code instructions.
In such a case, the variable doesn't have a memory address.
However, the moment you use the & operator on a variable, you are explicitly requesting its address. This acts as a directive to the compiler, forcing it to allocate storage for that variable in memory so that a valid address can be returned.
This interaction between your source code and the compiler's behavior is fundamental to low-level programming.
How are memory addresses assigned in C?
This Stack Overflow discussion provides an excellent explanation of this concept. It clarifies why you can't predict a variable's address and how the & operator changes the game.
Please read the first few paragraphs of the accepted answer (by user Brendan), which explain how compilers can optimize variables away. Then, read the final paragraph that starts with 'The only thing you can do is get the address at run-time...', which explains how using & forces the compiler to ensure the variable has an address.
Test your understanding!
Given the following code snippet:
#include <stdint.h>
void process_sensor_data() {
uint32_t sensor_id = 0xAE12F00D;
int16_t temperature_c = 25;
uint32_t* id_ptr = &sensor_id;
}
- What is the type of the expression
&temperature_c? - Can you legally write
int16_t temp_copy = &temperature_c;? Why or why not? - Based on the section about compiler optimizations, what does the line
uint32_t* id_ptr = &sensor_id;guarantee about thesensor_idvariable when the program is compiled?
Show answer
- The type of the expression
&temperature_cisint16_t*(pointer to a signed 16-bit integer). - No, this is not legal and would result in a compiler error or warning.
&temperature_cproduces a memory address (a pointer), buttemp_copyis a variable designed to hold a 16-bit integer value. You cannot assign a pointer to a non-pointer integer type without an explicit (and usually dangerous) cast. The types are incompatible. - It guarantees that the
sensor_idvariable will be allocated in memory (likely on the stack) rather than being potentially optimized away into a register. By taking its address, we force the compiler to give it a concrete location that theid_ptrcan point to.
Conclusion
Today we've focused on one critical piece of the puzzle: the address-of operator (&). It is the bridge between the variables you declare and the memory addresses that pointers are designed to hold.
Key Takeaways:
- The
&operator is a unary operator that returns the memory address of its operand. - The operand must be an l-value (e.g., a variable), not a literal or temporary expression.
- Getting a variable's address is essential for tasks like allowing a function to modify a caller's data.
- Using the
&operator on a variable forces the compiler to allocate it in memory, preventing certain optimizations and ensuring it has a stable address.
We now know how to get an address and store it in a pointer. In the next lesson, we will complete the circle by learning how to use that stored address to get back to the original variable's value. This is done with the dereference operator (*), the logical counterpart to &.