One of the major ways that C differs from languages like Java is that it allows for direct manipulation of something called “pointers.” Pointers are references to specific memory spaces, allowing for direct memory control and management. 

Many programmers moving into C spend a lot of time on pointers — how to use them, how to update them, how to control them, and (more importantly) the fundamentals of how they work. With great power comes great responsibility. In languages such as Java, pointers are not exposed because pointers can become an easy way to make a program unpredictable and chaotic — the ability to directly access pointers also makes it possible to manipulate pointers incorrectly.

C Programming For Beginners

Last Updated August 2019

  • 76 lectures
  • All Levels
4.4 (3,812)

Learn C in ten easy steps on Windows, Mac OS X or Linux | By Huw Collingbourne

Explore Course

At the same time, being able to manipulate pointers directly means that programmers in C have more control over how they allocate memory and control data. For a C programmer, a deeper understanding of pointers and memory allocation is absolutely essential.

What are pointers in C?

When a person forms a new memory, that memory is physically stored through the person’s neural network. This process creates new connections between the person’s neurons. It may be strange to consider, but a person’s memories are in a physical location in their mind — just a very distributed and (to us) opaque one. 

When dealing with computers, the direct accessibility of individual memories is a little more direct. Every piece of data a computer stores is in a specific memory address. “Pointers” provide a reference to these locations.

brown wooden letter c decor

In the old days of computing, pointers were very simple. A system that has only 54 pieces of potential data, for instance, can only point to 1 through 54. So, a piece of data might be at space “3” or space “53.” Today, the memory allocation for programs is much more complex.

In C, pointers are generally used for direct memory management. When memory management functions poorly, the consequence is usually what’s called a “memory leak.” The longer the program runs, the more memory it uses until the program itself might crash. By using pointers, developers can take direct control over the data that they store — manipulating and freeing up that data space as needed.

Why are pointers potentially dangerous?

Before learning about pointers, developers should understand the risks of working with them. Pointers can be chaotic and unwieldy in terms of programming; because they are so close to the machine layer of the code, they are both powerful and dangerous.

C was initially created in 1972, though it wasn’t the language as we know it now until the 1980s. Modern programming languages provide significantly greater levels of automation and abstraction than C. They do all the memory allocation, management, and handling for the developer. While this takes some control away from the developer, it enhances the consistency and stability of the end product.

In C, inexperienced programmers may incorrectly allocate or manipulate memory, leading to both security and stability issues. If a programmer does not deeply understand how pointers work, it can lead to a complex and unstable program. If programmers don’t properly comment on their code, it can become unreadable and unmaintainable to others.

Fundamentally, many programmers are operating at a higher level of abstraction and don’t necessarily need to do memory management on their own. But the more complex a software solution is, the more important memory management actually becomes.

Nevertheless, C programmers will need to learn about pointers and memory management before they can develop larger programs. For larger programs, memory management can be a critical aspect of ensuring platform stability — it just requires that programmers be extremely conscientious and detail-oriented. 

How do you create pointers in C?

Understanding pointers begins with how to create them. Whenever you create a variable in C, it creates a memory address. Every variable that you create has not only a type, but also a location. Consider the following example:

#include <stdio.h>

int main() {
	int var1;
}

In the above example, we created an “integer” called “var1”. It’s empty for now; we didn’t give it a value. We didn’t even try to do anything with it. But despite that, because we declared the integer, it already has a memory address.

How do we access that stored address? By using the & (ampersand) in front of the variable, which lets us print it out in regular strings in C. Let’s print our memory address:

int main() {
		int var1;
		printf(“%x”,&var1);
	}

“%x” is the method of using “printf()” to print a string containing this memory location; you can learn more about it in our article on Strings in C. What follows will look random to us; the allocated memory will be something like “0x7fff9575c05f.” Memory addresses are steadily becoming longer and more complex. But that doesn’t matter because we don’t need to track the allocated memories manually; that’s what the pointer variable is for.

So, you don’t need to directly declare a pointer. In this example, the int pointer created itself — and that would be true of any data type, not just type int. With any type, you’ll be able to assign the address initially automatically, and the & sign combined with the printf() function prints the address. As long as you know this basic pointer syntax, you can do quite a lot.

But just because you don’t need to directly declare a pointer doesn’t mean that you won’t ever need to. It’s possible to declare a pointer directly, like so:

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

This would create a new pointer under “newPointer” that has a size of an integer. You can then alter, reference, and eventually delete this pointer variable. 

How can you reference a pointer in C?

C references a pointer with a * in front of it in code. So, in the above example, you could do this:

Int *newPointer = malloc(sizeof(int));
	printf(*newPointer);

You might think this would print the value of newPointer, but it wouldn’t be able to anyway; the pointer isn’t pointing to any value yet. This action simply allocates it. The asterisk (*) before the reference means the printf() function will print the pointer’s address, rather than hold the value at that address. An asterisk is generally used with a variable’s name to reference the pointer directly rather than the variable itself.

This applies to variables that are not arrays. An array is already essentially a pointer; by default, the array points to the [0] (base) value of the array. Thus, it’s possible to use array names as constant pointers. To access different elements of the array, you will need to use a pointer as an array. 

How do you change the value of a pointer in C?

There are two things you can do in C: change the value of a pointer and reassign pointers to a different address. While this may seem like the same thing, they’re actually very different. Consider the “pointer” to be the door that you open and the value of the pointer to be what is behind that door.

Changing the value of a pointer is no different from simply changing the value of the variable, because the variable will remain stored where the variable is stored. In the following example:

Int newPointer = malloc(sizeof(int));
newPointer = 0;
printf(newPointer);
printf(“\n”);
printf(*newPointer);
printf(“\n”);
newPointer = 1;
printf(newPointer);
printf(“\n”);
printf(*newPointer);

You will see that the newPointer’s pointer does not change at all, even though the value does. The results will likely be similar to:

0
0x7fff9575c05f
1
0x7fff9575c05f

The value that the pointer is referencing changes all the time. What is more important is how to change the pointers themselves.

How do you reassign pointers to a different address?

As noted, the pointer doesn’t change when the value does. But a pointer switch can change where a pointer points to.

Let’s go back to newPointer. 

int newPointer = 1;

“*newPointer” is a memory location, but “newPointer” (without an asterisk) is simply a variable. Right now, “*newPointer” points to a memory location, and that memory location reads “1.”

Now, let’s say we do this:

int firstPointer = 1;
int secondPointer = 2;

We have a firstPointer variable that says “1” and a secondPointer variable that says “2.” But we can change that with the following code:

*firstPointer = *secondPointer;

You’ve now given “firstPointer” the address of a different variable.

Now, there are a few interesting things to consider. First, no values changed. What happened is that firstPointer now points to the same memory address as secondPointer. When you call firstPointer next, it will read “2” and not “1.” 

Second, there no longer exists a pointer to the memory space that referenced “1.” This can be bad because the memory wasn’t freed. It was simply de-referenced.

Since it’s possible to reference pointers using asterisks (*), it’s possible to manipulate them much like any other variable. But this can be dangerous because you’re not directly changing the values of any variables. Instead, you’re changing where a given variable points to. The first int ptr doesn’t exist, and the integer variable has changed, but the integer itself is still stored somewhere. 

Using function pointers in C

In addition to using pointers to variables, you can also use pointers to functions. But while a variable pointer points to a value is stored, a function pointer points to the code of the function.

With a function pointer, you aren’t allocating or freeing up memory. You are simply pointing to a location within the code where the function is already present. Otherwise, functions are referred to like a variable pointer, with an asterisk before the function name.

It’s possible to use a function pointer instead of a “switch case,” because a function pointer literally points to the function intended for use. For example, you could build a function pointer array from pointers to functions that you want to move through. But this is generally considered to be bad form, not because it isn’t usable, but because it isn’t very readable. It can be difficult for another programmer reading the code to understand what is happening.

You would declare a function pointer as so:

int (*functionPointer)(int);

When declaring a function pointer, you need to wrap it in parentheses. Otherwise, you can treat the functionPointer as another type of pointer.

Using pointer arithmetic

Pointers can have integers added or subtracted to them, which would lead to a different pointer. By adding or subtracting to pointers, you can also use the pointers as an array. 

For example:

Int pointerArray [10];
pointerArray[0] == *(pointerArray + 1);

This moves the address of the pointer up by four bytes of memory. Why? Because an int takes up four bytes of space.

This type of pointer arithmetic is not intuitive except to those who have in-depth knowledge of machine code, such as assembly. 

Very few programmers will need to use pointer arithmetic, and while it’s a good foundation for knowledge, it’s not necessarily useful for most. Additionally, memory access via pointer arithmetic is often considered to be fundamentally unsafe — it can allow developers to access and manipulate memory spaces that they shouldn’t tamper with.

Dereferencing a pointer in C

Once you have finished using a pointer, you might want to destroy it. There’s no “delete” function in C. What you can do is free() the allocated memory. You would do this as follows:

char *variablePointer;
variablePointer = (char *) malloc(20);
free(variablePointer);

Of course at that point, you haven’t done much; you just allocated a pointer and then immediately destroyed it.

Note that while this deletes the value of the pointer, the pointer itself is not. You can have null pointers, but you cannot eliminate the pointers themselves. You can only free up the memory associated with it.

Common issues when using pointers

Many more current languages do not use pointers (or, at least, don’t actively require as much manipulation of pointers). Because of this, some programmers can struggle when managing their pointers. 

A few common issues when using pointers include:

C uses pointers for memory management, but pointers can also work for complex, machine-level operations if programmers understand pointer arithmetic and pointer mechanics in-depth. Without pointers in C, programs can develop memory leaks or become unwieldy and difficult to control. 

Importantly, it’s very easy to break code or produce unreadable code when working with pointers. There are very few safeguards when working with pointers, and direct manipulation of memory locations and memory allocations can be dangerous. A thorough understanding is necessary if programmers want to dive deeper.

Page Last Updated: July 2021

Top courses in C (programming language)

C Programming For Beginners - Master the C Language
Tim Buchalka's Learn Programming Academy, Jason Fedin
4.4 (22,430)
Bestseller
Advanced C Programming Course
Tim Buchalka's Learn Programming Academy, Jason Fedin
4.4 (1,828)
Build Undetectable Malware Using C Language: Ethical Hacking
Aleksa Tamburkovski, Joe Parys
4.5 (880)
C Programming Language: The Ultimate Guide for Beginners
Aptuts Online Coaching, Prashant Nayak
4.5 (184)
C Programming from scratch- Master C Programming
Priyanka Yadav Desai
4.6 (55)
Bestseller
Advanced C Programming: Pointers
Huw Collingbourne
4.7 (2,414)
C Programming For Beginners
Huw Collingbourne
4.4 (3,812)
C PROGRAMMING made easy
Meganadha Reddy
4.4 (1,531)
C Programming from Scratch to Master
SACHIN NANDHA SABARISH J
4.7 (33)

More C (programming language) Courses

C (programming language) students also learn

Empower your team. Lead the industry.

Get a subscription to a library of online courses and digital learning tools for your organization with Udemy for Business.

Request a demo