Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
Declaring Pointer Variables in C
A pointer is a variable that stores an address. Learn the declaration syntax, the base type, the size of a pointer and the one-asterisk trap that catches almost every beginner. Free C tutorial for embedded systems.
Finally, the moment this free C programming course has been building towards: declaring pointer variables in C. You already know that every variable has an address and that the address operator reveals it. A pointer is simply a variable designed to hold such an address.
This lecture of our free C tutorial explains the syntax, the idea of a base type, and the size of a pointer. If you are learning c programming for embedded systems, this is the notation you will read in every driver, HAL and register header you ever open.
Table of Contents
What You Will Learn
By the end of this lecture you can:
- Explain what a pointer variable is in one sentence.
- Write correct declarations for pointers to
int,float,charanddouble. - Define the base type of a pointer and say why it exists.
- Measure the size of a pointer with
sizeofand explain why it does not depend on the base type. - Avoid the declaration trap where only the first name becomes a pointer.
Prerequisites
You need the two previous lectures of this chapter: memory as numbered bytes, and the address operator &. You should also be comfortable declaring ordinary variables of type int, float and char. Nothing else is needed. This free embedded C course introduces every new idea in small steps.
What Is a Pointer Variable
A pointer is a variable whose value is a memory address. Like any variable it has a name, it has a type, it is stored somewhere in memory, and it occupies a certain number of bytes. What makes it special is the meaning of what it holds: an ordinary int holds a number, a pointer holds the location of something else.
The word “points” is a good picture. A pointer stores the address of a target, so we say it points to that target. Follow the address and you arrive at the target.
| Ordinary variable | Pointer variable | |
|---|---|---|
| What it holds | A data value, such as 42 | An address of another object |
| Has its own address? | Yes | Yes |
| Occupies memory? | Yes, size by type | Yes, size of an address |
| Must be declared? | Yes | Yes |
Declaring Pointer Variables in C
The general declaration is:
data_type *pointer_name;
The pointer_name must be a valid C identifier, following the same rules as any variable name. The asterisk * in front of the name tells the compiler that this variable is a pointer. The data_type is the type of the thing the pointer is allowed to point to, which we call the base type (some books say referenced type).
int *ip; /* pointer to int */
float *fp; /* pointer to float */
char *cp; /* pointer to char */
double *dp; /* pointer to double */
Here ip is meant to point to int variables. We say its type is “pointer to int”, written int *, and its base type is int. Likewise fp is a float *, and so on.
Spacing styles
All of these are the same declaration to the compiler: int *p;, int* p; and int * p;. Embedded style guides usually write int *p; with the star next to the name, and the next section explains exactly why that style is safer.
The Base Type of a Pointer
If every pointer just stores an address, why does the declaration need a data type at all? The type is not about the pointer itself. It is about what lives at the other end. When you later follow the pointer to read the target, the compiler must know how many bytes to read and how to interpret them. Reading four bytes as an int gives a very different answer from reading eight bytes as a double, even from the same address.
| Pointer declared as | Bytes read when followed | Interpreted as |
|---|---|---|
char * | 1 | A character or a byte |
short * | 2 | A 16-bit integer |
int * | 4 (typical) | An integer |
float * | 4 | A floating-point number |
double * | 8 | A double-precision number |
The base type also controls pointer arithmetic: adding 1 to an int * moves it forward by the size of an int, not by one byte. We cover that in a later lecture, but it is the second big reason the base type must be declared.
Rule to remember
A pointer remembers where the target is (the address) and what kind of target to expect (the base type). Both pieces of information are needed to use it safely.
How Much Memory Does a Pointer Use
A pointer is a variable, so the compiler reserves bytes for it. Because every pointer stores an address and nothing else, all object pointers on a given platform have the same size, regardless of the base type. The size equals the width of an address on that machine. Older books state that a pointer is 2 bytes, which was right for 16-bit systems. Today it varies:
| Platform | Typical pointer size |
|---|---|
| 8-bit AVR microcontroller | 2 bytes |
| 32-bit ARM Cortex-M microcontroller | 4 bytes |
| 64-bit Linux or Windows PC | 8 bytes |
Let us measure it on your own machine. The program also demonstrates the trap we meet in the next section.
#include <stdio.h>
int main(void)
{
int *ip;
float *fp;
char *cp;
double *dp;
printf("sizeof(int *) = %zu\n", sizeof ip);
printf("sizeof(float *) = %zu\n", sizeof fp);
printf("sizeof(char *) = %zu\n", sizeof cp);
printf("sizeof(double *) = %zu\n", sizeof dp);
int *a, b;
char *s, t, u;
printf("a is %zu bytes, b is %zu bytes\n", sizeof a, sizeof b);
printf("s is %zu bytes, t is %zu byte, u is %zu byte\n", sizeof s, sizeof t, sizeof u);
return 0;
}
Build and run
gcc -Wall -Wextra -o ptr_size ptr_size.c
./ptr_size
Expected output on a 64-bit Linux PC
sizeof(int *) = 8
sizeof(float *) = 8
sizeof(char *) = 8
sizeof(double *) = 8
a is 8 bytes, b is 4 bytes
s is 8 bytes, t is 1 byte, u is 1 byte
All four pointer types are the same size, 8 bytes here. Cross-compile the same file for a Cortex-M and you would see 4. The last two lines reveal something important, so let us look at them closely.
The One-Asterisk Trap
You can declare ordinary variables and pointers in one statement, but you need to know how the asterisk binds. Look at the line int *a, b;. Many beginners read it as “two pointers to int”. The output proves otherwise: a is 8 bytes (a pointer) and b is 4 bytes (a plain int).
The reason is that the asterisk belongs to the name that follows it, not to the type. The declaration really says “*a is an int, and b is an int”. Each name needs its own star if you want it to be a pointer.
| Declaration | First name | Second name | Third name |
|---|---|---|---|
int *a, b; | a: pointer to int | b: plain int | – |
int *a, *b; | a: pointer to int | b: pointer to int | – |
char *s, t, u; | s: pointer to char | t: plain char | u: plain char |
int* a, b; is especially misleading because the spacing suggests that both are pointers when only a is.
You can still mix pointers and ordinary variables in one declaration when you really want to, for example char *cursor, first, last;. Just be sure you meant it.
Pointer Declarations in Embedded C Programming
The declarations in this lecture are the same ones you will see in real embedded C programming. A few patterns to recognise:
uint8_t *rx_buf;a pointer to a receive buffer of raw bytes.const char *msg;a pointer to read-only text, commonly stored in flash.volatile uint32_t *reg;a pointer to a hardware register. Thevolatilekeyword tells the compiler the value can change on its own, so every access must really happen.struct uart_cfg *cfg;a pointer to a configuration structure passed to a driver function.
Why 32-bit matters on a microcontroller
On a 32-bit Cortex-M each pointer costs 4 bytes of RAM. A table of 100 pointers uses 400 bytes, which is a significant slice of a chip with 8 KB of RAM. Knowing that a pointer is just a stored address helps you judge such costs before they bite.
const and volatile in pointer declarations
Two keywords often appear next to the star. We study them in detail in later chapters, but you should be able to read them today:
| Declaration | Meaning |
|---|---|
const int *p; | p can point elsewhere, but you may not modify the target through p |
int *const p; | p always points to the same place, but the target can be modified |
volatile uint32_t *p; | the target may change outside program control, as with hardware registers |
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
Writing int *a, b; and expecting two pointers | b is just an int | Write int *a, *b; or one declaration per line |
| Assuming a pointer is 2 bytes | Wrong size calculations | Use sizeof |
Thinking char * and int * differ in size | Confused about memory use | They are the same size, only the base type differs |
| Declaring a pointer and using it immediately | It holds a garbage address | Always give it a valid address first (next lecture) |
| Forgetting that a pointer needs its own storage | Memory budget surprises | Count the bytes of every pointer |
Best Practices
- Declare one pointer per line, with the star next to the name.
- Choose names that tell the reader what is pointed to, such as
rx_buforcfg, rather thanp1. - Use
constwhenever a function only reads through a pointer. - Use
volatilefor pointers to hardware registers. - Print
sizeofof a pointer on your target when memory is tight.
Performance and security note
A pointer that has been declared but not yet set is called an uninitialised or wild pointer. It holds whatever leftover bits were in that memory, so following it can read secrets or corrupt data. Many security vulnerabilities start this way. The next lecture shows how to initialise pointers safely.
Key Takeaways
- A pointer variable stores a memory address.
- Declare it with
data_type *name;. The data type is the base type. - The base type tells the compiler how to read the target and how far to step during arithmetic.
- All pointers on a platform have the same size, set by the address width.
- In
int *a, b;onlyais a pointer.
Conclusion
You now know how to create a pointer variable and what its type means. At this point the pointers declared above contain nothing useful: they have names and sizes but no targets. In the next lecture of this free C programming course we give pointers real addresses, copy them between variables, and meet the special NULL value. As practice, compile the size program for another target if you have a cross-compiler, and write down how each pointer size compares with the 64-bit result.
Frequently Asked Questions
What is a pointer in C?
A pointer is a variable that stores the memory address of another object. It points to that object by holding its location.
How do you declare a pointer in C?
Write the base type, then an asterisk, then the name: int *ptr; declares a pointer to int.
Why does a pointer need a data type?
The type tells the compiler how many bytes to read at the target and how to interpret them, and it sets the step size of pointer arithmetic.
Is a char pointer smaller than an int pointer?
No. Every object pointer on a platform has the same size, because each one only stores an address. Only the base type differs.
What is the size of a pointer in C?
It equals the address width of the platform: about 2 bytes on 8-bit AVR, 4 on 32-bit ARM Cortex-M, and 8 on a 64-bit PC. Check with sizeof.
In int *a, b; is b a pointer?
No. Only a is a pointer. The star applies to the name next to it, so b is a plain int.
Do I need pointers for embedded C programming?
Yes. Hardware registers, buffers and driver interfaces all use pointers, so they are essential in embedded C programming.
Continue the Free Embedded C Course
Next: assign real addresses to your pointers, copy one pointer into another, and learn why NULL matters.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment