Capítulo 5_Apuntadores

17
Capítulo 5. http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.] Capítulo 5. Uso avanzado de apuntadores 5. Apuntadores y arreglos multidimensionales Supongamos que tenemos la siguiente declaraciÃ3n: int Matriz4][3]={ {8,0,-1}, {4,10,-3}, {23,64,88}, {-9,7,14} }; Matriz Matriz[0] 8 0 -1 Matriz[1] 4 10 -3 Matriz[2] 23 64 88 Matriz[3] -9 7 14 Esto define un arreglo de 2 dimensiones, el cual representa una matriz de 4 renglones por 3 columnas. Al igual que en el caso de arreglos de una sola dimensiÃ3n, podemos relacionar este arreglo con apuntadores. Esto se logra al considerar la matriz como: ¡ un arreglo unidimensional de arreglos unidimensionales! Nuestro arreglo bidimensional M, en la figura, tiene 4 renglones y 3 columnas. M es el nombre de todo el arreglo; M[0] se refiere al primer renglÃ3n de la matriz. M[0][0] se refiere al primer elemento de la matriz. El compilador trata a ambos M y M[0] como arreglos, pero de tamaño diferente, como se muestra con el siguiente cÃ3digo: printf("Tamaño de Matriz\t\t=%d\n", sizeof(Matriz)); printf("Tamaño de Matriz[0]\t\t=%d\n", sizeof(Matriz[0])); printf("Tamaño de Matriz[0][0]\t\t=%d\n", sizeof(Matriz[0][0])); Al correr: Tamaño de Matriz = 24 Tamaño de Matriz[0] = 6 Tamaño de Matriz[0][0] = 2 El tamaño de Matriz[0][0] es 2, como es de esperarse. No obstante, el tamaño de Matriz es el tamaño del todo el arreglo,

description

Apuntadores Programacion en C

Transcript of Capítulo 5_Apuntadores

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    Captulo 5.

    Uso avanzado de apuntadores5. Apuntadores y arreglos multidimensionales

    Supongamos que tenemos la siguiente declaraci3n:

    int Matriz4][3]={ {8,0,-1},

    {4,10,-3},

    {23,64,88},

    {-9,7,14} };

    Matriz

    Matriz[0] 8 0 -1

    Matriz[1] 4 10 -3

    Matriz[2] 23 64 88

    Matriz[3] -9 7 14

    Esto define un arreglo de 2 dimensiones, el cual representa una matriz de 4 renglones por 3 columnas. Al igual que en el caso dearreglos de una sola dimensi3n, podemos relacionar este arreglo con apuntadores. Esto se logra al considerar la matriz como: unarreglo unidimensional de arreglos unidimensionales! Nuestro arreglo bidimensional M, en la figura, tiene 4 renglones y 3 columnas.M es el nombre de todo el arreglo; M[0] se refiere al primer rengl3n de la matriz. M[0][0] se refiere al primer elemento de la matriz.El compilador trata a ambos M y M[0] como arreglos, pero de tamao diferente, como se muestra con el siguiente c3digo:

    printf("Tamao de Matriz\t\t=%d\n", sizeof(Matriz));

    printf("Tamao de Matriz[0]\t\t=%d\n", sizeof(Matriz[0]));

    printf("Tamao de Matriz[0][0]\t\t=%d\n", sizeof(Matriz[0][0]));

    Al correr:

    Tamao de Matriz = 24

    Tamao de Matriz[0] = 6

    Tamao de Matriz[0][0] = 2

    El tamao de Matriz[0][0] es 2, como es de esperarse. No obstante, el tamao de Matriz es el tamao del todo el arreglo,

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    y el tamao de Matriz[0] es el tamao de un rengl3n.

    Sabemos que el nombre de un arreglo unidimensional es un apuntador al primer elemento. Para una matriz es lo mismo, soloque el primer elemento es el arreglo Matriz[0], siendo Matriz[0] un apuntador a int. Se tiene que:

    Matriz equivale a &Matriz[0] y a &Matriz[0][0].

    Matriz y Matriz[rengl3n] son apuntadores constantes. Entonces, Matriz es un apuntador al arreglo Matriz[0], y Matriz[0] es unapuntador a int. Esto se puede expresar en trminos del operador de indirecci3n como:

    Matriz es un apuntador a un arreglo de int.*Matriz es un apuntador a int. Esto es evidente porque Matriz[0] es un apuntador a int, y *Matriz *(Matriz+0) Matriz[0] es un apuntador a int.**Matrizs un int. Esto es evidente porque Matriz[0][0] es un int, y **M atriz *(*Matriz) *(Matriz[0]) *(Matriz[0]+0) Matriz[0][0] es un int.

    Por otro lado, Matriz[i] es un apuntador al i-es?mo rengl3n de la matriz.

    1. Pasando arreglos multidimensionales a funciones.

    Tenemos el siguiente c3digo:

    #include

    void muestra_matriz(int mat[][3]);

    int main(void)

    {

    int M[4][3]={ {8,0,-1},

    {4,10,-3},

    {23,64,88},

    {-9,7,14} };

    muestra_matriz(M);

    return (0);

    }

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    void muestra_matriz(int matriz[][3])

    {

    int renglon, columna;

    for (renglon=0;renglon

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    Un arreglo de apuntadores es un arreglo unidimensional cuyos elementos son apuntadores. El arreglo apun_cad es un arreglo deapuntadores. Puesto que los [] tienen mayor precedencia que el *, la declaraci3n *apun_cad[3] es equivalente a *(apun_cad[3]) en lacual tenemos un arreglo en donde cada elemento es un apuntador a char. Analizando la declaraci3n char *(apun_cad[3]), tenemosque:

    apun_cad[3] es de tipo char *.*(apun_cad[3]) es de tipo char.

    Los elementos del arreglo apun_cad son referenciados en la manera usual como, apun_cad[0], apun_cad[1], apun_cad[2], etc. y ambosque son apuntadores a cadenas se pueden manejar como apuntadores o como cadenas:

    apun_cad[1]="Hola";

    *(apun_cad[1]+1)=?I?; o apun_cad[1][1] = ?Y?;

    Nota que es valida la notaci3n de matrices.

    Desde que el nombre de un arreglo es un apuntador al primer elemento del arreglo, y el primer elemento de apun_cad es un apuntador,apun_cad es un apuntador a apuntador. Desde que el nombre de un arreglo, apun_cad es una apuntador constante. Usando el operadorde indirecci3n para desreferenciar el apuntador apun_cad, tenemos.

    apun_cad es un apuntador a apuntador a char.*apun_cad es un apuntador a char. esto es evidente porque apun_cad[0] es un apuntador a char, y *apun_cad = *(apun_cad+0)

    apun_cad[0] es una apuntador a char.**apun_cad es char. esto es evidente porque apun_cad[0][0] es un char, y **apun_cad = *(*apun_cad) *(apun_cad[0]+0)

    apun_cad[0][0] es un char.

    Recuerda que apun_cad es una apuntador constante, los operadores ++ y -- no deben ser aplicados a apun_cad.

    1. Apuntadores a apuntadores

    Considera lo siguiente:

    char *apun_cad[3] ={ "Programar",

    "es",

    "divertido"};

    char *apun_apun;

    apun_apun=apun_cad;

    apun_apun apun_cad

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    "Programar"

    "es"

    "divertido"

    Podemos accesar los elementos de apun_cad, puesto que es un arreglo, pero por lo mismo no podemos incrementarlo, por que es unapuntador constante. Por lo tanto, lo que sigues es ilegal:

    for (i=0;i

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    de apuntadores a char (al primer elemento); cada apuntador del arreglo apunta al primer carcterde cada argumento:

    argv[0]

    argv[1]

    argv[3]

    NULL

    argv /* es de tipo apuntador a apuntador a char */

    argv[0] 3 *argv /* es de tipo apuntador a char */

    argv[0][1] 3 **argv /* es de tipo char */

    Aqu? estamos empleando el concepto de arreglo de apuntadores. Un arreglo de apuntadores se maneja como cualquier otroarreglo; solo hay que tener mucho cuidado con los tipos. El siguiente programa suma 4 enteros:

    #include

    #include

    int main(int argc, char **argv)

    {

    int suma, a, b, c, d;

    if(argc!=5) {

    printf("Numero de argumentos invalido \n");

    exit(EXIT_FAILURE);

    }

    a=atoi(argv[1]);

    b=atoi(argv[2]);

    c=atoi(argv[3]);

    d=atoi(argv[4]);

    suma = a + b+ c +d;

    printf("La suma es : %i\n", suma);

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    return (0);

    }

    La funci3n atoi convierte una cadena a int.

    La funci3n exit termina la ejecuci3n del programa. La macro EXIT_FAILURE indica que el programa no termino bien.

    1. Asignaci3n dinmica de memoria.

    Un programa en C puede almacenar informaci3n en la memoria principal de dos maneras. La primera se basa en el uso devariables globales y locales. En el caso de las variables globales, el almacenamiento se mantiene fijo a lo largo de laejecuci3n del programa. Para las variables locales, el almacenamiento se asigna de la pila de la computadora. Tanto para elalmacenamiento de variables globales como de variables locales, es preciso que el programador conozca de antemano lacantidad de espacio en memoria que necesitar en cada situaci3n.

    La segunda manera de almacenar informaci3n es por medio del sistema de asignaci3n dinmica de memoria. En estemtodo, el espacio de memoria para almacenamiento se asigna segn se necesite del rea de memoria libre mont3n,devolvindose a la misma cuando se haya dejado de utilizar. La regi3n de memoria libre se encuentra entre el rea dealmacenamiento permanente del programa y la pila, dentro del segmento de datos.

    Una ventaja de usar la asignaci3n dinmica de memoria es que la misma zona de memoria puede ser utilizada muchas vecespara almacenar datos distintos a lo largo de la ejecuci3n del programa. Esto es, en un momento dado se pueden almacenardatos de un tipo y liberar ese espacio de memoria cuando ya no se necesiten almacenar esos datos, y a continuaci3n emplearesa zona de memoria para almacenar otro tipo de datos.

    El estndar ANSI de C define cuatro funciones para el sistema de asignaci3n dinmica de memoria: malloc(), calloc(),free() y realloc(). Donde malloc() y free() son las funciones que constituyen el ncleo del sistema de asignaci3n dinmicade memoria.

    Las funciones definidas por el estndar ANSI requieren del archivo header stdlib.h, para poder ejecutarse.

    Las principales funciones de asignaci3n dinmica de memoria, que proporciona ANSI C, se presentan a continuaci3n:

    2. malloc().

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    Sintaxis:

    void *malloc(size_t tam)

    La funci3n malloc() regresa un apuntador a la direcci3n de la primera localidad del bloque de memoria que acaba de asignar. Esteapuntador debe ser forzado, por medio de un molde (cast), al tipo de dato para el cual se est reservando memoria, con el fin depoder acceder correctamente a cada dato. El argumento de la funci3n especifica el nmero de bytes que se van a reservar dememoria. Si no hay suficiente memoria para satisfacer la petici3n, malloc() regresa un apuntador nulo. Es importante que antes deusar el apuntador se verifique que ste no es nulo, por que si se intenta usar un apuntador nulo, lo ms probable es que se produzcala detenci3n del programa.

    A continuaci3n se presenta un ejemplo que expone como se emplea esta funci3n:

    #include

    int *asigna_mem(void)

    {

    int *punt;

    punt = malloc(100*sizeof(int));

    if (punt==NULL){

    printf("Falla en la asignaci3n de memoria. \n");

    exit(EXIT_FAILURE);

    }

    return punt;

    }

    3. calloc().

    Sintaxis:

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    void *calloc(size_t num, size_t tam)

    La funci3n calloc() regresa un apuntador a la direcci3n de la primera localidad del bloque de memoria que acaba de asignar. Esteapuntador debe ser forzado, por medio de un molde (cast), al tipo de dato para el cual se est reservando memoria, con el fin depoder acceder correctamente a cada dato. La cantidad de memoria que se asigna est determinada por el producto (num)(tam), dondetam est dado en bytes y representa el tamao asociado al tipo de dato, y num es el nmero de datos que se desea almacenar. Si nohay suficiente memoria para satisfacer la petici3n, calloc() regresa un apuntador nulo. Es importante que antes de usar el apuntador severifique que ste no es nulo.

    El siguiente ejemplo muestra el uso de calloc():

    #include

    #include

    float *aparta_mem(void)

    {

    float *punt;

    punt = (float *) calloc(80, sizeof(float));

    if (punt==NULL){

    printf("Falla en la asignaci3n de memoria.");

    exit(EXIT_FAILURE);

    }

    return punt;

    }

    1. realloc()

    Sintaxis:

    void *realloc(void *ptr, size_t nuevotam)

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    Esta funci3n cambia el tamao de la memoria asignada. La zona de memoria asignada est apuntada por ptr, y cambia altamao indicado por nuevotam. El valor de nuevotam, en bytes, puede ser mayor o menor que el actual.

    Esta funci3n regresa un apuntador al bloque de memoria asignado, ya que es posible que realloc() tenga que mover el bloquede memoria para aumentar su tamao. De ocurrir esto, el contenido del bloque antiguo se copia al nuevo.

    El apuntador que regresa realloc() debe ser forzado, por medio de un molde (cast), al tipo de dato para el cual se estcambiando el tamao de memoria, con el fin de poder acceder correctamente a cada dato.

    La funci3n falla si no hay suficiente memoria libre en el mont3n para asignar nuevotam bytes. Si ocurre esto, realloc()regresa un apuntador nulo.

    El siguiente ejemplo muestra como se emplea esta funci3n:

    #include

    #include

    #include

    int main(void)

    {

    char *punt;

    punt = (char *) malloc(17);

    if (punt==NULL){

    printf("Falla en la asignaci3n de memoria. \n");

    exit(EXIT_FAILURE);

    }

    strcpy(punt, "Las quince letras");

    punt = (char *) realloc(punt, 18);

    if (punt==NULL){

    printf("Falla en la asignaci3n de memoria. \n");

    exit(EXIT_FAILURE);

    }

    strcat(punt, ".");

    printf(punt);

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    free(punt);

    return 0;

    }

    2. free()

    Sintaxis:

    void free(void *ptr)

    La funci3n free() retorna al mont3n, el bloque de memoria apuntado por ptr. Esto permite que la memoria liberada se puedavolver a asignar posteriormente.

    La funci3n free() s3lo puede liberar memoria que previamente fue asignada por medio de las funciones calloc(), malloc() yrealloc(). Si se trata liberar de memoria usando un apuntador que no fue devuelto por alguna de las funciones antesmencionadas, lo ms probable es que se destruya el sistema de gesti3n de memoria, provocando la detenci3n del sistema.

    El uso de free() se muestra en el ejemplo utilizado para explicar la funci3n realloc().

    3. Apuntadores a estructuras

    Considera lo siguiente:

    struct fecha {

    int mes;

    int dia;

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    int anio;

    };

    struct fecha hoy, *apun_fecha;

    apun_fecha = &hoy;

    (*apun_fecha). mes = 10; apun_fecha mes = 10;

    (*apun_fecha).dia = 14; apun_fecha dia = 14;

    (*apun_fecha).anio = 95; apun_fecha anio = 95;

    apun_fecha hoy

    10 mes

    14 dia

    95 anio

    Hemos visto que un apuntador puede ser definido para apuntar a un tipo de dato bsico como son los int o las char, o a unarreglo. Pero los apuntadores pueden tambin definirse para apuntar a una estructura, como se ve en la figura. En la figura seemplea la estructura fecha definida como:

    struct fecha {

    int mes;

    int dia;

    int anio;

    };

    Al igual que definimos variables de tipo estructura fecha, como:

    struct fecha hoy, maniana;

    podemos definir una variable tipo apuntador estructura fecha:

    struct fecha *apun_fecha;

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    Los apuntadores pueden emplearse de la manera usual:

    apun_fecha = &hoy;

    Una vez que la asignaci3n ha sido hecha, cualquier miembro de la estructura puede ser indirectamente acezada a travs deapun_fecha como:

    (*apun_fecha).mes = 10;

    (*apun_fecha).dia = 14;

    (*apun_fecha).anio = 95;

    Se requieren los parntesis por que el operador miembro de estructura (.) Tiene mayor precedencia que el operador deindirecci3n (*). Los apuntadores a estructuras son muy a menudo usados en C, por esa raz3n existe un operador especialpara accesar indirectamente los miembros de unas estructura. Este es el operador a estructura,. Por lo tanto, las asignacionesanteriores pueden codificarse como:

    apun_fecha mes = 10;

    apun_fecha dia = 14;

    apun_fecha anio = 95;

    Lo cual es mas corto y fcil de leer.

    A continuaci3n un programa de ejemplo de apuntadores a estructuras. Este programa lee una lista de empleados y la imprime.El tamao del arreglo se determina dinmicamente usando la funci3n calloc. La macro Resetea_entrada elimina caracteresnueva l?nea extras.

    #include

    #include

    #define Resetea_entrada while(getchar()!=?\n?)

    typedef struct {

    char nombre[20];

    int edad;

    float sal_men;

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    } persona;

    void lee_emp(persona *apun_emp)

    int main(int argc,char *argv[])

    {

    persona *empleados;

    int num_de_emp;,i;

    if (argc == 2) {

    num_de_emp = atoi(argv[1]);

    if((empleados = persona)calloc(num_de_emp,sizeof(persona)))==NULL) {

    printf("Ya no hay memoria\a\n");

    exit(EXIT_FAILURE);

    }

    for(i=0;i

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    Resetea_entrada;

    printf("Salario del empleado: ");

    scanf("%f",&apun_emp men_sal);

    Resetea_entrada;

    }

    4. Apuntadores a funciones.

    Un uso ms avanzado de los apuntadores son los apuntadores a funciones. Desde que las funcionesocupan un rea de memoria, C puede definir un apuntador a ellas. Los apuntadores a funciones seemplean por 2 razones. Primero una funci3n no puede ser pasada a otra funci3n como unargumento, pero un apuntador si. Segundo, una funci3n no se puede almacenar en un arreglo o unaestructura, pero un apuntador si puede serlo. Una funci3n puede llamarse por medio de un apuntadorque apunta a ella. As?, varias funciones pueden llamarse usando la misma instrucci3n de llamado.

    En el siguiente ejemplo, apun_fun es un apuntador a una funci3n divide, la cual regresa un flotante yque requiere 2 argumentos enteros. Este apuntador se inicializa igualndolo al nombre de cualquierfunci3n que regrese un entero. As? como el nombre de un arreglo es un apuntador constante alprimer elemento, el nombre de una funci3n es un apuntador constante que apunta al c3digoejecutable de la funci3n. Esta vez, main usa un apuntador a funci3n para llamar a la funci3ndivide.

    #include

    float divide(int,int);

    int main(void)

    {

    float (*apun_fun)(int,int);

    float respuesta;

    apun_fun=divide;

    respuesta = (*apun_fun)(3,2);

    printf("respuesta=%.1f\n",respuesta);

    return (0);

    }

    float divide(int op1,int op2)

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    {

    float resultado;

    resultado = (float)op1/(float)op2;

    return (resultado);

    }

    Cuando trabajamos con apuntadores a funciones, el ANSI C requiere conocer el tipo de valor regresala funci3n, as? como el numero y tipos de parmetros que requiere. Para declarar la variableapun_fun para ser del tipo "un apuntador a una funci3n que regresa un float y que requiere dosargumentos de tipo int", se usa la declaraci3n:

    float (*apun_fun)(int,int);.

    Los parntesis en torno a *apun_fun son requeridos porque el operador de llamada (), tiene mayorprecedencia que el operador de indirecci3n *, y *apun_fun() podr?a ser interpretado por elcompilador como *(apun_fun()), la cual es una funci3n llamada apun_fun la que regresa unapuntador.

    El ejemplo anterior muestra como usar apuntadores a funci3n, pero no da una ventaja con respecto ala manera usual de llamada funciones. Un uso apropiado de loas apuntadores a funci3n los tenemosen la implantaci3n de mens. Dado que se puede definir un arreglo de apuntadores a funciones, esviable usar este ltimo para crear un men. El arreglo mapea las opciones del usuario con laejecuci3n de los comandos. En este arreglo se puede buscar y ejecutar la opci3n ms rpido quecon un switch o un if. A continuaci3n te damos un ejemplo de ello.

    Una estructura llamada comando se define para que contenga 2 miembros, un apuntador al nombre delcomando, y un apuntador a la funci3n correspondiente que se llama:

    struct cmd {

    char *nombre_cmd;

    int (*apun_fun)();

    }

    Un vector, donde cada elemento es del tipo estructura comando, puede ser inicializado para quecontenga todos los nombres de los comandos y los correspondientes nombres de las funciones:

    struct cmd tabla[]={ {"sumar",sumaritem},

  • Captulo 5.

    http://sai.uam.mx/apoyodidactico/pa/Unidad5/pauni5.html[03/03/2010 11:54:11 a.m.]

    {"borrar",borraritem},

    {"listar",listaritem},

    {"salir",terminar} };

    Cuando se escribe el nombre del comando por parte del usuario, en la tabla se puede buscar, y lafuncin apropiada puede ser llamada, como sigue:

    typedef enum {FALSO,CIERTO} bool;

    char comando[10]; /*comando dado por el usuario */

    int num_de_cmd: /* total de numero de comandos en la tabla */

    bool cmd_no_hallado = CIERTO;

    prinf("Dar comando: ");

    gets(comando);

    for (i=0;((i