Address Operator
In C the address operator is an unary operator1that returns the address of a variable.

Pointers
A Pointer is a variable that stores the memory address of another variable. Instead of holding a direct value, it holds the address where the value is stored in memory.
- A pointer is declared by specifying its data type and name, with an asterisk (*) before the name. Syntax: data_type *pointer_name:
- Accessing the pointer directly will just give us the address that is stored in the pointer. To get the value at the address stored in a pointer variable, we use
*operator which is call dereferencing operator in C.
Dereference a pointer
Dereference is, use of a pointer to access the value whose address is being stored. We use * operator to get the value of the variable from its address.
- When we dereference a pointer, we deal with the actual data stored in the memory location it points to
- When we write
*ptr, the compiler looks at the address stored in the pointer, goes to that memory location, and accesses or changes the actual data stored there.
Pointer arithmetic
This means changing the value of a pointer to make it point to a different element in memory.
- You can move a pointer with
++and--(and with+=/-=):
int myNumbers[3] = {10, 20, 30};
int *p = myNumbers; // myNumbers[0]
printf("%d\n", *p); // 10
p++; // move to myNumbers[1]
printf("%d\n", *p); // 20
p--; // back to myNumbers[0]
printf("%d\n", *p); // 10
p += 2; // jump to myNumbers[2]
printf("%d\n", *p); // 30- You can subtract two pointers that point to elements in the same array to find out how many elements are between them:
int myNumbers[5] = {10, 20, 30, 40, 50};
int *start = &myNumbers[1]; // points to 20
int *end = &myNumbers[4]; // points to 50
printf("%ld\n", end - start); // 3 elements apartPointer arithmetic depends on type. No all pointers move the same way because when you add 1 to a pointer, it moves forward by the size of the thing it points to
- An
int*pointer moves by the size of an integer (usually 4 bytes). - A
char*pointer moves by the size of a character (1 byte).
Pointer to the pointer (double pointer)
A normal pointer is like a note with an address on it. A pointer to pointer is like another note telling you where that first note is kept
Pointers and Structs (The -> Operator)
When we work with structures (like nodes in a Linked List or Binary Tree), we use pointers to connect them. To access a property of a struct through a pointer, C gives us a shortcut: the arrow operator ->.
l->valoris exactly the same as(*l).valor.- First, it dereferences the pointer
(*l)to get the actual box in memory, and then it uses the dot.valorto look inside that box.
Class Example (Linked Lists):
#include <stdio.h>
#include <stdlib.h>
typedef struct lligada {
int valor;
struct lligada *prox;
} *LInt;
int main() {
// Criamos um nó dinâmico
LInt meuNodo = malloc(sizeof(struct lligada));
// Usamos a seta -> para aceder aos campos do struct
meuNodo->valor = 42;
meuNodo->prox = NULL;
// Lemos o valor usando a seta ->
int x = meuNodo->valor;
free(meuNodo);
return 0;
}Step-by-Step en Python Tutor:
- Línea 11: En la columna izquierda (Stack), aparece la variable
meuNodo. En la columna derecha (Heap), aparece una caja vacía de dos espacios (valoryprox). La flecha conecta el Stack con el Heap. - Líneas 14-15: Ves cómo el número
42y unaXazul (que representaNULL) llenan los espacios dentro de la caja en el Heap. - Línea 18: En el Stack, se crea la variable
xy copia el valor42leyendo la caja del Heap. - Línea 20: La caja del Heap desaparece (liberación de memoria) y la flecha se rompe.
Passing by Reference (Why do we need pointers in functions?)
In C, when you pass a variable to a function, it normally sends a copy of the value (Pass by Value). If the function changes it, the original variable remains unchanged.
To let a function modify our original variable, we must pass its memory address using the Address Operator &, and the function must receive it using a Pointer.
Class Example (The pop function in a Stack):
#include <stdio.h>
#include <stdlib.h>
typedef struct lligada {
int valor;
struct lligada *prox;
} *Stack;
// Recebe o endereço do topo da pilha (*s) e o endereço de uma variável (*x)
int pop(Stack *s, int *x) {
if (*s == NULL) return 1;
Stack t = *s;
*s = (*s)->prox;
*x = t->valor; // Modificamos a variável original no main!
free(t);
return 0;
}
int main() {
// Simulamos uma pilha com um único elemento (o número 99)
Stack minhaPilha = malloc(sizeof(struct lligada));
minhaPilha->valor = 99;
minhaPilha->prox = NULL;
int valorRetirado = 0; // Variável normal no main
// Passamos o ENDEREÇO da pilha e o ENDEREÇO da variável
pop(&minhaPilha, &valorRetirado);
return 0;
}Step-by-Step en Python Tutor:
- Líneas 23-25: Se crea
minhaPilhaen el Stack apuntando a un nodo con el valor99en el Heap. - Línea 27: Nace
valorRetiradoen el Stack valiendo0. - Línea 30 (Llamada a función): Entramos a
pop. ¡Fíjate en las flechas! El punterosapunta hacia la variableminhaPilha(Stack a Stack). El punteroxapunta haciavalorRetirado. - Línea 15: A través del puntero
x, la función cambia remotamente el0por el99directamente en el marco delmain.
Pointer to the pointer (Double Pointer **)
A pointer to a pointer is needed when you want a function to be able to change where a normal pointer is pointing.
If you pass a normal pointer to a function, the function can change the data inside that address, but it cannot change the address itself. If you need to redirect the original pointer to a completely new memory box, you need a double pointer.
Class Example (Modifying the Head of a List):
Because LInt is defined as a pointer (typedef struct lligada *LInt), writing LInt *l in a function argument is actually a double pointer (struct lligada **l).
#include <stdio.h>
#include <stdlib.h>
typedef struct lligada {
int valor;
struct lligada *prox;
} *LInt;
// Queremos que esta função mude para onde a cabeça da lista aponta
void inserirNoInicio(LInt *l, int x) {
LInt novo = malloc(sizeof(struct lligada));
novo->valor = x;
// novo->prox aponta para onde a lista começava antes
novo->prox = *l;
// Mudamos o ponteiro original no main para apontar para o 'novo'
*l = novo;
}
int main() {
LInt cabeca = NULL; // Lista começa vazia
// Passamos o endereço do ponteiro 'cabeca'
inserirNoInicio(&cabeca, 10);
inserirNoInicio(&cabeca, 20); // O 20 vai empurrar o 10
return 0;
}Step-by-Step en Python Tutor:
- Línea 22:
cabecainicia vacía (NULL/ cruz azul) en el Stack. - Línea 25 (Primera Inserción): Entramos a la función. El puntero doble
lapunta a la variablecabecadelmain. Se crea un nodo en el Heap con el valor10. En la línea 18,cabecaahora apunta mágicamente a este nuevo nodo. - Línea 26 (Segunda Inserción): Volvemos a entrar. Se crea el nodo
20. En la línea 15, el nuevo nodo se engancha al nodo10. En la línea 18,cabecadeja de apuntar al10y ahora apunta al20. ¡La cabeza de la lista ha sido actualizada.
The NULL Pointer (The Dead End)
A NULL pointer is a pointer that points to exactly nowhere (address 0x0). It is fundamentally used as a safe indicator that a data structure has ended.
- In Linked Lists: The
proxpointer of the last node isNULL. - In Binary Trees: If a node has no left child, its
esqpointer isNULL(a leaf has both asNULL). - Danger: Trying to dereference a NULL pointer (e.g., asking for
l->valorwhenlisNULL) will instantly crash the program with a Segmentation Fault. That is why we always writewhile (l != NULL)!
Bibliografía
Para profundizar en la gestión de memoria y la aritmética de punteros, se recomiendan las siguientes fuentes:
- GeeksforGeeks:
• Address Operator in C - Guía detallada sobre el operador&y la referencia de memoria. • C Pointers - Compendio completo sobre declaración, inicialización y uso de punteros.
• Dereference Pointer in C - Explicación sobre el acceso a valores mediante el operador*. - Kernighan, B. W., & Ritchie, D. M.: The C Programming Language. (El libro “K&R”). Es la biblia del lenguaje C y explica de forma inigualable la relación entre punteros y arrays.
- Stanford CS Education Library: Essential C - Un recurso excelente para entender la memoria dinámica y los errores comunes de punteros.
Links Relacionados
Estas notas contienen las implementaciones prácticas donde se aplican todos los conceptos de esta página:
- Estructuras Lineales:
• pi tp 8 - Implementación de filas circulares y gestión de memoria dinámica inicial.
• pi tp 9 - Construcción de Stacks y el uso de punteros simples para Listas Ligadas.
• pi tp 10 - Operaciones avanzadas de Listas: Inversión in-place, conversión a arrays e inserción ordenada (uso de punteros dobles). - Estructuras No Lineales:
• pi tp 11 - Árboles Binarios (ABin): Recursividad bidimensional y búsqueda avanzada mediante punteros. - Ejercicios Prácticos:
• Ficha3 - Ejercicios de aritmética de punteros y arrays.
• Ficha4 - Problemas de manipulación de estructuras dinámicas y punteros dobles.
Footnotes
-
An unary operator in C is an operator that acts on a single operand. Unlike binary operators like
+ina + bunary operators streamline operations using a single variable or constant. ↩