Otro dia mas de
"No tengo idea de C pero estoy haciendo un post" pero que espero que les sirva a todos. El capitulo de hoy se llama:
Como leer punteros en C.. Esos malditos asteriscos,parentesis,etc que hacen que uno pierda la cabeza y no quiera programar mas. Asi que vamos a enseñar a leerlos y de paso aprenderan como crearlos bien y no poniendo parentesis y esperando que funcione

como seguro la mayoria hizo en un comienzo.
Como los ejemplos es lo mas comodo para aprender y ver espero que este lleno de los mismos y sirva para todos.
Para seguir este mini-tutorial comenzamos por los ejemplos mas basicos. Mas adelante vamos a necesitar de esta tabla ( si se cae el link busca imagenes en google de "operator precedence C":

El cual nos va a servir para entender el por que de la misma. Esta tabla habla de la precedencia y la asociacion de cada operando.. Mientras mas alta la precedencia significa que va primero, y si hay 2 o mas con la misma precedencia entonces vamos a usar la asociacion para determinar cual va primero, algunos van de izquierda a derecha y otros de derecha a izquierda.
Comenzemos..
Pero eso no es un pointer

, pero es bueno para comenzar, eso deberia leerse como "declarar
n como
int". Veamos el primer pointer:
Esto se leeria "
p es un
pointer que apunta a un
int", ¿Y como llegamos a esto?, tomemos como comienzo el nombre del pointer o su identificador ( p ), por la precedencia primero esta el simbolo de puntero *, y luego finalmente el tipo afuera. Que si observan como se "leeria" tiene una correspondencia con el orden.
Veamos un ejemplo mas complejo
int Array[30][4];
int (*p)[4]=Array;
int *q[5];
Lo primero es un array 30 x 4, simple como todos. Pero como leo el segundo ? Para esto vamos a desmenuzarlo poco a poco.
Primero observamos donde esta el nombre ( p ), y tenemos algo asi ( x )[ x ], los corchetes y los parentesis tienen el mismo orden de precedencia y se evaluan de izquierda a derecha, es decir que lo primero es lo de dentro del parentesis. Observamos dentro y vemos que "
p es un
puntero", terminamos de analizar ese parentesis y nos queda evaluar el corchete y finalmente el tipo, quedando de esta forma:
"
p es un
puntero a un
array de 4 elementos de tipo
int"
Y uno pensaria que el 3ro es igual pero como se puede observar esta ves NO hay parentesis. y los corchetes [] tiene mayor orden de precedencia que el indicador de puntero * ( no confundir con multilpicacion ). Esto cambia TODO. ya que ahora va el corchete primero quedando en este orden: Nombre, Corchetes, puntero, tipo.
"
q es un
array de 5
punteros a
ints"
Por una parte teniamos un puntero a un array, y ahora tenemos un array de punteros. Veamos un par mas:
int **p1; // p1 es un puntero a un puntero a un int
int *&p2; // p2 es la direccion a un puntero a un int
int &*p3; // ERROR: Un puntero a una referencia es ilegal, no tiene sentido
int &&p4; // ERROR: Una referencia a una referencia de un int tampoco tiene sentido y es ilegal
Si se preguntan el por que es ilegal esos ultimos 2 piensen lo siguiente:
Un puntero ocupa un espacio en memoria, es decir es una espacio de memoria que tiene una direccion. Si intentamos hacer &*p3, lo que se intenta hacer es un puntero la direccion de un int.. Pero esa direccion del int no ocupa espacio en memoria, es decir no hay una posicion de RAM que tenga ese valor de la direccion, sino que directamente se obtiene del int. Entonces uno no puede apuntar a algo que no existe. Lo mismo ocurre con p4, Es como pedir la direccion donde esta almacenada la direccion del int. Es absurdo ya que como dijimos la direccion no ocupa espacio fisico.
Const / volatile.const int n = 3;
int const m = 5;
A pesar que cambien de posicion ambos tiene el mismo significado tanto n como m, son de tipo int y constantes. Lo mismo ocurre con esto:
const int *p;
int const *q;
Ambos son exactamente lo mismo, es decir
p o q, son
punteros a un tipo
int constante. Observer que eso no hace constante al puntero si quisieramos que el puntero sea constante seria asi:
Y si quisieramos completarla, es decir un puntero constante a un int constante
const int * const p = &q;
Como siemrpe hay mas cosas dejo una tabla de nuevo con distintos casos y que deberia dejar sin dudas respecto cuando es uno u otro, si quieren leerlo comienzen desde el nombre hacia la izquierda ya que no poseen mas operandos
char ** p1; // puntero a puntero a char
const char **p2; // puntero a puntero a const char
char * const * p3; // puntero a const puntero a char
const char * const * p4; // puntero a const puntero a const char
char ** const p5; // const puntero a puntero a char
const char ** const p6; // const puntero a puntero a const char
char * const * const p7; // const puntero a const puntero a char
const char * const * const p8; // const puntero a const puntero a const char
Punteros a funcionesLos punteros a funciones son algo simples de ver y casi siempre tienen esta forma:
tipo (x)(argumentos)
Obviamente tal ves no tan simple asi, pero ya vamos a ver unos ejemplos. El ejemplo mas simple seria:
Nuevamente observamos donde esta el nombre, en nuestro caso es p. Y que esta entre parentesis, en esos 2 parentesis al tener la misma precedencia nos fijamos en la asociacion y vemos que es de izquierda a derecha. Entonces primero tomamos el parentesis de la izquierda, luego el de la derecha y finalmente el int ( recordando que las funciones devuelven valores ). Y vamos creando nuestra definicion:
"
p es un
puntero a una funcion que toma un argumento
char y que devuelve un
int"
Otro ejemplo
char ** (*p)(float,float);
Ocurre lo mismo que el caso anterior, primer parentesis, luego el segundo, luego los punteros y finalmente el char. Y esto se leeria:
"
p es un
puntero a una funcion que toma como argumento 2
float y que devuelve un
puntero a un
puntero a un
char"
Siempre hay alguien complicado y podemos seguir agregando, este es el ultimo ejemplo:
void * (*a[5])(char * const, char * const);
Este es mas complejo.. pero empezemos a separarlo.. tenemos nuevamente nuestros 2 parentesis ( misma precedencia y que por su asociacion es de izquierda a derecha) Asi que lo primero es (*a[5]), como los corchetes tiene mas precedencia que el simbolo de puntero quedaria:
"
a es un
array de 5
punteros"
Luego observamos el segundo parentesis y vemos que "char * const" son 2
punteros constantes a un
char. Uniendo y usando luego lo de afuera:
"
a es un
array de 5
punteros a funciones que tienen como argumento 2
punteros constantes a
char y que devuelven un
puntero void"
-------------------------
2 ejemplos mas el cual van a separarse para que la gente lo vea un poco mas claro:
int * (* (*fp1) (int) ) [10];
(*fp1) // fp1 es un puntero
(int) // a una funcion que toma como argumento un int
(* ) // y que devuelve un puntero
[10] // a un array de 10
int * // punteros a int
int *( *( *arr[5])())();
arr[5] // arr es un array de 5
* // punteros
() // a funciones
*( ) // que devuelven punteros
() // a funciones
int * // y que devuelven punteros a ints
EDIT:Otro ejemplo que es muy comun de ver es;
Con experiencia uno sabe que primero se accede al pointer se realiza lo que tiene que hacerse y luego se
incrementa el pointer y no el contenido del mismo.
Ahora si uno se fija en la tabla, observara que el post incremento tiene la mayor precedencia y luego esta el puntero. Lo cual es equivalente a hacer:
Y es el por que se incrementa el puntero y no el contenido del puntero