Versión 3.0

800x600 mínimo
En esta lección:

Definición
-----
Punteros y arrays
-----
Punteros y funciones
-----
Puntero a una función
-----


Otras secciones:

Conceptos básicos
-----
Programando en C
-----
Programando en C++
-----
Programando Windows 9x.
-----
Teoría electrónica
-----
Circuitos electrónicos
-----
Actividades adicionales
-----
Hipervínculos
-----


Contácteme:

Dudas y comentarios
-----


Hola mundo

Definición

Dicho simplemente, un puntero es una dirección en memoria. Como es costumbre en este tutorial, los conceptos se explican mejor por sí mismos, el código es el siguiente:


#include <stdio.h>

main ()
{
    /* Una variable normal y un puntero de tipo int */
    int almacen, *puntero;

    almacen=45;             /* Se asigna un valor cualquiera a variable */
    puntero=&almacen;       /* La direccion de almacen */
    printf("El contenido de la variable llamada almacen\n"
    "y que esta ubicada en %xh es de %d\n", puntero, *puntero);
    return 0;
}

/*  Resultado de la ejecución del programa:

El contenido de la variable llamada almacen
y que esta ubicada en 2796h es de 45

*/

En primer lugar podemos ver una variable estática llamada almacen y una más que lleva un asterisco al principio llamada *puntero, por el momento no se fije en este detalle, lo explicaremos mas adelante. En la línea 7 se le asigna a la variable almacen el valor de 45 tal y como lo hemos hecho en los programas vistos hasta ahora. En la línea 8 se aprecia una forma de asignar un valor extraño a la variable llamada puntero, se trata del operador de dirección ampersand &, que se utiliza en C para acceder a la direccion en memoria de una variable, de aquí salen dos puntos muy importantes:

  1. Cuando al nombre de una variable le precede el operador ampersand, éste define la dirección de la variable y por lo tanto se dice que apunta hacia la variable. En el código de arriba se asigna a la variable llamada puntero la dirección de la variable llamada almacen. En efecto, la variable puntero es un puntero propiamente dicho.

  2. Para saber el contenido de una variable señalada por un puntero utilizamos el asterisco antes del nombre de la variable puntero, en el código de ejemplo observe en la instrucción printf( ) la manera en que se despliegan la dirección y el contenido de la variable llamada puntero.

Como su nombre lo indica, decimos, refiriendonos a la línea 4 del código de ejemplo, que la dirección de memoria señalada por *puntero corresponde a una variable de tipo int, por lo tanto *puntero es un puntero a una variable de tipo int. Un puntero debe definirse para señalar a un tipo específico de variable y por lo tanto no deberá utilizarse en el mismo programa para señalar a una variable diferente pues esto produce errores de incompatibilidad de código. Como se puede ver en el código de arriba, es posible conocer el contenido de la variable almacen de dos formas diferentes, utilizando el nombre de la variable directamente, o bien con un puntero que señale a la dirección de almacen.

puntero y variable no asignados

Es común en el estudio de punteros utilizar algunos gráficos para comprender este importante tema de la programación en C. Un rectángulo representa a la variable estática almacen, en tanto que un rectángulo con un punto en su interior representa a un puntero, en nuestro ejemplo, el llamado a su vez puntero. Este diagrama representa el programa en el punto correspondiente a la línea 5 en donde aún no se le ha asignado valor alguno a las variables, observe que el puntero en este momento no apunta a ningún lado y la variable almacen no tiene asignado aún un valor determinado.

puntero señala a almacen

Siguiendo la ejecución del programa, en la línea 7 asignamos a almacen el valor de 45, en tanto que en la línea 8 indicamos que puntero guarde la dirección de almacen. En la línea 9 utilizamos la instrucción printf( ) para demostrar los dos importantes conceptos estudiados hasta ahora, en primer lugar indicamos desplegar el valor hexadecimal correspondiente a la dirección en memoria ocupada por la variable almacen y posteriormente desplegamos el valor almacenado en la variable en sí, que en este caso es 45. Aunque en apariencia tenemos dos variables, en realidad se trata de una sola, en este caso almacen, solo que se hace uso del puntero para desplegar los valores mencionados. Es importante que observe detenidamente la instrucción printf( ) y que además compile y ejecute el código de ejemplo para un mejor entendimiento de estos conceptos.

Volver al principio

Hola mundo

Punteros y arrays

En el siguiente código de ejemplo se han definido algunas variables y dos punteros. El primer puntero llamado alla es un puntero a una variable de tipo char y el segundo llamado pt apunta a una variable de tipo int. También se han definido dos arrays llamados cadena y lista, los utilizaremos para demostrar la correspondencia entre punteros y los nombres de los arrays. El nuevo código es el siguiente:


#include <stdio.h>
#include <string.h>
int main()
{
    char cadena[30], *alla, primera, segunda;
    int  *pt, lista[100], indice;

    strcpy(cadena, "Esta es una cadena de texto.");

    primera = cadena[0];
    segunda = *cadena;      /* primera y segunda son iguales */
    printf("La primera salida es %c %c\n", primera, segunda);

    primera = cadena[8];
    segunda = *(cadena+8);  /* primera y segunda son iguales */
    printf("La segunda salida es %c %c\n", primera, segunda);

    alla = cadena+10;       /* cadena+10 es igual a &cadena[10] */
    printf("La tercera salida es %c\n", cadena[10]);
    printf("La cuarta salida es %c\n", *alla);

    for (indice = 0 ; indice < 100 ; indice++)
    lista[indice] = indice + 100;
    pt = lista + 27;
    printf("La quinta salida es %d\n", lista[27]);
    printf("La sexta salida es %d\n", *pt);

    return 0;
}
punteros y arrays

Utilizaremos un dibujo para representar la condición inicial de nuestro programa. Se puede observar que tenemos tres variables, dos punteros, una cadena y un array de enteros, ó también podríamos decir que tenemos tres variables, dos punteros y dos arrays. Cada array está compuesto por el array en sí y un puntero que señala al inicio del array de acuerdo a la definición de un array en C, esto quedará completamente aclarado en el siguiente párrafo. Cada array está compuesto de un número idéntico de elementos de los cuales solo unos cuantos al principio y al final son mostrados para mayor claridad del dibujo.
En C, el nombre de un array está definido como un puntero constante que señala al principio del array. En el código de ejemplo se observa que en la línea 8 asignamos una cadena constante a la variable llamada cadena simplemente para tener algunos datos con los cuales poder trabajar, enseguida asignamos a la variable de tipo char llamada primera el valor contenido en el primer elemento. Como el nombre de una cadena es un puntero constante al primer elemento de la cadena podemos asignarle el mismo valor a segunda utilizando el asterisco y el nombre de la cadena (*cadena). Tenga presente que en la línea 8 sería incorrecto escribir segunda = *cadena[0]; porque el asterisco toma el lugar de los corchetes, o sea hacen el mismo trabajo. Para todo propósito práctico, cadena es un puntero a una variable de tipo char, esto tiene una restricción que un puntero real no tiene, no puede ser cambiado como una variable ya que siempre contiene la dirección del primer elemento de la cadena y por lo tanto siempre apunta hacia el principio de la cadena. Aún y cuando no puede ser cambiado, se puede utilizar para referirse a otros elemntos de la cadena como veremos en la siguiente sección del programa.

En la línea 14 se ha asignado a la variable primera el valor del noveno caracter de la cadena (recuerde que en C los índices empiezan en 0) en tanto que a segunda se le asigna el mismo valor porque hemos permitido que el puntero señale más allá del principio de la cadena, en la línea 15 dice que se debe sumar 8 al valor del puntero cadena, entonces obtener el valor almacenado en aquella locación y almacenarlo en la variable segunda.

Es muy recomendable que Usted compile y ejecute este segundo programa de ejemplo ya que además de los conceptos tratados en los párrafos anteriores a su vez demuestra el concepto de la aritmética de punteros, experimentar con el código nos hará más familiar el manejo de los punteros y a la vez aportará nuevos elementos a lo ya visto en materia de arrays, es importante tener en cuenta que C maneja de forma automática la organización de los punteros de acuerdo al tipo de variable que señalan, esto dependiendo a su vez de la forma en que el compilador defina los diferentes tipos de variables, este concepto quedará más claro cuando en una lección posterior estudiemos el concepto de las estructuras, por lo pronto estudie detenidamente la última parte del código y observe como el sistema ajusta automáticamente el índice cuando utilizamos un puntero a una variable de tipo int. El resultado de la ejecución del programa es el siguiente:

Punteros y arrays

Volver al principio

Hola mundo

Punteros y funciones

Recordará que en la lección Funciones mencionamos que había dos maneras de obtener datos provenientes de una función. Una era a través de un array y la otra es utilizando un puntero, demostramos este concepto en el siguiente programa de ejemplo:


#include <stdio.h>

void reparar(int tuercas, int *tornillos);

int main()
{
    int pernos, rondanas;
    pernos = 100;
    rondanas = 101;
    
    printf("Los valores iniciales son %d %d\n", pernos, rondanas);
    /* Cuando se llama a "reparar" tomamos el valor de pernos*/
    /* y la direccion de rondanas */
    reparar(pernos, &rondanas);
    printf("Los valores finales son %d %d\n", pernos, rondanas);
    return 0;
}

void reparar(int tuercas, int *tornillos)/* tuercas es un valor entero */
                                         /* tornillos apunta a un entero */
{
    printf("Los valores inicilales en la funcion son %d %d\n",
      tuercas, *tornillos);
    tuercas = 135;
    *tornillos = 172;
    printf("Ahora los valores en la funcion son %d %d\n",
      tuercas, *tornillos);
}

En este programa tenemos dos variables declaradas en el programa principal, pernos y rondanas, ninguna fué declarada como puntero. Enseguida asignamos valores a ambas y las desplegamos en pantalla y entonces llamamos a la función llamada reparar ( ) tomando ambos valores, a la variable pernos simplemente la enviamos como parámetro a la función, pero en cambio tomamos la dirección de la variable rondanas como segundo parámetro de la función. Ahora tenemos un problema. Los dos argumentos no son iguales ya que el segundo es un puntero a una variable. De alguna manera debemos alertarle a la función que recibirá una variable entera y un puntero a una variable de tipo int, esto se hace de una manera simple. En la línea 19 vemos que la función declara como primer parámetro a una variable de tipo int llamada tuercas y a un puntero a una variable de tipo int llamado tornillos, por tanto la llamada a la función en el programa principal está acorde con el encabezado de la misma.

En el cuerpo de la función desplegamos los valores pasados como parámetros luego los modificamos y desplegamos los nuevos valores. Hasta este momento las cosas están lo suficientemente claras, la sorpresa viene cuando regresamos a la función principal main ( ) y desplegamos los valores una vez más. Encontramos que el valor de pernos se restaura al valor que tenía antes de la llamada a la función reparar, esto es así porque C hace una copia de la variable en cuestión y lleva la copia a la función llamada, dejando a la variable original intacta tal y como lo explicamos anteriormente. En el caso de la variable rondanas hacemos una copia del puntero a la variable y llevamos la copia a la función. Como tenemos un puntero a la variable original, aún y cuando el puntero es una copia local, sigue señalando a la variable original por lo tanto podemos cambiar el valor almacenado en rondanas desde el interior de la función reparar. Cuando regresamos al programa principal, encontramos a la variable rondanas con un nuevo valor. En el ejemplo no existe un puntero en el programa principal porque enviamos simplemente la dirección de la variable a la función reparar. El resultado de este programa es este:

punteros y funciones

Volver al principio

Hola mundo

Puntero a una función

En el siguiente programa mostramos como utilizar un puntero a una función, en la línea 7 el programa define puntero_a_funcion como un puntero a una función y no solo a cualquier función, señala a una función con un solo parámetro de tipo float, la función además no retorna nada, tal y como lo especifica la palabra clave void antes de la definición del puntero. El paréntesis en el nombre del puntero es necesario para evitar una posible confusión con una definición de prototipo para una función que regrese un puntero a void. Este es el código:


#include <stdio.h>

void despliega_cosas(float ignorar_datos);
void despliega_mensaje(float mostrar_datos);
void despliega_numero(float numero_flotante);

void (*puntero_a_funcion)(float);

int main()
{
    float pi = 3.14159;
    float two_pi = 2.0 * pi;

    despliega_cosas(pi);        /* Se muestra en pantalla */
    puntero_a_funcion = despliega_cosas;
    puntero_a_funcion(pi);      /* Se muestra en pantalla */
    puntero_a_funcion = despliega_mensaje;
    puntero_a_funcion(two_pi);  /* Se muestra en pantalla */
    puntero_a_funcion(13.0);    /* Se muestra en pantalla */
    puntero_a_funcion = despliega_numero;
    puntero_a_funcion(pi);      /* Se muestra en pantalla */
    despliega_numero(pi);       /* Se muestra en pantalla */

    return 0;
}

void despliega_cosas(float ignorar_datos)
{
    printf("Esta es la funcion ignorar_datos\n");
}

void despliega_mensaje(float mostrar_datos)
{
    printf("El dato a mostrar es %f\n", mostrar_datos);
}

void despliega_numero(float numero_flotante)
{
    printf("El numero a desplegar es %f\n", numero_flotante);
}

Observe los prototipos dados en las lineas 3 a 6 que declaran tres funciones que utilizan el mismo parámetro y regresa nada (void) al igual que el puntero. Como son similares, es posible utilizar el puntero para referirse a las funciones como lo demuestra la parte ejecutable del programa. En la línea 14 contiene una llamada a la función despliega_cosas y la línea 15 asigna el valor de despliega_cosas a puntero_a_funcion. Como el nombre de la función está definido como un puntero a esa función, su nombre puede ser asignado a una variable puntero hacia la función. Recordará que el nombre de un array es en realidad un puntero constante al primer elemento del array, de la misma manera, el nombre de una función es en realidad un puntero constante el cual señala a la función misma. El puntero es sucesivamente asignado a la dirección de cada una de las tres funciones y cada una es llamada una ó dos veces a manera de ilustración de cómo se utiliza un puntero a una función. El resultado de la ejecución del programa es el siguiente:

puntero a funcion

Un puntero a una función no es utilizado a menudo pero es una construcción muy poderosa cuando se utiliza, continuaremos estudiando el uso de los punteros examinando los programas que veremos en las siguientes lecciones.

Volver al principio

Hola mundo

© 1998, 1999 Virgilio Gómez Negrete, Derechos Reservados