No history yet

Memory Addresses

Memory and Addresses

Think of your computer's memory (RAM) as a massive, linear street of tiny boxes, or bytes. Each byte has a unique, numbered address, just like houses on a street. When you declare a variable, like int age = 30;, the computer finds an empty set of boxes, puts the value 30 inside, and associates the name age with that location.

But what is that location's actual address? You can find out using the address-of operator, &. Placing it before a variable's name gives you the memory address where that variable's data is stored.

#include <stdio.h>

int main() {
    int score = 150;

    // Print the value of the variable
    printf("Value of score: %d\n", score);

    // Print the memory address of the variable
    // %p is the format specifier for addresses
    printf("Address of score: %p\n", &score);

    return 0;
}

The output for the address will be a long string of letters and numbers, like 0x7ffee76c3abc. This is a hexadecimal number, which is a common way to represent memory addresses. The exact address will change every time you run the program, as the operating system allocates memory differently each time.

Lesson image

Pointers as Address Variables

Since a memory address is just a number, we can store it in a variable. A variable specifically designed to hold a memory address is called a pointer. It

points to

the location of another piece of data.

To declare a pointer, you specify the type of data it will point to, followed by an asterisk * and the pointer's name. For example, int *ptr; declares a pointer named ptr that is intended to hold the address of an integer.

#include <stdio.h>

int main() {
    int level = 10;
    int *level_ptr; // Declare a pointer to an integer

    level_ptr = &level; // Assign the address of 'level' to the pointer

    printf("Address of level variable: %p\n", &level);
    printf("Address stored in level_ptr: %p\n", level_ptr);

    return 0;
}

Notice that &level and level_ptr print the exact same address. The pointer is now a signpost, holding the location of our original variable.

Reading and Writing with Pointers

Holding an address is useful, but the real power comes from accessing the data at that address. This is done with the dereference operator, which is also an asterisk *. When you place * before a pointer's name, you're not asking for the address it holds, but for the value at that address.

This lets you both read and modify a variable indirectly, without using its original name.

#include <stdio.h>

int main() {
    int health = 100;
    int *health_ptr = &health; // Pointer holds the address of health

    // Use dereference operator to READ the value
    printf("Value of health (via pointer): %d\n", *health_ptr);

    // Use dereference operator to WRITE a new value
    printf("Taking 20 damage...\n");
    *health_ptr = 80; // This changes the value of 'health' itself

    printf("Original health variable is now: %d\n", health);

    return 0;
}

The same * symbol is used for both declaring a pointer and it. The compiler understands the difference based on the context. If it's in a declaration (int *p;), it's creating a pointer. If it's used with an existing pointer (*p = 10;), it's accessing the value.

Key distinction: health_ptr is the memory address. *health_ptr is the integer value (100, then 80) stored at that address.

The Null Pointer

What happens if a pointer doesn't point to anything? An uninitialized pointer contains a random, garbage address. Trying to read from or write to a random address is a common cause of program crashes.

To handle this safely, we have the concept of a null pointer. It's a special value that guarantees the pointer isn't pointing to any valid memory location. In C, you can assign this using the NULL macro.

#include <stdio.h>
#include <stdlib.h> // NULL is defined here

int main() {
    int *safe_ptr = NULL;

    // It's good practice to check if a pointer is NULL
    // before trying to dereference it.
    if (safe_ptr != NULL) {
        *safe_ptr = 50; // This line will NOT run
    }

    printf("The program did not crash.\n");

    return 0;
}

Initializing pointers to NULL is a fundamental safety habit in C programming. It creates a predictable state, preventing your program from accessing memory it shouldn't.

Quiz Questions 1/5

In C programming, what is the primary purpose of the address-of operator (&) when placed before a variable name?

Quiz Questions 2/5

Which of the following correctly declares a pointer named p_score that is intended to hold the address of a float variable?

Pointers are a foundational concept. They give you direct, low-level control over memory, which is a key feature of C.