De los punteros en el lenguaje C
24 de noviembre de 2018 • fjbalon
Los punteros son uno de esos elementos más confusos y complejos cuando nos iniciamos en el lenguaje C, sea que vengamos de aprender directamente a programar, o sea que vengamos de otros lenguajes de más alto nivel, como C# o Python; los punteros suelen ser una característica que conlleva sufrimiento y sacrificio. Esto son parte de mis apuntes, para dar algo de claridad a este tema, espero que el lector pueda darle utilidad.
Cada posición de memoria tiene asociada una dirección que la identifica de forma única, algo así como un identificador. Dentro de ellas se almacenan instrucciones y datos. Para que al programador le resulte más sencillo referirse a una dirección de memoria se utilizan las variables, que no son más que nombres asociados a direcciones de memoria concretas.
Las variables de tipo puntero se utilizan para almacenar en ellas la dirección de memoria de otra variable. El puntero, como variable que es, tiene asociado su dirección de memoria; y su contenido será la dirección de memoria de otra variable. Entiendo que esto es muy confuso, quizá con la siguiente imagen esté algo más claro:
Los punteros pueden apuntar a variables previamente declaradas o a variables dinámicas. La potencia de los punteros reside en poder crear (reservar memoria) y destruir (liberar memoria) una variable (dinámica) en tiempo de ejecución. Un puntero ocupa normalmente 4 bytes y se declara siempre de acuerdo con el tipo de dato que apunta. Tenga en cuenta que la declaración de punteros en C se realiza anteponiendo
El lenguaje C dispone de los operadores de dirección (
int i, j, *p;
p = &i;
*p = 10;
p = &j;
*p = -2;
p = NULL;
Voy a tratar de explicar el código anterior:
Podemos trabajar con punteros de tipo indefinido con
int x = 1;
float r = 1.0;
void* ptr = &x;
*(int *)ptr = 2;
ptr = &r;
*(float *) ptr = 1.1;
Las operaciones aritméticas entre punteros en el lenguaje C se limitan a suma, resta, comparación y asignación. Estas operaciones a un tipo de dato x tienen automáticamente en cuenta el tamaño que ocupa ese tipo de dato en memoria. Es decir, el número de bytes necesario para almacenar un objetivo de ese tipo de dato: la suma incrementa la dirección a la que apunta el puntero teniendo en cuenta el tipo de dato al que apunte el puntero. O sea, si un puntero apunta a un tipo de dato entero y le suma 1, se desplaza el tamaño de un entero. En el caso de la resta, se decrementa la dirección.
Estas dos operaciones son útiles cuando trabajamos con vectores o matrices (lineal) y la suma o resta permitirían moverse entre los elementos de éste.
int a, b, c;
int *p1, *p2;
void *p;
p1 = &a;
*p1 = 1;
p2 = &b;
*p2 = 2;
p1 = p2;
*p1 = 0;
p2 = &c;
*p2 = 3;
p = &p1;
*p = p2;
*p1 = 1;