Question

When to Use & and * with Pointers in C

cpointers

Question

I am just starting to learn pointers in C, and I am confused about when to use & and *.

I understand that & gives the address of a variable, and * can be used with a pointer to access the value stored at the address it points to. However, things seem different when working with arrays, strings, or when passing pointers to functions. Because of that, I am having trouble seeing a consistent pattern.

When should I use & and * in C?

Short Answer

By the end of this page, you will understand the core roles of & and * in C, how they behave with normal variables, pointers, arrays, strings, and functions, and how to recognize the pattern behind pointer syntax instead of memorizing special cases.

Concept

In C, & and * are closely related operators used to work with memory addresses.

  • & means "give me the address of this object".
  • * means "work with the object stored at this address".

The confusion usually comes from the fact that * has two different jobs depending on where it appears:

  1. In a declaration, it means "this variable is a pointer".
  2. In an expression, it means "dereference this pointer" and access the value being pointed to.

The core idea

A normal variable stores a value:

int x = 10;

A pointer stores the address of another object:

int *p = &x;

Here is what happens:

  • x is an int
  • &x is the address of x
  • p is a pointer to int
  • *p is the int value stored at the address inside p

So these are connected:

p = &x;
*p == x;

Why this matters

Pointers are fundamental in C because they are used for:

  • modifying values inside functions
  • working with arrays efficiently
  • handling strings
  • dynamic memory allocation
  • building data structures like linked lists and trees
  • interacting with low-level memory and system APIs

The pattern behind the syntax

A useful way to think about it is:

  • Use & when you have a real variable and need its address.
  • Use * when you have an address in a pointer and need the value at that address.

For example:

int x = 5;
int *p = &x;

printf("%d\n", x);   // value of x
printf("%p\n", (void*)&x); // address of x
printf("%p\n", (void*)p);  // same address stored in p
printf("%d\n", *p);  // value at that address

Why arrays and strings feel different

Arrays are a common source of confusion because an array name often behaves like a pointer to its first element.

int arr[3] = {10, 20, 30};

In many expressions:

arr

acts like:

&arr[0]

That is why you often do not write & when passing an array to a function.

Strings in C are usually character arrays or pointers to characters, so they follow the same idea:

char str[] = "hi";
char *p = str;

Here str already behaves like a pointer to the first character in many contexts.

Function calls and pointers

C passes arguments by value. That means a function receives a copy of what you pass.

If you want a function to change a variable in the caller, you pass the variable's address:

void set_to_zero(int *p) {
    *p = 0;
}

int x = 5;
set_to_zero(&x);
  • &x gives the address of x
  • the function receives that address in p
  • *p = 0 changes the original x

This is one of the most important real uses of & and *.

Mental Model

Think of a variable as a house, and a pointer as a note containing the house address.

  • The variable x is the actual house.
  • &x is the street address of that house.
  • A pointer like p is a piece of paper storing that address.
  • *p means "go to the address written on the paper and enter the house".

Example with the analogy

int x = 42;
int *p = &x;

This means:

  • x is a house containing 42
  • &x is the address of that house
  • p stores that address
  • *p lets you read or change what is inside the house
*p = 100;

Now the house x contains 100.

Why arrays seem special

An array is like a row of houses standing next to each other.

int arr[3] = {10, 20, 30};

The name arr often acts like the address of the first house in the row.

So:

arr

usually means "the address of arr[0]".

That is why arrays often seem to skip the & step: the array name already gives you access to the starting address in many expressions.

Take Quiz

Syntax and Examples

Basic syntax

1. Declaring a pointer

int *p;

This means p is a pointer to int.

2. Storing an address in a pointer

int x = 10;
int *p = &x;
  • &x gets the address of x
  • p stores that address

3. Reading the value through a pointer

printf("%d\n", *p);

This prints 10.

4. Writing through a pointer

*p = 25;
printf("%d\n", x);

This changes x to 25.

Full beginner example

#include <stdio.h>

int main(void) {
    int x = 10;
    int *p = &x;

    printf("x = %d\n", x);
    printf("&x = %p\n", (void*)&x);
    printf("p = %p\n", (void*)p);
    printf("*p = %d\n", *p);

    *p = 99;
    printf("After *p = 99, x = %d\n", x);

    return 0;
}

What this shows

  • &x and p represent the same address
  • *p accesses the value stored at that address
  • changing *p changes x

Arrays

#include <stdio.h>

int main(void) {
    int arr[3] = {10, 20, 30};

    printf("arr[0] = %d\n", arr[0]);
    printf("*arr = %d\n", *arr);
    printf("*(arr + 1) = %d\n", *(arr + 1));

    return 0;
}

Explanation

  • arr often acts like &arr[0]
  • *arr means the first element
  • *(arr + 1) means the second element

Functions

#include <stdio.h>

void add_one(int *p) {
    *p = *p + 1;
}

int main(void) {
    int x = 7;
    add_one(&x);
    printf("x = %d\n", x);
    return 0;
}

Explanation

  • &x passes the address of x
  • p receives that address
  • *p accesses and updates the original value

Strings

#include <stdio.h>

int main(void) {
    char str[] = "cat";
    char *p = str;

    printf("%c\n", *p);
    printf("%c\n", *(p + 1));

    return 0;
}

str behaves like a pointer to the first character in many expressions, so p = str; works without &.

Step by Step Execution

Consider this example:

#include <stdio.h>

void double_value(int *p) {
    *p = *p * 2;
}

int main(void) {
    int x = 6;
    int *ptr = &x;

    double_value(ptr);

    printf("x = %d\n", x);
    return 0;
}

Step-by-step trace

1. int x = 6;

A normal integer variable x is created and stores 6.

2. int *ptr = &x;

  • &x gets the address of x
  • ptr stores that address
  • ptr now points to x

3. double_value(ptr);

The value of ptr is passed to the function.

Important: C still passes by value here. The function gets a copy of the pointer, but that copy still contains the address of x.

4. Inside double_value(int *p)

The parameter p receives the copied address.

So p points to the same x as ptr.

5. *p = *p * 2;

  • *p on the right reads the value at the address: 6
  • 6 * 2 gives 12
  • *p on the left writes 12 back to that same address

Now x becomes 12.

6. printf("x = %d\n", x);

This prints:

x = 12

Key lesson

Even though the pointer itself was copied when passed to the function, both the original pointer and the copied pointer refer to the same memory location. That is why dereferencing the pointer lets the function modify the original variable.

Real World Use Cases

Modifying values inside functions

Pointers let a function update caller-owned data.

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

Used for:

  • swapping numbers
  • returning multiple results
  • updating counters or flags

Working with arrays efficiently

Arrays are often passed to functions as pointers to the first element.

int sum(int *arr, int size) {
    int total = 0;
    for (int i = 0; i < size; i++) {
        total += arr[i];
    }
    return total;
}

Used in:

  • statistics
  • parsing input
  • image and signal processing

Handling strings

C strings are arrays of characters, so pointers are everywhere.

void print_first_char(char *s) {
    if (s != NULL) {
        printf("%c\n", *s);
    }
}

Used for:

  • user input
  • file parsing
  • command-line argument handling

Dynamic memory

Functions like malloc return pointers.

int *p = malloc(sizeof(int));

You use pointers to access allocated memory.

System programming and APIs

Many C library and operating system APIs use pointers for:

  • buffers
  • output parameters
  • memory-mapped data
  • device communication
Take Quiz

Real Codebase Usage

In real C projects, developers do not use pointers only in toy examples. They use them as part of common patterns.

Output parameters

A function may return success or failure and write the actual result through a pointer.

int divide(int a, int b, int *result) {
    if (b == 0) {
        return 0;
    }
    *result = a / b;
    return 1;
}

This pattern is common when a function needs to return:

  • a status code
  • computed output
  • multiple values

Guard clauses for pointer safety

Real code often checks pointers before dereferencing them.

void print_number(const int *p) {
    if (p == NULL) {
        return;
    }
    printf("%d\n", *p);
}

Array processing APIs

Many functions accept a pointer plus a size.

int find_max(const int *arr, int size) {
    if (arr == NULL || size <= 0) {
        return 0;
    }

    int max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}

const with pointers

Real code frequently uses const to document intent.

void show_message(const char *msg) {
    printf("%s\n", msg);
}

This means the function reads the string but does not modify it.

In-place updates

Pointers are used when code should update existing data instead of creating a copy.

Examples:

  • sorting an array
  • filling a buffer
  • updating a struct
  • toggling a flag inside shared state

Common Mistakes

1. Dereferencing an uninitialized pointer

Broken code:

int *p;
*p = 10;

Why it is wrong:

  • p does not point to valid memory
  • dereferencing it causes undefined behavior

Fix:

int x = 10;
int *p = &x;

2. Forgetting & when a function expects a pointer

Broken code:

void set_zero(int *p) {
    *p = 0;
}

int x = 5;
set_zero(x);

Why it is wrong:

  • set_zero expects an address
  • x is an integer value, not an address

Fix:

set_zero(&x);

3. Forgetting * when accessing the pointed value

Broken code:

int x = 5;
int *p = &x;
printf("%d\n", p);

Why it is wrong:

  • p is an address, not the integer value

Fix:

printf("%d\n", *p);

If you want to print the address itself, use %p:

printf("%p\n", (void*)p);

4. Confusing arr, &arr[0], and &arr

int arr[3] = {1, 2, 3};
  • arr often behaves like pointer to first element
  • &arr[0] is the address of the first element
  • &arr is the address of the whole array

These are related but not the same type.

5. Thinking arrays are pointers

Arrays and pointers are closely related, but they are not identical.

Broken assumption:

char str[] = "hi";
sizeof(str);   // this is not the same as sizeof(char *)

str is an array here, not just a pointer variable.

6. Using * in declarations and expressions as if they mean the same thing

int *p;   // declaration: p is a pointer
int x = *p; // expression: read the value p points to

Same symbol, different role depending on context.

7. Dereferencing NULL

Broken code:

int *p = NULL;
printf("%d\n", *p);

Fix: always ensure a pointer points to valid memory before dereferencing it.

Comparisons

ConceptMeaningExampleNotes
xthe value/object itselfint x = 10;normal variable
&xaddress of xint *p = &x;use when you need a pointer to x
ppointer value (an address)int *p = &x;stores where something lives
*pvalue at the address in pprintf("%d", *p);dereference

arr vs &arr[0] vs &arr

ExpressionMeaningTypical type
arrusually acts like address of first elementint * in many expressions
&arr[0]address of first elementint *
&arraddress of the whole arrayint (*)[N]

Pointer parameter vs normal parameter

Function styleWhat gets passedCan change caller's variable?
void f(int x)copy of valueNo
void f(int *x)copy of addressYes, through *x

* in declaration vs * in use

ContextExampleMeaning
Declarationint *p;p is a pointer to int
Expression*p = 10;access or modify the pointed value
Take Quiz

Cheat Sheet

Quick rules

  • &var -> address of var
  • *ptr -> value stored at the address in ptr
  • type *ptr; -> declares a pointer
  • pass &x to a function when it needs to modify x
  • use *p inside the function to read or write the original value

Common patterns

int x = 5;
int *p = &x;
printf("%d\n", *p);   // value of x
printf("%p\n", (void*)p); // address of x
*p = 10;  // changes x

Arrays

int arr[3] = {1, 2, 3};
arr      // usually like &arr[0]
*arr     // arr[0]

Functions

void change(int *p) {
    *p = 20;
}

int x = 5;
change(&x);

Strings

char str[] = "abc";
char *p = str;

str often acts like pointer to first character.

Safety rules

  • never dereference an uninitialized pointer
  • never dereference NULL
  • only use *p if p points to valid memory
  • use %p to print addresses
  • cast to (void *) when printing addresses with printf

Memory idea

  • variable -> stores a value
  • pointer -> stores an address
  • dereference -> follow the address to reach the value

FAQ

When do I use & in C?

Use & when you need the address of an existing variable, usually to store it in a pointer or pass it to a function that expects a pointer.

When do I use * in C?

Use * in a declaration to declare a pointer, and use * in an expression to access the value stored at the address held by that pointer.

Why do arrays often work without &?

Because in many expressions, an array name automatically behaves like a pointer to its first element.

Is an array the same as a pointer in C?

No. They are related, and arrays often decay to pointers in expressions, but they are different types.

Why do functions use pointers to change variables?

C passes arguments by value. Passing a pointer gives the function the address of the original variable, so it can modify the original data through dereferencing.

What does *p = 5 mean?

It means "store 5 in the object pointed to by p". It changes the value at that memory location.

What is the difference between p and *p?

p is the address. *p is the value stored at that address.

Why is &arr different from arr?

arr usually refers to the first element's address in expressions, while &arr is the address of the whole array object.

Take Quiz

Mini Project

Description

Build a small C program that updates variables through pointers and processes an array through a function. This project helps you practice when to pass a normal value, when to pass an address with &, and when to use * inside a function to modify the original data.

Goal

Create a program that increments a number, swaps two values, and prints the first element of an array using pointer-based functions.

Requirements

  1. Create a function that increments an integer using a pointer parameter.
  2. Create a function that swaps two integers using pointer parameters.
  3. Create a function that prints the first element of an integer array.
  4. In main, declare variables and call the functions correctly using & where needed.
  5. Print results before and after the changes to verify that the original variables were modified.
Take Quiz

Keep learning

Related questions

View more