Hola:
Imagina un puntero como una flecha que apunta a un sitio en la memoria. malloc() o free() no tienen nada que ver con el puntero en sí, sino con la zona de memoria a la que se apunta.
Si tu haces
int x=5;
esto hace que el compilador reserve una zona de memoria de cuatro bytes a la que llama x y mete dentro un 5. Si ahora haces
int p* = &x;
estás haciendo que una flecha p apunte a esa dirección de memoria x. Como tú no has reservado esa zona de memoria (lo ha hecho el compilador al declarar la variable), NO tienes que liberar tú esa memoria (lo hará el compilador cuando esa variable deje de existir).
Cuando haces malloc(tamaño), estás diciendole al sistema operativo que te reserve una zona de memoria de tamaño. Puesto que tu has pedido esa memoria, el sistema operativo no la liberará hasta que tú se lo pidas, con un free().
Como los punteros son flechas y los haces apuntar a un sitio y otro durante la ejecución del programa, es bastante habitual (más de lo que nos gustaría) que alguno de esos puntero quede apuntando a un sitio incorrecto. Por ejemplo, si haces esto
int *p = malloc(tamaño);
...
free(p);
Ahora p está apuntando a una zona de memoria que ya NO está reservada. Si no eres cuidadoso, puedes no darte cuenta y seguir usando p como si apuntara a una zona de memoria válida. En ese caso se dice que p es un "puntero descarriado"
Aquí tienes algunos
motivos por lo que es fácil que un puntero se vaya de madre.
Se bueno.