Apunte de Punteros - C-grupal

27
 Facultad de Informática. UNLP. Seminario de Lenguajes C - Unidad 5 Facultad de Informática Universidad Nacional de La Plata Seminario de Lenguajes Opción C Unidad 5 – Arreglos y Punteros 1

description

Manejo de punteros en ansi C.

Transcript of Apunte de Punteros - C-grupal

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Facultad de Informtica

    Universidad Nacional de La Plata

    Seminario de Lenguajes

    Opcin C

    Unidad 5 Arreglos y Punteros

    1

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    ndice de contenidoSobre los Punteros y su implementacin ......................................................................................................... 3

    Definicin ................................................................................................................................................................... 3

    Operadores & y * ............................................................................................................... 6 Pasaje por Referencia ........................................................................................................ 9

    Sobre los Arreglos y su implementacin ....................................................................................................... 10

    Definicin ........................................................................................................................ 11 Arreglos y Punteros ......................................................................................................... 12 Punteros Genricos .......................................................................................................... 16

    Arreglos de Caracteres ......................................................................................................................................... 16

    Definicin ........................................................................................................................ 16 Cadenas de caracteres con arreglos y punteros ................................................................ 17

    Aritmtica de Punteros ......................................................................................................................................... 20

    Arreglo de Punteros .............................................................................................................................................. 21

    Gestin de la memoria de C ............................................................................................................................... 24

    Malloc .............................................................................................................................. 24 Free .................................................................................................................................. 25 Calloc ............................................................................................................................... 25 Tabla de Resumen ............................................................................................................ 25

    Bibliografa ............................................................................................................................................................... 27

    2

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Sobre los Punteros y su implementacin

    En la clase 1 estudiamos el concepto de variable, un bloque de memoria

    que es identificada con un nombre y cuyo contenido puede variar a lo

    largo de la ejecucin de un programa. El tamao del bloque vara depende del tipo de dato de la

    variable. Por ejemplo, en PCs de 32 bits el tamao del tipo entero es de 4 bits, Este tamao puede

    variar entre distintos sistemas operativos. Incluso en C se puede tener distintos tipos de enteros

    como el long int.

    Para conocer el tamao de de los diferentes tipos de enteros podes ejecutar el siguiente cdigo:

    #include int main() {

    short entero_corto;int entero;long entero_largo;

    printf("El tamao del tipo short es: %d \n", sizeof(short));printf("El tamao del tipo int es: %d\n", sizeof(entero));printf("El tamao del tipo long es: %d\n", sizeof(long));

    }Como vimos en las primeras clases, as como C permite representar enteros, tambin es posible

    representar caracteres, nmeros reales o de punto flotante y punteros.

    Definicin Los punteros son un tipo de dato que almacena una direccin de memoria en lugar de un valor. Si

    queremos escribir cdigo compacto y eficiente en C es indispensable tener un conocimiento activo

    y profundo de los punteros, programarlos en forma correcta para evitar comportamientos

    inesperados.

    3

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Qu significa que una variable almacena la direccin de memoria de otra variable?

    Cuando declaramos una variable le indicamos al compilador el nombre y el tipo de dato. Por

    ejemplo, si declaramos

    int i;Le estamos indicando al compilador que tiene que reservar 4 bytes de memoria para almacenar el

    valor del entero. Tambin construye una tabla de referencias donde asocia el nombre de la

    variable con su direccin de memoria en donde esos 4 bytes han sido reservados. As, si luego

    escribimos

    i = 4 ;en ejecucin esperamos encontrar un 4 en el rea de memoria reservada para guardar el valor de

    i. En C, se denomina objeto a una variable como i. Pero esto no tiene que confundirnos con el

    paradigma de programacin orientada a objetos. Se usa esta palabra para referirnos a un conjunto

    que consta de smbolo(nombre), valor y direccin [Jensen, 2000].

    Tenemos entonces 2 valores asociados al entero i, el valor que almacena (el 4) y el lugar de la

    memoria donde se ha guardado, es decir, la direccin de i. Algunos textos se refieren a estos dos

    valores como rvalue (are value que sera el valor de la derecha de la asignacin) y lvalue (el

    value que sera el valor a la izquierda) . El lvalue es el valor que se permite a la izquierda de la

    asignacin, esto es, la direccin donde se alojar el resultado de la evaluacin de la expresin.

    Segn K&R[K&R, 1989] el lvalue es una expresin que hace referencia al objeto, siendo el objeto

    una regin de almacenamiento. El rvalue es el valor que se encuentra a la derecha del valor de la

    asignacin, el 4 de arriba. Los rvalues no pueden ser usados a la izquierda de una asignacin, es

    decir, no puedo escribir 4 = i ;

    Ahora, si consideramos las siguientes asignaciones,

    int j, k;k = 2;

    4

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    j = 7;

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Operadores & y *En C, cuando definimos una variable de tipo puntero usamos un * antes del nombre y tambin le

    damos un tipo. Este tipo indica el tipo de dato que se encuentra guardado en la direccin que

    alojaremos en nuestro puntero. Por ejemplo, consideremos la siguiente definicin de una variable:

    int *ptr;

    ptr es el nombre de nuestro variable (de la misma manera que i era el nombre de nuestra variable

    de tipo entero).

    El * indica al compilador que estamos declarando una variable puntero para que se reserven los

    bytes necesarios para alojar una direccin de memoria. Int significa que queremos usar nuestra

    variable para almacenar la direccin de un entero. Se dice entonces que el puntero apunta a un

    entero.

    Recordemos que ANSI no inicializa las variables que estn dentro de una funcin. Esto es, a la

    variable i no la inicializ en 0 en forma implcita al declararla. En forma similar se comporta con

    los punteros, segn la declaracin hecha arriba, ptr no tiene ningn valor asignado es decir no se

    le asigna ningn valor. Se dice entonces que ptr es un puntero no inicializado (apunta a una

    direccin aleatoria). Para inicializar un puntero sin hacerlo apuntar a una direccin vlida se

    defini la macro NULL, su valor puede variar de acuerdo al sistema operativo donde se est

    desarrollando el cdigo. Al definir esta macro, C se asegura que sea interpretado de la misma

    manera en distintos compiladores y sistemas operativos, contribuyendo a la portabilidad del

    cdigo. Entonces, es vlido y recomendable escribir

    ptr = NULL;para inicializar un puntero. Luego, de la misma manera que preguntamos si una variable es igual

    a cero if (i ==0) , preguntamos por if (ptr==NULL)

    6

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Volviendo a nuestra variable ptr. Supongamos ahora que queremos almacenar en ptr la direccin

    de nuestra variable entera i. En la figura 2 se puede observar el esquema de la memoria que

    quedara.

    Figura 2 Punteros, variables y almacenamiento en memoria

    Para hacerlo se usa el operador unitario & y escribimos:

    ptr = &i;Lo que hace el operador & es tomar la direccin de i , aun cuando i est a la derecha del operador

    de asignacin y copia esa direccin en el contenido de nuestro puntero ptr. Ahora ptr es un

    puntero a i.

    Por su parte, el operador * es llamado de desreferenciacin o indireccin, y se usa de la siguiente

    manera:

    *ptr = 8;Qu hace sta asignacin? Copia en la posicin de memoria apuntada por ptr el valor 8.

    Es decir, cuando usamos el operador * estamos haciendo referencia al valor apuntado por ptr y no

    al valor del ptr en s. En la figura

    7

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Figura 3 Operadores de punteros

    De la misma manera podramos escribir:

    printf(%d\n, *ptr_edad);Para imprimir en la salida estndard el valor entero que se encuentra en la posicin de memoria

    apuntada por ptr.

    Analicemos ahora el siguiente cdigo, tomado del Tutorial de C:

    Ejemplo 1

    #include

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Pasaje por ReferenciaRecordemos que en la clase 1 estudiamos que en C los bloques de cdigo se estructuran en

    funciones, que no se pueden anidar, y sus parmetros son siempre por valor. Los punteros

    permiten la implementacin de los parmetros por referencia. Los argumentos de tipo puntero

    permiten a una funcin tener acceso y cambiar objetos que estn en la funcin que llamo.

    Figura 4 Pasaje por Referencia

    9

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Sobre los Arreglos y su implementacin

    As como tenemos tipos de datos simples, como los punteros, los enteros, los caracteres, entre otros;

    tambin tenemos datos complejos, que se construyen a partir de los datos simples. Es el caso de los

    arreglos.

    Antes de comenzar, pensemos en por qu es necesario identificar el tipo de variable a la que

    apunta un puntero como

    int *ptr;Esto nos va a permitir hacer la siguiente asignacin

    *ptr = 9;

    El compilador sabe que tiene que copiar 4 bytes a la posicin de memoria que apunta ptr porque

    ptr fue declarado como un puntero a entero. De modo similar ocurrira con nmeros de punto

    flotante o dobles, se copiara el nmero correcto de bytes para poder almacenar el valor

    correspondiente. Pero adems de esto, el compilador podra interpretar el cdigo para poder

    moverse a travs de las distintas posiciones de memoria simplemente incrementando y

    decrementando este puntero. Cmo es esto?

    Supongamos un bloque de memoria de 2 posiciones de nmeros enteros en una fila, es decir 8

    bytes de memoria son reservados para alocar 2 enteros. En el siguiente ejemplo se puede observar

    un arreglo de 2 enteros, que ocupan cada uno 4 bytes

    10

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Figura 5 Arreglos y almacenamiento en memoria

    Ahora apuntamos nuestro ptr al primer elemento de este bloque, el primer entero. Adems, si

    suponemos que est en la posicin de memoria 405332 (decimal), cuando escribimos

    ptr + 1;lo que hace el compilador es apuntar al siguiente entero, que est en la posicin de memoria

    405336 Y esto por qu ocurre? Ya que el compilador sabe que ptr es un puntero (que su valor es

    una posicin de memoria) y que apunta a un entero (si direccin actual, 405332, apunta a un

    entero), aade 4 a ptr en lugar de 1, as apunta a al siguiente entero en la posicin de memoria

    405336. En forma similar, si fuera declarado por double sumara 8 en lugar de 4.

    Es decir, la suma del puntero en realidad es incrementar a la direccin la cantidad de bytes dada

    por el sizeof del tipo de dato que apunta. No es una adicin normal, en C se define como

    aritmtica de punteros y se ahondar en clases subsiguientes.

    Y dado que un bloque de 2 enteros almacenados en forma contigua en la memoria es, por

    definicin, un arreglo de enteros, esto nos permite vislumbrar una relacin intrnseca entre

    arreglos y punteros.

    DefinicinPor definicin, un arreglo es una lista de ms de una variable que tienen el mismo nombre y se

    11

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    alocan en forma consecutiva en la memoria. Cada variable se diferencia entre s por un ndice. La

    declaracin en C es la siguiente:

    tipo nombre-arreglo[dimension];

    Por ejemplo,

    int x[12]; char cadena[5];

    Esto es, cuando el compilador encuentra una declaracin de un arreglo reserva una cantidad de

    posiciones de memoria contigua, dada por dimensin. C protege estas direcciones y son accedidas

    a travs de un ndice. Este ndice va de 0 a dimensin-1 y C no controla que el ndice est dentro

    del rango. Adems, recordar que las cadenas terminan con el carcter \0 (el valor nulo).

    Entonces, consideremos lo siguiente, tomado del Tutorial de C:

    int mi_arreglo[] = {1,23,17,4,-5,100};

    Tenemos entonces un arreglo conteniendo 6 enteros. Nos referimos a cada uno de ellos a travs

    del ndice utilizando la notacin tradicional.

    mi_arreglo[0] hasta mi_arreglo[5]

    Arreglos y Punteros

    En la notacin a travs de arreglos e ndices, podemos escribir el siguiente cdigo:

    void pruebaConArreglos(){int dim=10; i=0; mi_arreglo[dim];for(;i

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    int *ptr;ptr = &mi_arreglo[0]; /*ptr apuntar al primer elemento del arreglo*/

    Entonces, podemos imprimir los elementos de nuestro arreglo usando la notacin de arreglos o

    desreferenciando a nuestro puntero. Consideremos ahora el siguiente ejemplo, tomado del

    Tutorial de C

    Ejemplo 2#include int mi_arreglo[] = {1,23,17,4,-5,100};int *ptr;int main(void){int i;ptr = &mi_arreglo[0]; /* apuntamos nuestro puntero al 1 elemento del arreglo*/printf("\n\n");for (i = 0; i < 6; i++){

    printf("mi_arreglo[%d] = %d ", i, mi_arreglo[i]); /*

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    ptr = mi_arreglo;

    Esto es el nombre de un arreglo es la direccin del primer elemento que contiene el arreglo. En muchos textos encontramos que el nombre del arreglo es un puntero y en realidad no es tan as porque puedo hacer ptr = mi_arreglo; pero NO mi_arreglo = ptr;Mientras ptr es una variable, mi_arreglo es una constante, una vez que la direccin donde se almancena el primer elemento del arreglo no puede ser cambiada una vez que mi_arreglo[] fue declarado.

    Y aqu va una explicacin textual del tutorial de C de Jensen que me pareci muy adecuada y clara.

    Al discutir el trmino lvalue se estableci que Un objeto es una regin de almacenamiento; Un lvalue es una expresin que hace referencia a un objeto.Esto plantea un problema interesante. Ya que mi_arreglo es una regin nominada de almacenamiento, por qu no es el lvalue en la asignacin que se hace arriba?. Para resolver este problema, algunos se refieren a cosas como mi_arreglo como un lvalue no modificable.

    Modifiquemos el programa de ejemplo cambiando la lnea siguiente y verificando que los resultados son idnticos.:

    ptr = &mi_arreglo [0]; por ptr = mi_arreglo;

    Algunos escritores se refieren al nombre de un arreglo como un puntero constante. Qu queremos decir con esto? Bueno, para entender el trmino constante en este contexto, volvamos a nuestra definicin del trmino variable. Cuando declaramos una variable reservamos un lugar de la memoria para almacenar el valor del tipo apropiado. Una vez hecho esto, el nombre de la variable puede ser interpretado en una de dos maneras. Cuando es usada en el lado izquierdo del operador de asignacin, el compilador la interpreta como la direccin de memoria en la cual colocar el resultado de la evaluacin de lo que se encuentra al lado derecho del operador de asignacin. Pero cuando se usa del lado derecho del operador de asignacin, el nombre de una variable es interpretada de modo que representa el contenido de la direccin de memoria reservada para contener el valor de dicha variable.Con esto en mente analicemos la ms simple de las constantes, como en:

    int i, k;

    14

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    i = 2;

    Aqu, mientras i es una variable y ocupa un espacio en la seccin de datos de la memoria, 2 es una constante y como tal, en lugar de ocupar memoria en el segmento de datos, es embebida directamente en la seccin de cdigo de la memoria. Esto quiere decir que cuando escribimos algo como k = i; le decimos al compilador que cree cdigo que en el momento de ejecucin observar en la direccin &i para determinar el valor a ser movido a k, mientras que el cdigo creado al hacer algo como i = 2, simplemente pone el 2 en el cdigo (no se hace referencia al segmento de datos). Esto es porque ambos, k e i son objetos, pero 2 no es un objeto.

    De modo similar a lo que ocurre arriba, ya que mi_arreglo es una constante, una vez que el compiladorestablece donde ser almacenado el arreglo en la memoria, ya sabe la direccin de mi_arreglo[0] y cuando encuentra:

    ptr = mi_arreglo;

    Simplemente usa esta direccin como una constante en el segmento de cdigo y no hace ms que eso (no se hace una referencia al segmento de cdigo).

    Es decir, en C los nombre de un arreglo es la direccin de memoria del primer elemento del arreglo. Y C lo toma como una constante. Esto es, una vez declarado se le asigna un posicin de memoria y este valor ser alocado en el segmento de cdigo en la memoria, y no en el segmento de datos. De esta manera, son vlidas las siguientes expresiones:

    ptr=&mi_arreglo[i] es equivalente a ptr=mi_arreglo;

    En general, mi_arreglo[i] es equivalente a *(mi_arreglo+i)&mi_arreglo[i] es equivalente a mi_arreglo+i;

    Adems, puede moverme en el arreglo a travs de mi_arreglo o de la variable ptr

    *(ptr +i) es equivalente a ptr[i]

    Cualquier expresin escrita con un arreglo y un ndice es equivalente a una expresin escrita como un puntero y un desplazamiento.

    15

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Punteros Genricos

    La forma en que gestiona C los punteros, discriminando punteros segn el tipo del valor apuntado, hace factible la posibilidad de errores de conversin. Por ejemplo que un puntero a entero lo apunte en ejecucin a un double o a un entero corto, o compare dos punteros de distinto tipo. Para reducir estas posibilidades C define un puntero genrico o void * (carente de tipo) . En ejemplos anteriores hemos visto su uso. Tambin se pueden escribir cosas como

    void *vptr;

    C permite la compracin de un puntero de cualquier tipo con el tipo genrico. De esta manera se pueden realizar casteos o conversiones para poder hacer compatibles con la especificacin de conversin. Un ejemplo es en el Programa 1.1 las lneas 10, 11 y 12 donde se castea el puntero a void para imprimirlo con la especificacin %p.

    Arreglos de Caracteres

    DefinicinEn C, las cadenas son arreglos de caracteres terminados en \0 y no se constituyen como un tipo de

    datos especfico.

    Consideremos por ejemplo el siguiente codigo:char mi_cadena[20];mi_cadena[0] = 'T';mi_cadena[1] = 'i';mi_cadena[0] = 'o';mi_cadena[0] = '\0';

    En general, las cadenas no se contruyen de esta manera, pero nos permite ilustrar que las cadenas en C son arreglos de caracteres terminadas en \0. En el ejemplo, el compilador asigno un bloque continuo de memoria de 20 bytes para almacenar caracteres y los inicializa con Tio\0

    Es importante no confundir con la palabra clave NULL \0 es un carcter binario que ocupa 1 byte. El NULL es el nombre de una macro definida para inicializar punteros nulos y su definicin est

    16

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    en un archivo de cabeceras del compilador de C.

    A continuacin, 3 formas de escribir lo mismo para definir una cadena:char mi_cadena [20] = {'T', 'i', 'o', '\0',};

    char mi_cadena [20] = "Tio";

    char mi_cadena[] = "Tio";

    Cuando usamos el \0 se agrega al final de la cadena en forma automtica. En el ltimo ejemplo el compilador cuenta el carcter nulo y guardar 4 bytes en la memoria para almacenar la cadena, la direccin de la cual est dada por el nombre del arreglo mi_nombre Es natural encontrar el siguiente cdigo en reemplazo del anterior:

    char *mi_cadena = "Tio";

    La diferencia entre ellas es que usando la notacin del arreglo, en memoria esttica se reservan 4 bytes. En la notacin por puntero, adems de los 4 bytes se necesitan N bytes para almacenar el puntero (mnimo 2 bytes pero puede ser 4 o ms) . Ya que la direccin se almacena

    Adems, en la notacin de arreglo, mi_cadena es la forma corta de &mi_cadena[0] la cual es la direccin del primer elemento del arreglo. Ya que la direccin ser fijada en ejecucin es una constante, no una variable. Por otra parte, en la notacin por punteros mi_cadena es una variable. Decidir el mtodo a utilizar y cual es el mejor depende de lo que se pretende realizar con el programa.

    Cadenas de caracteres con arreglos y punterosAhora, qu pasara si cada una de estas declaraciones fueran hechas dentro de una funcin?

    void funcion_A(char *ptr){

    char mi_arreglo[] = "ABCDE";

    }void funcion_B(char *ptr){

    char *mi_puntero = "FGHIJ";

    }

    17

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    En funcion_A, el arreglo est definido como una variable local y su valor ABCDE son los datos. Al ser una variable local, el valor se guarda en la pila (stack) De la misma manera se guarda la cadena FGHIJ que es el valor apuntado por mi_puntero. En este caso en el segmento de datos.

    Consideremos ahora el siguiente programa:

    Ejemplo 3

    #include char strA[80] = "Cadena a usar para el programa de ejemplo";char strB[80];int main(void){char *pA; /* 1. un apuntador al tipo caracter */char *pB; /* 2. otro apuntador al tipo caracter */puts(strA); /* 3. muestra la cadena strA */pA = strA; /* 4. apunta pA a la cadena strA */puts(pA); /* 5. muestra a donde apunta pA */pB = strB; /* 6. apunta pB a la cadena strB */putchar('\n'); /* 7. dejamos una lnea en blanco */while(*pA != '\0') /* 8. linea A (ver texto) */{*pB++ = *pA++; /* 9. linea B (ver texto) */}*pB = '\0'; /* 10. linea C (ver texto) */puts(strB); /* 11. muestra strB en la pantalla */return 0;}

    En primer lugar declaramos 2 strings de 80 posiciones cada una. Ambas se inicializan con \0 por ser declaradas como globales al programa y luego strA se inicializa con 42 caracteres de la cadena que est escrita entre . Es decir, C aloco 2 espacios de memoria distintos con espacio suficiente para los elementos de cada una de las cadenas.Tambin declaramos 2 punteros a caracteres, pA y pB, y mostramos la cadena en la salida estndard. Despues apuntamos el puntero pA a la cadena strA y de la misma manera a pB le asignamos la cadena strB. Esto quiere decir que, por el significado de la operacin de asignacin, copiamos en pA la direccin de memoria del primer elemento de strA, que sera strA[0] Luego, a la funcin puts le enviamos como parmetro pA, que sera strA[0], es decir la direccin del primer elemento. Luego esta funcin se ocupa de imprimir la cadena completa hasta llegar al \0. El prototipo de esta funcin es

    int puts(const char *s);

    puts recibe un puntero , es decir, la direccin de memoria a la que apunta. Y recibe un valor, ya

    18

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    que en C todos los parametros son por valor. Las lneas 3. y 5. arrojan el mismo resultado.

    La lnea A es un bucle que se repetir mientras pA no llegue al final de la cadena, es decir *pA !='\0' La lnea B evalua la expresin de la derecha copiando el contenido apuntado por pA en el contenido apuntado por pB y luego se incrementa el valor de pA pasando a procesar al prximo carcter. En forma similar, pB copia el contenido del valor apuntado por pA e incrementa su posicin para pasar a copiar el siguiente carcter.

    Como el bucle termina de la lnea A termina al llegar al carcter nulo, en la lnea C se copia el \0 en strB o pB para finalizar la cadena.

    Otra alternativa al strcpy() estndar podra ser:

    char *mi_strcpy(char *destino, char *fuente) {char *p = destino;while (*fuente != '\0') {

    *p++ = *fuente++;}*p = '\0';return destino;}

    Podemos observar que la funcin es la misma, pero retorna un puntero al destino. Recordemos que en C las funciones reciben parmetros por valor o copia. Los argumentos fuente y destino se modifican dentro de la funcin pero al finalizar estas direcciones no se modificaron. Slo cambia destino porque es el valor de retorno.Es importante destacar que en C los arreglos, al ser constantes, no se pueden copiar o asignar a travs de los operadores tradicionales. Deben utilizarse funciones especiales como strcopy, strcmp, entre otras. Estas funciones se describen en la librera string.h

    19

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Aritmtica de Punteros

    Las variables de tipo puntero soportan un conjunto de operaciones acotado y conciso. Luego de leer los captulos prescedentes, las siguientes afirmaciones resultan naturales y quizs obvias.

    Si pA y pB apuntos a elementos del mismo arreglo, entonces es correcto utilizar =, >,

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Arreglo de Punteros

    Puesto que en s mismos los punteros son variables, pueden almacenarse en arreglos como cualquier otra variable. Si declaramos

    char *lineas[5]; //Arreglo de 5 punteros a char

    Luego,

    lineas[i] es un puntero a char y,*lineas[i] es el primer carcter de la lnea i-simaEl caso tpico es un arreglo de cadenas de caracteres donde cada elemento del arreglo es un string. Y cada cadena puede tener una longitud diferente a sus vecinas.De esta forma se aloca slo la cantidad de bytes que se necesitan, por esto se denominan matrices de borde irregular. En la siguiente imagen se presenta esquemticamente la situacin planteada.

    Cada palabra est situada una a continuacin de la otra, separada por el carcter nulo. Asimismo, palabras apunta al elemento 0 del arreglo, es una constante.

    Veamos el siguiente cdigo de ejemplo, tomado del editor de textos gedit (notar las diferencias de color que utiliza este editor para separar estructuras de control, variables, asginaciones, entre otros).

    21

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Dentro del bucle for, el programa imprime el string del da completo, mientras que en el segundo printf se imprime la primera letra del nombre del da, por qu sucede esto?

    En el siguiente ejemplo, analicemos el uso de arreglos de cadenas implementados como arreglo de punteros. Escribir un programa que ingrese un conjunto de palabras y las imprima en orden.La tarea a realizar tiene 3 etapas bien claras:

    (1) ingresar todas las palabras

    (2) ordenarlas

    (2) imprimirlas en orden

    El programa sera entonces:#include int leoPalabras(char *lineas[]);void imprimo(char *lineas[], int n);void ordenoPalabras(char *lineas[], int n);

    int main(int arv, char argc[]){char *lineas[80];int cantPal;if ( (cantPal=leoPalabras(lineas))>0){

    ordenoPalabras(lineas, cantPal);imprimo(lineas, cantPal);}

    elseprintf ("No se ingresaron palabras...");

    22

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    }

    int leoPalabras(char *lineas[]){char palabra[20], *p;int cantPal=0;leoPalabra(palabra); //

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Gestin de la memoria de C

    Es frecuente alocar espacio de memoria en tiempo de ejecucin. Esto

    sucede cuando utilizamos punteros cuyo espacio de almacenamiento no se conoce a priori,

    permite posponer la decisin del tamao del bloque de memoria necesario para guardar por

    ejemplo, un arreglo de enteros o de estructuras. Una vez que ya no lo necesito, libero esta

    memoria para otros usos.

    Las variables globales se les asigna memoria en tiempo de compilacin. A las locales se usa la pila

    o stack. Para los punteros la memoria a reservar pertenece al espacio de la heap, es decir, a la

    bolsa de direcciones disponibles que no est usando el programa ni el sistema operativo u otro

    programa que se est utilizando.

    El subsistema de asingacin dinmica de C se utiliza para construir estrcturas muy importantes

    tales como listas enlazadas, arboles binarios y arreglos asignados dinmicamente.

    Para la gestin de la memoria se utilizan las funciones de la librera El ncleo son las

    funciones malloc() y free() Estas funciones trabajan juntas usando la regin de memoria libre para

    establecer y gestionar una lista de memoria disponible. Malloc toma memoria y Free la libera.

    MallocLa funcin malloc hace una peticin de memoria y se asigna una parte de la memoria disponible

    [Schildt, 2002]. El prototipo es void *malloc(size_t numero_de_bytes);

    El numero_de_bytes es el nmero de bytes de memoria que se quieren asignar. El tipo size_t est

    definido en stdlib.h como un entero sin signo. Retorna un puntero de tipo void, lo que significa

    que se puede asignar a cualquier tipo de puntero. El siguiente fragmento de cdigo asigna 1000

    24

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    bytes de memoria:

    char *p;p = malloc(1000);

    Luego de la asignacin, p apunta al primero de los 1000 bytes de memoria libre. Observar que no

    se ha utilizado ningn molde para asignar el valor devuelto por malloc() a p. Como se devuelve

    un puntero a void() se convierte automticamente al tipo del puntero que haya a la izquierda de la

    asignacin. El siguiente ejemplo aloca 50 enteros, usando sizeof para asegurar la portabilidad

    int *p;p = malloc(50*sizeof(int));

    Es importante verificar el valor devuelto por la funcin malloc(), dado que la memoria no es

    finita.

    int *p;p = malloc(50*sizeof(int));if (!p) printf(No hay ms memoria!) ;

    FreeLa funcin free() es la complementaria de malloc() ya que devuelve al sistema la memoria

    previamente asignada. Una vez que la memoria fue liberada puede ser reutilizada con malloc(). El

    prototipo de la funcin es void * free( *p);

    CallocEs una funcin similar al malloc() , con el mismo prototipo, pero es espacio reservado lo inicializa con 0.

    A continuacin, una tabla de resumen de las funciones presentadas.

    Tabla de Resumen

    25

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Funcin Objetivo Prototipo Ejemplomalloc Regresa un puntero a

    un espacio reservado para un objeto. NULL en caso contrario.

    void malloc(n, size_t obj);

    p=(char *) malloc(10*sizeof(char));

    free Desasigna el espacio apuntado por p. Si p es nulo tiene que ser un espacio alocado por calloc o malloc.

    void free(void *p); free(p);

    calloc Devuelve un puntero al espacio para un arreblo de n objetos cada uno con un tamao size El espacio se inicializa con 0

    void calloc(n, size_t obj);

    q= (int *) calloc(10, sizeof(int));

    Para finalizar, un ejemplo que ilustra los conceptos abordados en esta clase.Dado un arreglo de enteros, escribir una funcin que retorne todos los elementos menores que el promedio.

    Solucin 1: retornar el arreglo con los menores.

    float calculoProm(int *, int);

    int * menoresQue(int vec[], int dim){int *nuevo;float prom=calculoProm(vec, dim);nuevo=(int *) malloc(dim*sizeof(int));for(i=0, j=0;i

  • Facultad de Informtica. UNLP.Seminario de Lenguajes C - Unidad 5

    Adems, nuevo est declarada como una variable local. Si declaro int nuevo[20]; estara incurriendo en un grave problema porque cuando termina la funcin ese espacio de memoria se libera porque es una variable local. Por otro parte, si no realizo el malloc el vector queda interno con valores no vlidos.

    Solucin 2: pasar el arreglo por referencia.

    int menoresQue(int *vec, int dim){

    int *nuevo, j;float prom=calculoProm(vec, dim);nuevo=(int *) malloc(dim*sizeof(int));for(i=0, j=0;i