Nearby lessons

86 of 124

C - Memory Address

Every variable lives at a numbered location in memory. Learn what a memory address is, how the & operator reveals it, how to print one with %p, and how addresses relate to a variable's size.

Memory Is a Numbered Row of Bytes

Think of RAM as a very long street where every byte has a house number. When you declare int x = 42; the compiler reserves a few consecutive bytes and remembers the number of the first one.

AddressContentsBelongs to
0x7ffd100042int x (4 bytes)
0x7ffd1004'A'char c (1 byte)
0x7ffd10083.14float f (4 bytes)
In simple words: the variable name is a label you invented for the compiler's benefit. The address is where the data actually sits. Pointers exist so you can work with that address directly.

The & Address-Of Operator

Put & in front of a variable to get its address instead of its value:

Example02
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int x = 42;
6 char c = 'A';
7 float f = 3.14f;
8 
9 printf("x = %d at address %p\n", x, (void *) &x);
10 printf("c = %c at address %p\n", c, (void *) &c);
11 printf("f = %.2f at address %p\n", f, (void *) &f);
12 return 0;
13}
Output
x = 42  at address 0x7ffd4a2b1c4c
c = A  at address 0x7ffd4a2b1c4b
f = 3.14 at address 0x7ffd4a2b1c44

Printing Addresses with %p

%p is the correct specifier for an address, and the standard expects a void * — so cast. Using %d instead is wrong and may print garbage on 64-bit systems:

Example03
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int x = 100;
6 
7 printf("Correct : %p\n", (void *) &x);
8 
9 /* printf("Wrong: %d\n", &x);
10 warning: format '%d' expects 'int' but argument is 'int *' */
11 
12 printf("As number: %lu\n", (unsigned long) &x);
13 return 0;
14}
Output
Correct  : 0x7ffee3b4a8ac
As number: 140732686919852

Size Determines the Spacing

Declare an array and the addresses step forward by exactly sizeof(element) each time — proof that memory is contiguous:

Example04
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int a[4] = {10, 20, 30, 40};
6 int i;
7 
8 printf("int is %zu bytes\n\n", sizeof(int));
9 for (i = 0; i < 4; i++)
10 printf("a[%d] = %2d at %p\n", i, a[i], (void *) &a[i]);
11 return 0;
12}
Output
int is 4 bytes

a[0] = 10  at 0x7ffd8c1a2340
a[1] = 20  at 0x7ffd8c1a2344
a[2] = 30  at 0x7ffd8c1a2348
a[3] = 40  at 0x7ffd8c1a234c

Different Types, Different Steps

A char array steps by 1, a double array by 8. The type is what tells C how far to move:

Example05
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 char c[3] = {'a', 'b', 'c'};
6 double d[3] = {1.1, 2.2, 3.3};
7 int i;
8 
9 printf("char (%zu byte):\n", sizeof(char));
10 for (i = 0; i < 3; i++) printf(" c[%d] at %p\n", i, (void *) &c[i]);
11 
12 printf("double (%zu bytes):\n", sizeof(double));
13 for (i = 0; i < 3; i++) printf(" d[%d] at %p\n", i, (void *) &d[i]);
14 return 0;
15}
Output
char (1 byte):
  c[0] at 0x7ffd1234abc0
  c[1] at 0x7ffd1234abc1
  c[2] at 0x7ffd1234abc2
double (8 bytes):
  d[0] at 0x7ffd1234abd0
  d[1] at 0x7ffd1234abd8
  d[2] at 0x7ffd1234abe0

Addresses Change Every Run

Modern operating systems randomise where your program's memory lands, for security. Run the same program twice and you will see different numbers:

Example06
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int x = 5;
6 
7 printf("Address of x: %p\n", (void *) &x);
8 printf("(Different on the next run - this is normal)\n");
9 return 0;
10}
Output
Run 1: Address of x: 0x7ffd4a2b1c4c
Run 2: Address of x: 0x7ffc8e1f3a2c

Where Different Variables Live

Globals, locals and heap allocations sit in distinct regions, and their addresses reflect that:

Example07
CCode Cell
1#include <stdio.h>
2#include <stdlib.h>
3 
4int globalVar = 100; /* data segment */
5 
6int main()
7{
8 int localVar = 200; /* stack */
9 static int staticVar = 300; /* data segment */
10 int *heapVar = malloc(sizeof(int)); /* heap */
11 
12 *heapVar = 400;
13 
14 printf("global : %p\n", (void *) &globalVar);
15 printf("static : %p\n", (void *) &staticVar);
16 printf("local : %p\n", (void *) &localVar);
17 printf("heap : %p\n", (void *) heapVar);
18 
19 free(heapVar);
20 return 0;
21}
Output
global : 0x55a4c8e04010
static : 0x55a4c8e04014
local  : 0x7ffd3c2a1b4c
heap   : 0x55a4ca1f52a0

Why scanf Needs the &

This is the first place every C learner meets addresses. scanf must write into your variable, so it needs to know where the variable is — not what it currently holds:

Example08
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int age;
6 char name[50];
7 
8 printf("Enter age: ");
9 scanf("%d", &age); /* & needed: scanf must write here */
10 
11 printf("Enter name: ");
12 scanf("%49s", name); /* no & : an array name IS an address */
13 
14 printf("%s is %d\n", name, age);
15 return 0;
16}
Output
Enter age: 25
Enter name: Rahul
Rahul is 25

Comparing Addresses

Addresses are numbers, so you can compare them. Within one array this is well-defined and genuinely useful:

Example09
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int a[5] = {1, 2, 3, 4, 5};
6 
7 printf("&a[0] == a : %s\n", (&a[0] == a) ? "yes" : "no");
8 printf("&a[3] > &a[1] : %s\n", (&a[3] > &a[1]) ? "yes" : "no");
9 printf("Bytes between them : %ld\n",
10 (long) ((char *) &a[3] - (char *) &a[1]));
11 printf("Elements between : %ld\n", (long) (&a[3] - &a[1]));
12 return 0;
13}
Output
&a[0] == a          : yes
&a[3] > &a[1]       : yes
Bytes between them  : 8
Elements between    : 2

Common Mistakes

  • Forgetting & in scanfscanf("%d", age) treats the value as an address and corrupts memory.
  • Adding & to an array in scanf — an array name already is an address.
  • Printing with %d — use %p with a (void *) cast.
  • Hard-coding an address — they change every run.
  • Taking the address of a literal&5 is meaningless; only objects have addresses.
  • Keeping the address of a local after its function returns — the storage is gone.
scanf("%d", age) is the classic. If age happens to hold 42, scanf writes an integer to address 42 — memory your program does not own. It crashes immediately, or worse, appears to work. Build with -Wall and the compiler will flag the format mismatch every time.
📝 Key Takeaways
  • Memory is a numbered sequence of bytes; an address is one of those numbers.
  • &variable gives the address of variable.
  • Print an address with %p and a (void *) cast.
  • A variable occupies sizeof(type) consecutive bytes.
  • Addresses change between runs — never hard-code one.

🧠 Test Your Knowledge

4 Questions
Progress: 0 / 4