miércoles, 24 de noviembre de 2010

Punteros, ejemplos en lenguaje C

Entrada para puntos extras.

Lo punteros son variables que apuntan a otras variables, es decir, que guardan la dirección de la ubicación en memoria de otras variables. La dirección es representada con un numero por lo general en base hexadecimal, ejemplo: 0x4F8G23A.

Ahora veremos ejemplos de como manejar punteros en lenguaje C.

La forma de declarar un puntero es: tipo* puntero;

#include <stdio.h>

int main(int inicial, char** args){
  int* puntero;
  double* otro_puntero;
  char* char_puntero;
  int* arreglo[100];
  return 9;
}

Hay que ver el signo asterisco (*) llamado indirección antes del nombre del puntero, este designa que es propiamente un puntero y no una variable convencional. Aquí solamente le decimos al compilador que reserve una posición en la memoria para nuestro puntero.

Antes de usar el puntero tenemos que iniciarlo, para eso le pediremos que guarde la dirección (representada con el signo amperson &) de alguna variable.

tipo* puntero;
tipo variable;
puntero = &variable;

Para cambiar el contenido de la dirección a la que apunta el puntero, utilizamos el operador de indirección. Esto hace que cambie el valor al que hace referencia y no la dirección del mismo puntero.

*puntero = nuevo_valor;

Un ejemplo completo

#include <stdio.h>

int main(int a, char** args){
  int* p; //puntero                                                            
  int m[100];

  p = &m[0]; //& direccion                                                      
  m[0] = 4;
  printf("%d\n", *p); //* indireccion                                           

  m[0] = 883;
  printf("%d\n", *p); //cambia dinamicamente, la direccion es fija              

  *p = 999; //accediendo al valor                                                printf("%d\n", *p);
  return 24;
}

La relación estrecha existente entre arreglos y punteros se debe a que el nombre de un arreglo es equivalente a la dirección del primer elemento (&arreglo[0]), no es necesario utilizar el operador de dirección para apuntar al primer elemento de un arreglo.

int* p; //puntero                                                              int m[100];

p = &m[0]; //& direccion                                                       
m[0] = 993;
printf("%d\n", *p);

p = m;
printf("%d\n", *p);

Si a un puntero se le suma una cantidad, cambia la dirección esa misma cantidad mas no el valor al que apunta. Es equivalente a hacer:

int* p; //puntero
p++;
p--;

Debido a la precedencia de operadores, la expresión:

variable = *(puntero++);

significa asignar el contenido de lo apuntado, a la variable, y después incrementar la dirección un lugar, y la expresión

variable = *(++puntero);

significa incrementar la dirección del puntero y después asignarle el contenido de lo apuntado, a la variable.

Si declaramos una variable en C, el espacio se reserva durante todo el tiempo de ejecución aun cuando solo lo necesitemos para una pequeña fracción del programa, para evitar esto podemos almacenar el arreglo en la zona de amontonamiento ó Heap.

double* puntero;

esta declaración no crea un espacio para almacenar la varible, solamente una dirección para después almacenarla, ahora tenemos que designar la cantidad de espacio que deseamos, para ello utilizamos una función de la librería stdlib dedicada a la tarea, entre ellas esta la mas común: malloc

puntero = malloc(8);

malloc recibe la cantidad de bytes que se desea reservar y nos devuelve un puntero o dirección a la primera posición de la "pila" reservada en el Heap, si no se puede crear el espacio, devuelve un puntero a NULL.

Utilizando la función sizeof podemos designar el tamaño de cada variable.

puntero = malloc(sizeof(double));

Si queremos guardar en el Heap alguna operación solamente haríamos algo como

*puntero = operacion;

Y como y habíamos visto para recuperar la variable almacenada

var = *puntero;

Como una cadena no es mas que un arreglo que almacena caracteres, podemos guardar cadenas en punteros

char* puntero;
puntero = "Hello World"

Así *puntero contendría el primer elemento de la cadena (H) y si necesitamos el segundo solamaente escribimos *(puntero + 1).

De la misma manera que podemos declarar arreglos, podemos declarar arreglos de punteros

char* arreglo[] = {"frase1", "frase2", "frase3"};

Con *arreglo podemos acceder a la primera frase, con *(arreglo + 1) a la segunda frase, etc.

Para crear estructuras y asignar valores a los miembros hacemos:

typedef struct estudiantes{
  int a;
  double b;
  char c[10];
}estudiante;

estudiante* yo;
yo = (estudiante*) malloc(sizeof(estudiante));
yo -> a = 234;
yo -> c = 'H';
yo -> (c + 1) = 'e';

Nota que antes de inicializar un elemento de la estructura es necesario alojarla en la memoria con la ayuda de malloc.

Cuando pasamos una estructura a una función, se copian todos los elementos de la estructura, para resolverlo pasamos un puntero que apunta a la estructura en lugar de toda la estructura.

struct nombre{
  int datos;
}*puntero;

funcion(struct nombre *puntero);

Una función que regresa un puntero se escribe

tipo *funcion(struct nombre *puntero);

Bibliografía

2 comentarios: