Nearby lessons

88 of 124

C - Pointer Declaration

How to declare a pointer in C — the type *name; syntax, why the asterisk binds to the name and not the type, pointers to every kind of data, and the special case of void *.

The Declaration Syntax

Write the target type, an asterisk, then the name. The three spacings below are identical to the compiler:

Example01
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int x = 42;
6 
7 int *p1 = &x; /* most common style */
8 int * p2 = &x; /* legal */
9 int* p3 = &x; /* legal, but see the warning below */
10 
11 printf("*p1=%d *p2=%d *p3=%d\n", *p1, *p2, *p3);
12 return 0;
13}
Output
*p1=42 *p2=42 *p3=42

The Asterisk Binds to the Name

This trips up almost everyone who writes int* p;. The * belongs to the declarator, not the type. So int* a, b; declares one pointer and one plain int — which is almost never what the author intended. Writing int *a, *b; makes the truth visible.
Example02
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int x = 10, y = 20;
6 
7 int *a = &x, *b = &y; /* BOTH are pointers */
8 int *c = &x, d = 99; /* c is a pointer, d is a plain int */
9 
10 printf("*a=%d *b=%d\n", *a, *b);
11 printf("*c=%d d=%d (d is not a pointer)\n", *c, d);
12 
13 printf("sizeof(c)=%zu sizeof(d)=%zu\n", sizeof(c), sizeof(d));
14 return 0;
15}
Output
*a=10 *b=20
*c=10  d=99  (d is not a pointer)
sizeof(c)=8 sizeof(d)=4

A Pointer for Every Type

Any type can be pointed to, including other pointers:

Example03
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int i = 42;
6 float f = 3.14f;
7 double d = 2.71828;
8 char c = 'A';
9 
10 int *pi = &i;
11 float *pf = &f;
12 double *pd = &d;
13 char *pc = &c;
14 int **ppi = &pi; /* pointer to a pointer */
15 
16 printf("*pi = %d\n", *pi);
17 printf("*pf = %.2f\n", *pf);
18 printf("*pd = %.5f\n", *pd);
19 printf("*pc = %c\n", *pc);
20 printf("**ppi = %d\n", **ppi);
21 return 0;
22}
Output
*pi   = 42
*pf   = 3.14
*pd   = 2.71828
*pc   = A
**ppi = 42

Types Must Match

Assigning the wrong pointer type is a warning at best and a silent misread at worst — the pointer type decides how many bytes are read:

Example04
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int i = 65;
6 char c = 'A';
7 
8 int *pi = &i; /* correct */
9 char *pc = &c; /* correct */
10 
11 /* int *wrong = &c;
12 warning: initialization of 'int *' from 'char *'
13 Dereferencing it would read 4 bytes from a 1-byte variable */
14 
15 printf("*pi = %d\n", *pi);
16 printf("*pc = %c\n", *pc);
17 return 0;
18}
Output
*pi = 65
*pc = A

void * — The Generic Pointer

A void * can hold the address of anything, which is why malloc returns one. It cannot be dereferenced until you cast it to a real type:

Example05
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int i = 42;
6 float f = 3.14f;
7 void *generic;
8 
9 generic = &i; /* holds an int address */
10 printf("As int : %d\n", *(int *) generic);
11 
12 generic = &f; /* now a float address */
13 printf("As float : %.2f\n", *(float *) generic);
14 
15 /* printf("%d", *generic); ERROR: cannot dereference void * */
16 return 0;
17}
Output
As int   : 42
As float : 3.14

Pointers to Structs

Use -> to reach a member through a pointer. It is shorthand for (*p).member:

Example06
CCode Cell
1#include <stdio.h>
2 
3struct Student {
4 char name[30];
5 int marks;
6};
7 
8int main()
9{
10 struct Student s = {"Rahul", 85};
11 struct Student *ps = &s;
12 
13 printf("Dot on the struct : %s, %d\n", s.name, s.marks);
14 printf("Arrow via pointer : %s, %d\n", ps->name, ps->marks);
15 printf("Explicit dereference: %s, %d\n", (*ps).name, (*ps).marks);
16 
17 ps->marks = 92; /* writes through the pointer */
18 printf("After change : %d\n", s.marks);
19 return 0;
20}
Output
Dot on the struct  : Rahul, 85
Arrow via pointer  : Rahul, 85
Explicit dereference: Rahul, 85
After change       : 92

const and Pointers — Read It Backwards

Where you put const decides what is frozen. Read the declaration right to left:

DeclarationReads asCan change *p?Can change p?
int *ppointer to intYesYes
const int *ppointer to const intNoYes
int *const pconst pointer to intYesNo
const int *const pconst pointer to const intNoNo
Example07
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int a = 10, b = 20;
6 
7 const int *p1 = &a; /* cannot change the value */
8 /* *p1 = 99; ERROR */
9 p1 = &b; /* but can repoint */
10 
11 int *const p2 = &a; /* cannot repoint */
12 *p2 = 99; /* but can change the value */
13 /* p2 = &b; ERROR */
14 
15 printf("*p1 = %d (repointed to b)\n", *p1);
16 printf("a = %d (changed via p2)\n", a);
17 return 0;
18}
Output
*p1 = 20 (repointed to b)
a   = 99 (changed via p2)

Function Pointers

A function has an address too. The parentheses around *name are essential — without them you declare a function returning a pointer:

Example08
CCode Cell
1#include <stdio.h>
2 
3int add(int a, int b) { return a + b; }
4int multiply(int a, int b) { return a * b; }
5 
6int main()
7{
8 /* pointer to a function taking (int, int) and returning int */
9 int (*operation)(int, int);
10 
11 operation = add;
12 printf("add : %d\n", operation(5, 3));
13 
14 operation = multiply;
15 printf("multiply : %d\n", operation(5, 3));
16 return 0;
17}
Output
add      : 8
multiply : 15

Always Initialise

An uninitialised pointer holds garbage. Point it at something real, or at NULL so you can test it:

Example09
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int x = 42;
6 
7 /* int *bad; holds garbage - dereferencing crashes */
8 int *good = &x; /* points at something real */
9 int *safe = NULL; /* explicitly points nowhere */
10 
11 printf("*good = %d\n", *good);
12 
13 if (safe != NULL)
14 printf("*safe = %d\n", *safe);
15 else
16 printf("safe is NULL - not dereferenced\n");
17 return 0;
18}
Output
*good = 42
safe is NULL - not dereferenced

Common Mistakes

  • int* a, b; — only a is a pointer. Write int *a, *b;.
  • Declaring without initialisingint *p; then *p = 5; is undefined behaviour.
  • Mismatched typesint *p = &someChar; reads past the variable.
  • Dereferencing a void * — cast it first.
  • Forgetting parentheses in a function pointerint *f(int) is a function, not a pointer.
  • Using . instead of -> on a struct pointer.
Trainer's Note: read declarations from the name outwards. int *(*p)[5] is: p is a pointer, to an array of 5, of pointers to int. Start at the identifier, go right when you can, left when you must.
📝 Key Takeaways
  • Syntax: type *name; — the type is what it points to.
  • In int *a, *b; both are pointers; in int *a, b; b is a plain int.
  • A pointer type must match what it points to.
  • void * can hold any address but cannot be dereferenced directly.
  • const placement decides what is protected — the pointer or the target.

🧠 Test Your Knowledge

4 Questions
Progress: 0 / 4