Introduccion a punteros en C (empezando desde cero)
December 13th, 2009
ahora hablare sobre punteros en c, cualquiera que quiera programar en c debe de manejar los punteros como si fueran las tablas de multiplicar, personalmente he visto muchos casos de personas que inclusive estando a nivel universitario les cuesta trabajo entender como trabajan los punteros, es por eso que escribo esto y vamos a empezar desde cero, simpre es bueno tener unas buenas bases de punteros. En cierto sentido manejar punteros es facil, si tenemos algunos conocimientos en la forma en que se almacena la informacion en una computadora mmmm…ya empezamos mal (pensaras), pero no te preocupes no introducire demasiada jerga computacional, solo hablaremos de los conceptos en su forma mas general y digerible para cualquiera que alguna vez haya programado solo un poco.
Material Previo
Dentro de la computadora existe algo llamado memoria ram, es un espacio donde se almacenan todos los datos que utiliza la computadora solo cuando esta encendida, eso incluye los programas en C que escribes y ejecutas.
La memoria esta formada por registros cada registro tiene una direccion y el tamaño de estos registros depende de la arquitectura de la computadora, por lo general las computadoras tienen una arquitectura de 32, pero computadoras mas recientes tienen arquitecturas de 64 bits. Esto quiere decir que el tamaño de los registros de la memoria son de 32 bits para arquitecturas de 32 bits y asi para las demas arquitecturas.
Imaginemos a la memoria RAM como una tira enorme finita, dividida en tantos segmentos (registros) como el tamaño de la ram lo permita, cada uno de esos segmentos o registros es de un tamaño de 32 bits o n bits dependiendo de la arquitectura, es decir cada uno de los registros tiene 32 espacios (bits) y un bit es un espacio donde se puede colocar un 1 o un 0.
Un grupo de 8 bits contiguos o si lo prefieres un grupo de 8 espacios contiguos en un registro se llama BYTE, por consecuencia un registro de la memoria contiene 4 bytes.
analizemos la siguiente imagen.

Como habiamos dicho la memoria la dividimos en muchos registros, cada registro almacena 32 bits o 8 bytes y cada bit solo puede contener 0 o 1, cada uno de los registros tienen asociada una direccion, esto es para los programas que trabajan en la computadora puedan localizar los datos que almacenan en ella, una direccion de memoria es un numero asociado con un registro de la memoria que generalmente se maneja en sistema hexadecimal.
Todo lo anterior es importante porque los punteros trabajan sobre direcciones de memoria. Tres cosas importantes sobre una variable son:
- Una variable tiene una direccion de memoria
- Una variable tiene un tipo de dato (int,char,long..etc)
- Una variable almacena un valor
Las variables que ponemos en nuestros programas en C se guardan en la memoria ram, el tamaño del espacio que ocupa una variable depende del tipo de la variable, veamos la siguiente lista:
- Variables tipo Char ocupan 1 byte
- Variables tipo int ocupan 2 bytes
- Variables tipo long ocupan 4 bytes
- Variables tipo double ocupan 8 bytes
Los anteriores son solo algunos de los tipos que maneja C, vemos que por ejemplo un char ocupa solo 8 bits o un byte, esto implica que no llena todo un registro de la memoria pues los registros son de 32 bits o 4 bytes, mientras que el tipo long ocupa hasta 2 registros completos.
Bueno ahora que sabemos como es que se almacenan las variables en la computadora, pasemos a los punteros. Hay que aclarar que para tener un buen manejo de punteros hay que tener en cuenta el “material previo” de manera muy general y conceptual pues es la base de lo que sigue.
¿Que es un puntero?
veamos la siguiente definicion:
Un puntero es una variable que almacena la direccion de memoria de otra variable
Eso es, simplemente un puntero es una variable que se usa para guardar la direccion de memoria donde se encuentra otra variable.
Todos sabemos que para usar una variable en lenguaje C primero hay que declararla y las variables puntero no son la exepcion, veamos como se declara una variable puntero.
1 2 3 4 | //un puntero que apunta hacia una variable tipo int int *puntero_entero; //un puntero que apunta hacia una variable tipo char char *puntero_char; |
veras que la declaracion de un puntero es parecida a la declaracion de una variable con la exepcion de que hay que anteponer este operador indireccion * antes del nombre de la variable. Este operador se utiliza para obtener el contenido de la variable a la que apunta un puntero.
Hay otro operador importante que es el operador unario & y se utiliza para obtener la direccion de memoria donde se encuentra una variable.
Ojo hasta este punto hay que reconocer dos cosas importantes la direccion de una variable y el valor que almacena una variable son cosas distintas. Como dijimos un puntero almacena una direccion de memoria asociada a una variable veamos:
1 2 3 4 5 6 7 8 | //declaracion y uso de punteros int *puntero; int var = 5; puntero = &var; printf("valor de var:%d, direccion de var:%x",*puntero,&var); |
Al ejecutar las lineas anteriores observaremos lo que veiamos en el material previo, declaramos un variable puntero que apunta hacia una variable de tipo entero, luego declaramos una variable llamada var y le asignamos un 5, despues a puntero le asignamos la direccion de memoria de var, finalmente desplegamos el valor de var usando el puntero pues sabemos que *puntero me devuelve el contenido de la variable a la que apunta puntero y por ultimo &var me devuelve la direccion de var. La salida de las lineas anteriores debe ser.
valor de var:5, direccion de var:bf0452af
podemos modificar el valor de var utilizando el puntero, veamos un ejemplo;
1 2 | *puntero += 5; printf("nuevo valor de var %d", var); |
La salida deberia ser
nuevo valor de var 10
Todo lo anterior es solo la base de los punteros y esta dedicado para aquellos que no saben nada de punteros, asi que si ya eres experto, puedes criticar este articulo en la seccion de comentarios.
Aritmetica de punteros
Cuando yo escribo esto:
1 2 | int var = 1; var++; |
Esperaria que el valor de la variable var se incrementara en una unidad. Lo anterior NO sucede asi con los punteros, veamos
1 2 3 | int var = 1; int *puntero = &var; puntero++; |
La ultima instruccion no incrementa una unidad el contenido de puntero, lo que realmente hace es avanzar el puntero una cantidad especifica de bytes dada por el tipo de dato al que apunta el puntero,
es decir puntero++; hara que el puntero apunte a los siguientes 2 bytes de la memoria esto es porque el tipo de dato al que apunta es un entero y los enteros ocupan 2 bytes, en el caso de los enteros imagina a la memoria como una tira larga dividida en segmentos de 2 bytes, el puntero es como un flecha que apunta hacia cierto segmento de la tira, al escribir puntero++; la flecha apuntara siguiente segmento de la tira.
Un ejemplo final
En este ejemplo desplegaremos un arreglo usando un puntero
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> int main(){ int arreglo[4]; int *puntero; arreglo[0] = 50; arreglo[1] = 60; arreglo[2] = 70; arreglo[3] = 80; puntero = &arreglo[0]; for ( int i=0;i<4;i++ ){ printf("elemento: %d valor; %d \n", i+1, *puntero); puntero++; } } |
La forma en que se almacena un los elementos de un array en memoria es de manera contigua es decir se elige algun segmento de la memoria para almacenar el primer elemento del arreglo, el elemento 0 digamos, el elemento 1 se almacena inmediatamente despues del elemento 0, el elemento 2 se almacena despues del elemento 1, y asi hasta agotar los elementos del array, esto quieres decir que tendremos una sucesion de segmentos de 2 bytes uno tras otro y cada uno representa en elemento de un array de enteros.
Analizemos el codigo es programa simple, lineas 2 y 3 declaro un array de 4 elementos y un puntero ambos de tipo int. Lineas 8 a 11 lleno el array con algunos numeros. Linea 13 al puntero le asigno la direccion del primer elemento del array.
Finalmente en el ciclo for despliego el contenido al que apunta el puntero que es el valor del primer elemento de array, luego avanzo el puntero a los siguientes 2 bytes, es decir al siguiente elemento del arreglo y se repite el ciclo. la salida del programa anterior debe ser
elemento: 1 valor: 50
elemento: 2 valor: 60
elemento: 3 valor: 70
elemento: 4 valor: 80
Leave a Reply