Diario
de un programador. Día 185
Punteros
en C
En
esta oportunidad vamos a ver una de las características que hace que el
lenguaje C sea tan poderoso y el porqué es elegido por sobre otros para
desarrollar sistemas operativos, me refiero a los punteros, también conocidos como apuntadores. Los punteros
tienen la capacidad de trabajar directamente en la memoria como veremos a
continuación. Entender los punteros es algo complicado así que voy a ir lo más
pausado posible y dar muchos ejemplos para que quede claro, es indispensable
entenderlos bien ya que los capítulos siguientes incluirán el uso de punteros.
Ventajas del uso de
punteros
- En el caso de paso de variables a una función por
dirección o referencia, es necesario emplear parámetros formales como punteros
- Permiten realizar operaciones de asignación dinámica de
memoria
- Permiten efectuar operaciones con estructuras de datos
dinámicas
Todo lo mencionado anteriormente lo iremos viendo a medida
que vamos avanzando.
Una primera aproximación
Hasta
ahora sabemos que las variables contienen algún valor en específico, por
ejemplo la siguiente variable llamada num1 contiene el valor de 7:
int num1 = 7;
Hasta
aquí, todo bien. En cambio, un puntero lo que contiene es la dirección de
memoria de alguna variable.
Entonces,
podemos decir que un puntero es una variable que en vez de contener un valor,
contiene una dirección de memoria. En
este sentido, se entiende que una variable se refiere directamente a un
valor y un apuntador se refiere indirectamente a un valor
Entonces,
¿si un puntero es una variable, como se declara?. Esto lo veremos a
continuación.
¿Cómo se declara un
puntero?
Para
que el compilador entienda que lo que queremos declarar es un puntero y no una
variable, hay que agregar el signo * antes del nombre que escogeremos para
nuestro puntero. El signo * se conoce como operador de indirección.
Por
ejemplo:
int *ptrNum1;
Por
cierto, leí que los apuntadores deben ser inicializados cuando son declarados,
para evitar algún resultado inesperado. Un apuntador se puede inicializar con
valor 0, NULL o una dirección.
Estas
inicializaciones quedarían de la siguiente manera:
int *prtNum1 = 0;
int *prtNum1 = NULL;
int
*prtNum1 = &variable;
Las
tres maneras de inicializar un puntero son válidas. Destaqué la tercera opción
ya que de esa manera se indica una dirección. Al agregar el signo &
indicamos que lo que se debe asignar es una dirección de memoria. ¿Recuerdas
que la función scanf usa el operador & para asignar la variable a la
memoria? El operador & se le conoce como el operador de dirección.
Recordar
y aprender entonces que:
-
El operador de indirección es el *
-
El operador de dirección es el &
Ya
que sabemos esto, veamos un pequeño ejemplo en código a continuación.
Implementando un puntero
El
siguiente código muestra cómo asignar a un puntero la dirección de memoria de
otra variable.
#include<stdio.h>
int
main(void){
int num1 = 7;
int *ptrNum1 = &num1;
printf("Valor de
num1: %d\n", num1);
printf("Valor del puntero prtNum1: %p\n", ptrNum1);
return 0;
}
En la línea 4 se crea una variable de
tipo entero llamada num1, la cual tiene asignada el valor 7. En la línea 5 se
crea una variable de tipo puntero llamada ptrNum1, la cual tiene asignada la
dirección de la variable num1. Como se dijo antes, para asignar una dirección
de memoria se utiliza el operador de dirección &.
La línea 7 imprime el valor de la
variable num1, esta variable como ya dijimos contiene el valor 7. La línea 8
imprime el valor del puntero ptrNum1, como dijimos antes este puntero tiene
asignado la dirección de memoria de la variable num1. Destaqué el modificador
de formato que utiliza el puntero, el modificador %p se
utiliza para mostrar el valor de los punteros, los cuales se muestran en
formato hexadecimal. Si hubiese puesto un %d, mostraría su valor en formato
decimal, pero por tratarse de direcciones de memoria, lo normal es mostrarlos
en hexadecimal.
Al ejecutar este programa, me muestra
el siguiente resultado
Muy bien, ahí tenemos una dirección de
memoria, ¿pero cómo sabemos que esa dirección de memoria contiene el valor de
la variable num1? Para este caso, utilizamos el operador de indirección *
Voy a agregar una línea más al programa
anterior para averiguar el contenido de esa dirección de memoria.
#include<stdio.h>
int
main(void){
int num1 = 7;
int *ptrNum1 = &num1;
printf("Valor de
num1: %d\n", num1);
printf("Valor del puntero prtNum1: %p\n", ptrNum1);
printf("Contenido del
puntero prtNum1: %d\n", *ptrNum1);
return 0;
}
Vamos a ver otro ejemplo.
Vamos a crear un programa en el cual se
tendrán dos variables. La primera variable tendrá un valor y la segunda
variable será igualada a la primera. De esta forma:
int a = 7;
int b = a;
Lo siguiente que hará el programa será
mostrar en pantalla el valor de ambas variables. Luego, se modificará el valor
de la primera variable y se volverá a mostrar en pantalla el resultado. Como la
segunda variable está igualada a la primera, lo normal sería pensar que su
valor también será modificado. Veamos que sucede.
#include<stdio.h>
int
main(void){
int a = 7;
int b = a;
printf("valor de a:
%d\n", a);
printf("valor de b: %d\n\n", b);
a = 10;
printf("Nuevo valor de a: %d\n", a);
printf("Nuevo valor de b: %d\n", b);
return 0;
}
Al ejecutar el programa, esto es lo
que muestra:
El valor de la variable b no se modificó. Esto es porque la variable b, no es realmente una copia de la variable a. La variable b es una variable
totalmente nueva que tiene su propia dirección de memoria y que se inició en un
principio con el valor de la variable a. Es por esto que al
modificar el valor de la variable a, la variable b se mantuvo sin cambios. Entonces ¿cómo hacemos para que la
variable b actualice su valor a medida que la variable a se va modificando a lo largo del programa?, la respuesta
es con el uso de punteros.
Vamos a modificar la variable b y la transformaremos en un puntero.
int
main(void){
int a = 7;
int *b = &a;
printf("valor de a: %d\n", a);
printf("valor de b: %d\n\n", *b);
a = 10;
printf("Nuevo valor de a: %d\n", a);
printf("Nuevo valor de b: %d\n", *b);
return 0;
}
Al ejecutar el programa con la
modificación hecha, esto es lo que obtenemos.
Dejaremos este tutorial hasta aquí el
día de hoy para que repasen bien este asunto antes de seguir ya que hay mucho
más que aprender.
Buena suerte y saludos.
Gustavo J. Cerda Nilo
Septiembre 2016, Noviembre 2016
No hay comentarios:
Publicar un comentario