Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
C Address Operator Explained
Learn how the & operator returns the address of a variable, where it works, where it fails, and why scanf has always needed it. Part of our free C tutorial for embedded systems learners.
In the last lecture of this free C programming course you learned that every variable lives at an address. Now we learn how to ask for that address. The tool is the address operator, written &, and it is the first real pointer-related skill in this free C tutorial.
For anyone studying embedded C programming, the address operator is not a classroom curiosity. It is how you hand a buffer to a UART driver, give a DMA controller its target, or pass a variable to a function that must fill it in.
Table of Contents
What You Will Learn
By the end of this lecture you can:
- Use the C address operator on variables and array elements.
- Explain which operands are legal and which cause a compiler error.
- Print an address with
%pand understand the result. - Explain why
scanfneeds&in front of variable names. - Show that array elements have neighbouring addresses that differ by the element size.
Prerequisites
Finish the previous lecture on memory and addresses first. You should know that memory is a row of numbered bytes and that a variable’s address is the address of its first byte. You also need to know how to compile with gcc, which every lesson in this free embedded C course assumes.
The C Address Operator
The address operator is a unary operator: it takes one operand and is written directly in front of it. Place & before a variable and the expression produces the address of that variable. Read it aloud as “address of”: &voltage means “the address of voltage”.
Two facts are worth fixing in your mind right now:
- The result of
&xis not the value ofx. It is wherexis stored. - The type of the result is “pointer to the type of x”. For an
intvariable,&xhas typeint *. We will use that in the next lecture when we declare pointer variables.
Address Operator Demo Program
The program below takes the address of a normal variable and of each element of a small array. It then measures the distance in bytes between two neighbouring elements.
#include <stdio.h>
int main(void)
{
int readings[3] = {10, 20, 30};
double voltage = 3.3;
printf("voltage = %.1f stored at %p\n", voltage, (void *)&voltage);
for (int i = 0; i < 3; i++)
printf("&readings[%d] = %p\n", i, (void *)&readings[i]);
printf("gap between neighbours = %td bytes\n",
(char *)&readings[1] - (char *)&readings[0]);
return 0;
}
Build and run
gcc -Wall -Wextra -o addr_demo addr_demo.c
./addr_demo
Sample output
voltage = 3.3 stored at 0x7fff3faae200
&readings[0] = 0x7fff3faae20c
&readings[1] = 0x7fff3faae210
&readings[2] = 0x7fff3faae214
gap between neighbours = 4 bytes
What the output shows
The three element addresses end in 0c, 10 and 14 in hex. In decimal that is 12, 16 and 20, so each step is exactly 4 bytes, which is the size of an int on this machine. The final line proves it by casting both addresses to char * first, so the subtraction counts raw bytes. (Subtracting addresses is a pointer topic that we cover properly later in the chapter; here it is only a measuring tool.) Your own addresses will differ, but the 4-byte gap will not, unless your platform has a different int size.
| Element | Value | Address (hex, last digits) | Distance from previous |
|---|---|---|---|
| readings[0] | 10 | …20c | start |
| readings[1] | 20 | …210 | 4 bytes |
| readings[2] | 30 | …214 | 4 bytes |
What You Can and Cannot Take the Address Of
The address operator needs something that actually occupies memory. A variable does. An array element does. The result of an arithmetic expression, or the number 289 typed in the source, is a temporary value with no fixed home, so asking for its address makes no sense. C calls things that have a storage location lvalues, and the address operator demands an lvalue.
| Expression | Valid? | Reason |
|---|---|---|
&count | Yes | A named variable has storage |
&table[2] | Yes | An array element has storage |
&config.mode | Yes | A struct member has storage |
&289 | No | A constant has no storage location |
&(a + b) | No | The sum is a temporary value |
®ister_var (declared with register) | No | The register keyword forbids it |
Let the compiler show you. Here is a deliberately broken program:
int main(void)
{
int a = 1, b = 2;
int *p = &(a + b);
int *q = &289;
return 0;
}
Compiling it with GCC gives:
bad.c: In function 'main':
bad.c:4:14: error: lvalue required as unary '&' operand
4 | int *p = &(a + b);
| ^
bad.c:5:14: error: lvalue required as unary '&' operand
5 | int *q = &289;
| ^
Why scanf Needs the Address Operator
You have used the address operator since your first input program without being told. When you write scanf("%d", &temperature);, the function receives the address of temperature, not its value. C passes arguments by value, so if you passed the plain variable, scanf would only get a copy and could never change your original. With the address, it knows exactly where to write the number it reads.
This same idea is the basis for returning more than one result from a function, and we build on it in later lectures. One exception worth knowing: when you read a string into a char array, you write the array name without &, because an array name already stands for the address of its first element. We will explain that fully when arrays and pointers meet.
Printing Addresses Correctly
Old textbooks print addresses with %u and say that this does not mean addresses are unsigned integers, only that no better format was handy. On modern systems there is a better format. Use %p and cast the address to (void *):
| Format | Correct? | Problem |
|---|---|---|
%u | No | On 64-bit systems an address does not fit in an unsigned int, so the value is cut off |
%d | No | Signed, and the same truncation problem |
%lu | Risky | Works on some platforms, not portable |
%p with (void *) | Yes | The portable, standard way |
An address is simply a whole number, but its width depends on the machine. That is why the standard gave us a dedicated format.
Address Operator in Embedded C Programming
Here is how this operator shows up in real c programming for embedded systems work:
- Passing a buffer to a driver. A function such as
uart_send(&frame, sizeof frame)needs the location of the data to transmit. - Reading sensors into variables. A driver call like
sensor_read(&temperature)fills your variable directly, avoiding global state. - DMA setup. Controllers want the memory address of the source or destination buffer, which you obtain with
&or an array name. - Struct fields.
&config.baudlets you hand one field of a configuration structure to a helper function.
Safety rule for firmware
Never take the address of a local variable and keep it after the function returns. The local lives on the stack, and that space is reused by the next function call. The address becomes meaningless, and your firmware may crash or behave randomly. We revisit this in the pitfalls lecture.
Common Mistakes
| Mistake | What happens | Fix |
|---|---|---|
Forgetting & in scanf for a number | Warning at best, crash or garbage at worst | Always & for non-array targets |
Writing &(x + y) | Compile error: lvalue required | Store the sum in a variable first |
Printing an address with %d | Truncated value on 64-bit | Use %p and cast to (void *) |
Confusing &x with x | Wrong value used | &x is the location, x is the content |
| Mixing up bitwise AND with the address operator | Wrong behaviour | Binary a & b is AND; unary &a is address-of |
Two meanings of the same symbol
The symbol & also means bitwise AND when it sits between two operands. The compiler tells them apart from the position: with one operand on its right it is the address operator, with an operand on each side it is AND. You will use both constantly in embedded work, for example to mask a status register.
Best Practices
- Always cast to
(void *)and use%pwhen printing addresses. - Check the return value of
scanfto confirm the input was read. - Do not store the address of a short-lived local variable for later use.
- Compile with warnings on so a missing
&inscanfis reported.
Key Takeaways
- The address operator
&gives the address of a variable or any stored object. - It needs an lvalue, so constants and expressions are not allowed.
- Array elements have neighbouring addresses separated by the element size.
scanfneeds&so it can write into your variable.- Use
%pto print addresses portably.
Conclusion
You can now ask C where any variable lives. That is half of the pointer story. The other half is a place to keep the answer, which is what a pointer variable is. In the next lecture of this free C programming course we declare our first pointers and look at the notation that confuses so many beginners. Before moving on, try the demo again with a char array and confirm that the gap is 1 byte, and with a double array to see 8.
Frequently Asked Questions
What does the & operator do in C?
Placed before a variable it returns the address of that variable. It is called the address-of operator.
Why can’t I take the address of a constant?
A constant such as 289 is a value written in the code, not an object stored in memory, so there is no address to return. The compiler reports that an lvalue is required.
Why does scanf need & before the variable name?
C passes arguments by value. scanf must change your variable, so you give it the variable’s address instead of a copy.
Why do array strings in scanf not need &?
An array name already converts to the address of its first element, so adding & is unnecessary for reading into a char array.
How do I print an address in C?
Use printf("%p", (void *)&variable);. It is the portable way and works on both 32-bit and 64-bit systems.
Is the address operator used in embedded C programming?
Yes, constantly: passing buffers to drivers, filling variables from sensor functions and configuring DMA all rely on addresses.
Keep Going with the Free C Tutorial
Next: declare pointer variables, understand the asterisk notation and see why every pointer needs a base type.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments