Punteros en C
Cabe destacar que este contenido estaba antes en el desaparecido (desde hace un par de semanas) sentx.net, originalmente yo era el autor único de esto, pero le metieron mano en los acentos y trataron de corregir mi horrenda redacción un par de amigos de el SeNTX; con la esperanza de ahora si terminar esta introducción a punteros re-posteo el contenido en el blog para continuarlo, ¡que les sea de provecho!.
Motivado por una respuesta de uno de los maestros de la FCFM al momento de preguntarle "¿Maestro, cuando vamos a ver punteros?" y recibir su respuesta de "de punteros tú sólo vas a saber el nombre", trato de explicar un concepto tan fundamental en C, y tan poco o nulamente sabido por los estudiantes que dicen saber programar en C por usar arreglos para manejar sus cadenas y los ejemplos de juguete, sin siquiera considerar los manejos de memoria dinámica vía punteros, comencemos.
Nociones básicas
¿Qué es un puntero?
Es la dirección en memoria de un segmento en particular de la misma.
¿Cómo se define un puntero?
TIPO * variable;
Lo que se realiza, en una asignación de un numero entero normalmente expresado en forma hexadecimal, la diferencia de este entero es que ya se tiene noción de ser una dirección en memoria que apunta a un segmento de la misma de un tipo en particular. Entonces al realizar:
int * v;
¿A dónde apunta v?, si ya se dijo que v es entero que apunta a un segmento de memoria, pero ¿cuál segmento?, realmente no se sabe, y tampoco puedes hacer uso de el mismo.
Al definir v como dirección de memoria (puntero), ¿cómo se le asigna un valor a lo que apunta?, para esto se usa *VARIABLE:
*v = 1;
con el * se accesa directamente al segmento de memoria al que se apunta.
¿Qué sucede si se intenta hacer lo siguiente?
int * p1; *p1 = 1; printf("%d", *p1);
Sin duda compila sin ningún problema, ¿entonces está bien?; veamos...
int * p1; // p1 es un puntero a un segmento de memoria que apunta a un entero *p1 = 1; // el segmento al que apunta ahora se le asigna 1 como valor printf("%d", *p1); // imprime el entero al que se apunta.
Pero, ¿cuál memoria se está asignando?, nunca se definió. Entonces, ¿por qué compiló?, ¿cómo se arregla?, compiló por que p1 tiene una dirección de memoria pueden comprobarlo haciendo:
int * p1; printf("%p", p1);
verán su valor en hexadecimal.
El problema es que nuestro programa no tiene permiso a escribir a ese segmento de memoria, entonces al hacer: *p1 = 1, recibimos un error de segmentación "segmentation fault" y nuestro programa termina.
Esto se soluciona reservando la memoria que luego se va a utilizar, para esto se usa malloc, definido en "stdlib.h", de su man:
void *malloc(size_t size); aloja size bytes, en donde size es un entero, y regresa un puntero a la memoria alojada, la memoria no es limpiada. Si size es 0, entonces malloc() regresa NULL o un puntero único que luego puede ser usado en free().
Arreglando:
int * p1 = malloc(sizeof(int)); // p1 es un puntero a un segmento de memoria //que apunta a un entero *p1 = 1; // el segmento al que apunta ahora se le asigna 1 como valor printf("%d", *p1); // imprime el entero al que se apunta
¿Entonces toda variable tiene puntero?
Por su puesto, toda variable tiene una dirección, y entonces, ¿cómo saberla?...
Existe un operador llamado de dirección &, un uso más para el et (&& "and logico", & "bitwise and") se usa:
&variable
es un operador de derecha, por lo tanto nunca verán
&variable = 0x341424;
es así como se puede hacer lo siguiente:
int v = 1000; printf("La dirección de v es %p", &v);
lo que ahora abre la posibilidad a hacer lo siguiente:
int v = 1000; int *x ; x = &v; *x += 1; printf("v = x = %d", *x); printf("%p == %p\n", x, &v); // imprime la dirección usada
Modificar a v sin siquiera usar a v directamente, solamente su dirección, que luego se modifica con un puntero secundario x al que como verán no se inicializo con malloc, ya que no se va a usar la dirección original, si no la de v, donde v es una variable que la inicializó automáticamente al definirla como entero.
Hasta aquí por el momento, aún faltan 4 secciones más por definir:
- Aritmética de punteros
- Cadenas
- Punteros a estructuras
- Punteros a funciones