← Back to C++ Course | Chapter 7: Pointers & References | Lesson 9 of 11

C++ Reference vs Pointer

Syntax Differences

Pointers use an asterisk (*) for declaration and dereferencing and an ampersand (&) to retrieve a variable's address, while references use only the ampersand at declaration and then behave exactly like a normal variable, with no special dereference syntax needed afterward.

Example: Syntax Differences

cpp
#include <iostream>

int main() {
	int value = 5;
	int *ptr = &value;
	int &ref = value;
	std::cout << *ptr << " " << ref << std::endl;
	return 0;
}

Nullability

Pointers can be assigned nullptr to represent an empty or not-yet-initialized state, which is essential for optional or lazily-set data. References cannot be null — they must be bound to a valid variable the moment they're created, which rules out an entire category of null-pointer bugs.

Example: Nullability

cpp
#include <iostream>

int main() {
	int *ptr = nullptr; // valid: represents "no value"
	int value = 5;
	int &ref = value; // references must always refer to something
	std::cout << ref << std::endl;
	return 0;
}

Reassignment

Pointers can be reassigned to point to a different variable at any time during their lifetime, making them suitable for iteration and dynamic data structures. References are bound to their target at initialization and can never be redirected to refer to anything else afterward.

Example: Reassignment

cpp
#include <iostream>

int main() {
	int a = 1, b = 2;
	int *ptr = &a;
	ptr = &b; // pointers can be reassigned
	std::cout << *ptr << std::endl;
	return 0;
}

Pass-by-Value vs Reference vs Pointer

Pass-by-value copies the argument entirely, which is safe but can be expensive for large objects. Pass-by-pointer copies just the memory address but requires the function to check for nullptr. Pass-by-reference shares the original variable directly with clean syntax and no null-check burden.

Example: Pass-by-Value vs Reference vs Pointer

cpp
#include <iostream>

void byValue(int x) { x = 100; }
void byReference(int &x) { x = 100; }
void byPointer(int *x) { *x = 100; }

int main() {
	int a = 1, b = 1, c = 1;
	byValue(a);
	byReference(b);
	byPointer(&c);
	std::cout << a << " " << b << " " << c << std::endl;
	return 0;
}

Use Cases

Use references by default for clean function parameters and for operator overloading, where the syntax reads naturally. Reach for pointers instead when you need to represent an optional/absent value with nullptr, perform pointer arithmetic, or manage memory you allocated on the heap yourself.

Example: Use Cases

cpp
#include <iostream>

void printValue(const int &x) { // reference: clean parameter syntax
	std::cout << x << std::endl;
}

int main() {
	int arr[] = {1, 2, 3};
	int *ptr = arr; // pointer: needed for array iteration
	printValue(*ptr);
	return 0;
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.