Posteado por: wozgeass | abril 12, 2010

Programando en C para linux(septima parte)


Antes de iniciar con este tema me gustaria pedir una pequeña disculpa por el retraso para la realizacion de este post, y me gustaria que si encuentran algun error dejen un comentario espero sea de ayuda.

iniciemos . . .

APUNTADORES.

Los aputadores son una parte fundamental de C. Si no puedes usar los apuntadores apropiadamente entonces esta perdiendo la pontencia y la flexibilidad que C ofrece basicamente. El secreto para C esta en el uso de apuntadores.

C usa los apuntadores en forma extensiva. ¿Por que? genera codigo mas compacto y eficiete. Se usan apuntadores explicitamente con:

Arreglos.

Estructuras.

Funciones.

Variables.

DEFINICION DE UN APUNTADOR

Un apuntador es una variable que contiene la direccion en memoria de otra variable. El operador unario ‘&’ devuelve la direccion de memoria de una variable.

El operador de indireccion o derreferencia ‘*’ devuelve el contenido de un objeto apuntado por un apuntador.

ejemplo:

int *apuntador; //Apuntador de una variable entera

Se debe asociar a cada apuntador un tipo particular. Por ejemplo, no se puede asignar la direccion de una short int a un long int.

Para tener una mejor idea, considera el siguiente codigo:

#include<stdio.h>

main(){

int x=1;

int y=2;

int *apunt;

ap = &x;

y = *ap;

x = ap;

*ap = 3;

}

Cuando se compile el codigo se mostrara el siguiente mensaje:

warning: assignment makes integer from pointer without a cast

Con el objetivo de entender el comportamiento del codigo supongamos que la variable x esta en la localidad de la memoria 100, y en 200 y ap en 1000. NOTA: Un apuntador es una variable, por lo tanto, sus valores necesitan ser guardados en algun lado.

int x = 1;

int y = 2;

int *ap;

ap = &x;

0x100 0x200 0x300
x —> 1 y —> 2 ap —> 100

Las variables x e y son declaradas e inicializadas con 1 y 2 respectivamente, ap es declarado como un apuntador a entero y se le asigna la direccion de x (&x). Por lo que ap se carga con el valor 100.

y = *ap;

0x100 0x200 0x300
x —> 1 y —> 1 ap —> 100

Despues y obtiene el contenido de ap. En el ejemplo ap apunta a la localidad de memoria 100 — la localidad de x. Por lo tanto , y obtiene el valor de x— el cual es 1.

x = ap;

0x100 0x200 0x300
x —> 100 y —> 1 ap —> 100

Como se ha visto C no es muy estricto en la asignacion de valores de diferente tipo(apuntador a entero). Asi que es perfectamente legal(aunque el compilador genera un aviso de cuidado) asigna el valor actual de ap a la variable x. El valor de ap en ese momento es 100.

*ap = 3;

0x100 0x200 0x300
x —> 3 y —> 1 ap —> 100

Finalmente se asigna un valor al contenido de un apuntador (*ap).

Importante: Cuando un apuntador es declarado apunta algun lado. Se debe inicializar el apuntador antes de usarlo. Por lo que:

#include<stdio.h>

int main(){

int *ap;

*ap = 100;

}

Puede generar un error en tiempo de ejecucion o presentar un comportamiento erratico.

El uso correcto sera:

#include<stdio.h>

int main(){

int *ap;

int x;

ap = &x;

*ap = 100;

}

Con los apuntadores se puede realizar tambien aritmetica, por ejemplo:

#include<stdio.h>

int main(){

float *flp, *flq;

*flp = *flp + 10;

++*flp;

(*flp)++;

flq = flp;

}

Nota: Un apuntador a cualquier tipo de variables es una direccion en memoria — la cual es una direccion entera, pero un apuntador NO es una entero.

La razon por la cual se asocia un apuntador a un tipo de dato,, es po que no se debe conocer en cuantos bytes esta guardado el dato. De tal forma, que cuando se incrementa un apuntador, se incrementa el apuntador por un “bloque” de memoria, en donde el bloque esta en funcion del tamaño del dato.

Por tanto para un apuntador a un char, se agrega un byte a la direccion y para un apuntador a entero o flotante se agregan 4 bytes. De esta forma si a un apuntador a flotante se le suman 2, el apuntador entonces se mueve dos posiciones float que equivalen a 8 bytes.

APUNTADORES A FUNCIONES.

Cuando C pasa argumentos a funciones, los pasa por valor, es decir, si el parametro es modificado dentro de la funcion, una vez que termina la funcion el valor pasado de la variable permanece inalterado.

Hay muchos casos que se quiere alterar el argumento pasado a la funcion y recibir el nuevo valor una vez que la funcion ha terminado. Para hacer lo anterior se debe usar una llamada. Con esto se provoca que la computadora pase la direccion del argumento a la funcion.

Para entender mejor lo anterior considerememos la funcion swap() que intercambia el valor de dos argumentos enteros.

#include<stdio.h>
void swap(int *apunt_numero, int *apunt_valor);
int main(){
int numero, valor;
numero = 10;
valor = 30;
printf(“numero = %d \t valor = %d\n”, numero, valor);
swap(&numero, &valor);
printf(“numero = %d \t valor = %d\n”, numero, valor);
}
void swap(int *apunt_numero, int *apunt_valor){
int temporal;
temporal = *apunt_numero; //GUARDO EL VALOR DE LA DIRECCION
*apunt_numero = *apunt_valor; //PONE VALOR EN NUMERO
*apunt_valor = temporal;
}

APUNTADORES Y ARREGLOS.

Existe una relacion estrecha entre los punteros y los arreglos. En C, un nombre de un arreglo es un indice a la direccion de comienzo del arreglo.

En esencia, el nombre de un arreglo es un puntero al arreglo. Considerar lo siguente:

#include<stdio.h>

int a[10], x;

int *ap;

ap = &a[0];  //ap apunta a la direccion de a[0]

x = *ap;  // A ‘x’ se le asigna el contenido de ap (a[0] en este caso)

*(ap + 1) = 100;  //Se le asigna el segundo elemento de ‘a’ el valor de 100 usando ap

Como se puede observar en el ejemplola sentencia a[t] es identica a ap+t. Se debe tener cuidado ya que C no hace una revision de los limites del arreglo, por lo que se puede ir facilmente mas alla del arreglo en memoria y sobrescribir otras cosas.

C sin embargo es mucho mas sutil en su relacion entre arreglos y apuntadores. Por ejemplo se puede teclear solamente:

ap = a; en vez de ap = &a[0]; y tambien *(a+i); en vez de a[i];, en vez de a[i] esto es, &a[i] es equivalente con a+i.

Y como se ven el ejemplo, el direccionamiento de apuntadores se puede expresar como:

a[i] que es equivalente a *(ap + i)

Sin embargo los apuntadores y los arreglos son diferentes:

  • Un apuntador es una variable. Se puede ahacer ap=a y ap++.
  • Un arreglo NO ES una variable. Hacer a = ap y a++ ES ILEGAL.

Esta parte es extremadamente importante, espero la hayas entendido.

Con lo comentado se puede entender como los arreglos son pasados a las funciones. Cuando un arreglo es pasado a una funcion lo que en realidad se le esta psasando es la localidad de su elemento inical en memoria.

Porlo tanto:

strlen( s ) es equivalente a strlen( &s[0] )

Esta es la razon por la cual se declara la funcion como:

int strlen( char s[] ); y una declaracion equivalente es int strlen( char *s ) ya que char s[] es igual que char *s.

La funcion strlen() es una funcion de la biblioteca estandar que regresa la longitud de la cadena. Se muestra enseguida la version de esta funcion que podria escribirse:

strlen(char *cadena){

char *p = cadena;

while (*p != ‘ ‘){

p++;

}

return p – cadena;

}

Muestro a continuacion una funcion para copiar una cadena en otra. Al igual que en el fercicio anterior existen en la biblioteca estandar una funcion que hace lo mismo.

void strcpy ( char *cadena, char *copia ){

while ((*s++ = *t++) != ”);

}

En los dos ultimos ejemplos se emplea apuntadores y asignacion por valor.

NOTA: Se emplea el uso del caracter nulo con la sentencia while para encontrar el fin de la cadena.

ARREGLOS DE APUNTADORES.

En C se pueden tener arreglos de apuntadores ya que los apuntadores son variables.

A continuacion se muestra un ejemplo de uso: Ordenar las lineas de un texto de diferente longitud.

Los arreglos de apuntadores son una representacion de datos que manejan de una forma eficiente y conveniente lineas de texto de longitud variable.

¿Cómo se puede hacer lo anterior?

  1. Guarda todas la lineas en un arreglo de tipo char grande. Observando que \n marca el fin de cada linea.
  2. Guardar los apuntadores en un arreglo diferente donde cada apuntador apunta al primer caracter de cada linea.
  3. Comparar dos lineas usando la funcion strcmp() de la biblioteca del ANSI C.
  4. Si dos lineas estan desacomodadas — intercambiar (swap) los apuntadores(no el texto )

Con lo anterior se elimina:

  • El manejo complicado del almacenamiento.
  • Alta sobrecarga por el movimiento de lineas.

ARREGLOS MULTIDIMENSIONALES Y APUNTADORES.

Un arreglo multidimensional puede ser visto en varias formas en C, por ejemplo:

Un arreglo de dos dimensiones es un arregl de una dimension, donde cada uno de los elementos es en si mismo un arreglo.

Por lo tanto, la notacion:

arreglo[n][m];

Nos indica que los elementos del arreglo estan guardados renglon por renglon. Cuando se pasa un arreglo bidimensional a una funcion se debe especificar el numero de columnas — el numero de reglones es irrelevante.

La razon de lo anterior, es nuevamente los apuntadores. C requiere conocer cuantas son las columnas para que pueda brincar de renglon en renglon en la memoria.

Considerando que una funcion deba recibir int arreglo[5][35]; se puede declarar el argumento de la funcion como:

funcion ( int arreglo[][35]){ . . .}

O aun.

funcion (int (*arreglo)[35]){ . . . }

En el ultimo ejemplo se requieren los parentesis (*a) ya que [ ] tiene una precedencia mas alta que *.

Por lo tanto:

int (*arreglo)[35]; declara un apuntador a un arreglo de 35 enteros, y por ejemplo si hacemos la siguiente referencia arreglo + 2, nos estaremos refiriendo a la direccion del primer elemento que se encuentra en el tercer renglon de la matriz supesta, mientras que int *arreglo[35]; declara un arreglo de 35 apuntadores a enteros.

Ahora veamos la diferencia ( sutil ) entre apuntadores y arreglos. El manejo de cadenas es una aplicacion comun de esto.

Considera:

char *nombre[10];

char arr_nombre[10][20];

En donde es valido hacer nombre[3][4] y arr_nombre[3][4] en C.

Sin embargo:

arr_nombre es un arreglo verdadero de 200 elementos de dos dimensiones tipo char.

El acceso de los elementos arr_nombre en memoria se hace bajo la siguente formula 20 * renglon + columna + direccion_base.

En cambio nombre tiene 10 apuntadores a elementos.

NOTA: Si cada apuntador en nombre indica un arreglo de 20 elementos entonces y solamente entonces 200 chars estaran disponibles.

Con el primer tipo de declaracion se tiene la ventaja de que cada apuntador puede apuntar a arreglos de diferente longitud.

Considera:

char *nombre[] = {“No mes”, “Enero”, “Febrero”, “Marzo”, . . . };

char arr_nombre[][15] = {“No mes”, “Enero”, “Febrero”, “Marzo”, . . . };

Se puede indicar que se hace un manejo mas eficiente del espacio haciendo uso de un arreglo de apuntadores y usando un arreglo bidimencional.

INICIALIZACION ESTATICA DE ARREGLOS DE APUNTADORES.

La inicializacion de arreglos de apuntadores es una aplicacion ideal para un arreglo estatico interno:

funcion (){

static char *nombre[] = {“No mes”, “Enero”, “Febrero”, “Marzo”, . . .};

}

Recordando que con el especificador de almacenamiento de clase static se resera en forma permanente memoria del arreglo, mientras el codigo se esta ejecutando.

APUNTADORES A ESTRUCTURAS.

Los apuntadores a estructuras se definen facilmente y en una forma directa:

#include<stdio.h>

main(){

struct PRUEBA {char a,b,c;} probando;

struct PRUEBA *ap_probando;

probando.a = probando.b = probando.c = ‘a’;

ap_probando = &probando; //asignamos probando al apuntador

ap_probando->a = ‘a’; // Con el operador -> se accesan los datos

//nota: en algunas distribuciones linux me dieron problemas tambien podemos usar:

ap_probando.a = ‘a’;

ap_probando.b = ‘b’;

ap_probando->b = ‘b’;

}

FALLAS COMUNES CON APUNTADORES.

Los errores mas comunes que pasan a la hora de usar apuntadores son 2:

  • No asignar un apuntador a una direccion de memoria antes de usarlo.

int *x;

*x = 100;

Lo adecuando seria, tener primero una localidad fisica de memoria.

int *a, b;

a = &b;

*a = 100;

  • Indireccion no valida.

Supongamos que se tiene una funcion llamada malloc() la cual trata de asignar memoria dinamicamente (en tiempo de ejecucion), la cual regresa un apuntador al bloque de memoria requerida si se pudo o un apuntador a nulo en otro caso.

char  *malloc() —  Una funcion de la biblioteca estandar que se vera mas adelante.

Supongamos que se tiene un apuntador char *p;

*p = (char *) malloc(100); // pide 100 bytes de la memoria

*p = ‘a’

¿Existe un error en el anterior codigo? ¿Cuál es?

Claro que existe un error, El * en la primera linea ya que malloc regresa un apuntador y *p no apunta a ninguna direccion.

El codigo correcto debera ser:

p = (char *) malloc(100);

Ahora si malloc no puede regresar un bloque de memoria, entonces p es nulo, y por lo tanto no se podra hacer:

*p = ‘a’;

Un buen programa en C debe revisar lo anterior, por lo que el codigo anterior puede ser reescrito como:

p = (char *) malloc(100);  //pide 100 bytes de la memoria

if ( p == NULL ){  //preguntamos si regreso NULL

printf(“ERROR: fuera de memoria\n”);

exit(1);

}

*p = ‘a’

^_^!

Anuncios

Responses

  1. MMM MAS QUE RESPUESTA, ES UNA PREGUNTA:

    Como convertir un numero(int) a carácter Char, Es un solo dígito.

    he intentado con la función itoa, pero estube buscando y dice k no esta en linux porque no es estandar, intente con la funcion sprintf y no me despliega nada y despues me deplieja basura como �, tambien lo intente de forma directa VarChar = VarInt y me dice incompatibilidad de tipos :S ojala que exista alguna solucion

    Espero que tengas un ejemplo

    gracias.

    • claro amigo, checa este codigo:
      #include
      #include

      int imprime_caract(){
      int valor = 97;
      printf(“prueba de caracter [ %c ]\n “, valor);
      }

      char imprime_numero(){
      char caracter = “a”;
      printf(“Prueba de entero [ %d ]\n”, caracter);
      }

      int main(){
      imprime_caract();
      imprime_numero();
      }


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Categorías

A %d blogueros les gusta esto: