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:
- In a declaration, it means "this variable is a pointer".
- 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:
xis anint&xis the address ofxpis a pointer toint*pis theintvalue stored at the address insidep
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);
&xgives the address ofx- the function receives that address in
p *p = 0changes the originalx
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
xis the actual house. &xis the street address of that house.- A pointer like
pis a piece of paper storing that address. *pmeans "go to the address written on the paper and enter the house".
Example with the analogy
int x = 42;
int *p = &x;
This means:
xis a house containing42&xis the address of that housepstores that address*plets 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.
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;
&xgets the address ofxpstores 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
&xandprepresent the same address*paccesses the value stored at that address- changing
*pchangesx
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
arroften acts like&arr[0]*arrmeans 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
&xpasses the address ofxpreceives that address*paccesses 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;
&xgets the address ofxptrstores that addressptrnow points tox
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;
*pon the right reads the value at the address:66 * 2gives12*pon the left writes12back 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
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:
pdoes 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_zeroexpects an addressxis 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:
pis 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};
arroften behaves like pointer to first element&arr[0]is the address of the first element&arris 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
| Concept | Meaning | Example | Notes |
|---|---|---|---|
x | the value/object itself | int x = 10; | normal variable |
&x | address of x | int *p = &x; | use when you need a pointer to x |
p | pointer value (an address) | int *p = &x; | stores where something lives |
*p | value at the address in p | printf("%d", *p); | dereference |
arr vs &arr[0] vs &arr
| Expression | Meaning | Typical type |
|---|---|---|
arr | usually acts like address of first element | int * in many expressions |
&arr[0] | address of first element | int * |
&arr | address of the whole array | int (*)[N] |
Pointer parameter vs normal parameter
| Function style | What gets passed | Can change caller's variable? |
|---|---|---|
void f(int x) | copy of value | No |
void f(int *x) | copy of address | Yes, through *x |
* in declaration vs * in use
| Context | Example | Meaning |
|---|---|---|
| Declaration | int *p; | p is a pointer to int |
| Expression | *p = 10; | access or modify the pointed value |
Cheat Sheet
Quick rules
&var-> address ofvar*ptr-> value stored at the address inptrtype *ptr;-> declares a pointer- pass
&xto a function when it needs to modifyx - use
*pinside 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
*pifppoints to valid memory - use
%pto print addresses - cast to
(void *)when printing addresses withprintf
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.
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
- Create a function that increments an integer using a pointer parameter.
- Create a function that swaps two integers using pointer parameters.
- Create a function that prints the first element of an integer array.
- In
main, declare variables and call the functions correctly using&where needed. - Print results before and after the changes to verify that the original variables were modified.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.