Unidad 9 PUNTEROS EN C++ Objetivos Entender el concepto de puntero. Saber definir el tipo de datos puntero. Saber crear y acceder variables puntero y destruirlas. crear dinámicamente y acceder variables referenciadas por puntero, y destruirlas. usar punteros para la creación de arrays dinámicos. Usar punteros como alternativa al paso de parámetros por referencia. Usar punteros a funciones como parámetros de otras funciones. Saber utilizar punteros para mejorar la eficiencia de un programa 2 Contenidos 1. INTRODUCCIÓN 2. ¿QUÉ ES UN PUNTERO? DECLARACIÓN E INICIALIZACIÓN 2.1. Punteros a objetos 3. OPERADORES PUNTEROS 4. PASO POR DIRECCIÓN 5. EXPRESIONES CON PUNTEROS 5.1. Asignaciones de punteros 5.2. Aritmética de punteros 5.3. Comparación de punteros 6. PUNTEROS Y ARRAYS 6.1. Indexar un puntero 6.2. Punteros a arrays de caracteres 6.3. Arrays de punteros 7. GESTIÓN DINÁMICA DE MEMORIA 8. ARRAYS Y ASIGNACIÓN DINÁMICA 9. PUNTEROS Y ARRAYS BIDIMENSIONALES 9.1. Representación a partir de un puntero a arrays de una dimensión 9.2. Representación a partir de un array de punteros a arrays de una dimensión 10. PUNTEROS A PUNTEROS 11. PUNTEROS A FUNCIONES 11.1. Formato de declaración 11.2. Asignación del nombre de función a un puntero a función 11.3. Llamada a una función a través de su puntero 11.4. Paso de funciones a otras funciones 12. LA ORDENACIÓN RÁPIDA EN C++: qsort 13. UNA NOTA SOBRE DECLARACIONES DE OBJETOS EN C/C++ 14. ARGUMENTOS DE main 3 Concepto de puntero Un puntero es un tipo de variable que contiene la dirección en memoria de otra variable, que se conoce como objeto al que apunta. No hay que confundir una dirección de memoria con el contenido de esa dirección de memoria. int x=25; Dirección mm ... ... 1502 25 1504 1506 1508 ... ... ... ... La dirección de la variable x (&x) es 1502 El contenido de la variable x es 25 4 Tipos de punteros A objetos. - Cuando el puntero tiene como valor la localización de otro objeto. A funciones. - Cuando el puntero tiene como valor la localización de llamada a una función. Importancia del uso de punteros en C: Simula el paso por referencia (Paso por dirección). Mejoran la eficiencia de algunos algoritmos (más rápidos). Se usan para soportar las funciones de asignación dinámica de memoria (malloc, free, new, delete) y las estructuras de datos dinámicas. 5 PUNTEROS A OBJETOS •Una variable puntero se declara como todas las variables. •Debe ser del mismo tipo que la variable apuntada. Su identificador va precedido de un asterisco (*): <tipo> * Nombre_Puntero; int *punt; //Es una variable puntero que apunta a variable que contiene un dato de tipo entero llamada punt. char *car; //Es un puntero a variable de tipo carácter. long float *num; float *mat[5]; // . . . Un puntero tiene su propia dirección de memoria: &punt &car 6 Operadores punteros De dirección &: Aplicado al nombre de un objeto, devuelve la dirección de memoria de su operando. De indirección *: Aplicado al nombre de un objeto puntero, devuelve el valor del objeto localizado en la dirección que le sigue. A un puntero se le puede asignar: - la dirección de una variable no puntero. P1 = &var - el valor de otro puntero. P2 = P1 - un valor nulo. P1 = NULL 7 EXPRESIONES CON PUNTEROS • Hay tantos tipos de punteros como tipos de datos. • También pueden declararse punteros a estructuras más complejas (funciones, struct, ficheros...) e incluso punteros vacíos (void ) y punteros nulos (NULL). Ejemplo1 char dato; //variable que almacenará un carácter. char *punt; //declaración de puntero a carácter. punt = &dato; //en la variable punt guardamos la dirección // de memoria de la variable dato; punt apunta // a dato. Ambas son del mismo tipo, char. 8 EXPRESIONES CON PUNTEROS Aritmética de punteros : suma, incremento, decremento y resta de enteros a punteros. Ejemplos: Hay que tener cuidado con las direcciones apuntadas: int *punt = NULL, var = 14; punt = &var; printf(“%#X, %p”, punt, &var); printf(“\n%d, %d”, *punt, var); printf(“%d, %d”, *(punt+1), var+1); //la misma salida: dirección (FFF2) //salida: 14, 14 Numcualquiera, *(punt + 1) repesenta el valor contenida//salida: en la dirección de memoria15 aumentada en una posición (int=2bytes), que será un valor no deseado. Sin embargo var+1 representa el valor 15. punt + 1 representa lo mismo que &var + 1 (avance en la dirección de memoria de var). Comparación de punteros . Los punteros pueden usarse en una expresión relacional. Ejemplo: if(punt <punt2) printf (“punt apunta a una memoria menor que punt2”); Ver sumpun.cpp 9 Ejercicio: haz la traza de #include <stdio.h> void main(void) { int a, b, c, *p1, *p2; void *p; p1 = &a; *p1 = 1; p2 = &b; *p2 = 2; p1 = p2; *p1 = 0; p2 = &c; *p2 = 3; printf("%d %d %d\n", a, b, c); // Paso 9. ¿Qué se imprime? p = &p1; //no tiene sentido, aunque el compilador lo permita p1 = p2; *p1 = 11; printf("%d %d %d\n", a, b, c); // Paso 13. ¿Qué se imprime? } 10 Ver dirmen.cpp Punteros y Arrays En C se puede indexar un puntero. int mat[ ] = {2, 16, -4, 29, 234, 12, 0, 3}; Dirección del elemento 0 Dirección del octavo elemento &mat[0] &mat[1] &mat[2] &mat[3] &mat[4] &mat[5] & mat[6] &mat[7] 2 mat 16 -4 mat+1 mat+2 Puntero a la dirección del elemento 0 29 mat+3 234 12 mat+4 mat+5 Incremento en una unidad int (dos bytes) 0 3 mat+6 mat+7 mat++ 11 Punteros y Arrays II Punteros a arrays de caracteres. Las operaciones con cadenas se realizan con punteros. La forma de definir un puntero a una cadena de caracteres: char *cadena; El identificador del array es la dirección de comienzo del array. Para saber dónde termina la cadena, el compilador añade el carácter ‘\0’ (ASCII 0, NULL): char *nombre = “PEPE PEREZ”; nombre dirección de memoria P E P E *(nombre+2) P E R E Z \0 contenido 12 Punteros y Arrays III Arrays de puntero a cadenas de caracteres. En un array de punteros a cadenas de caracteres cada elemento apunta a un carácter. La declaración será: char *cad[10]; //array de 10 filas y cada fila una dirección a una cadena de caracteres cad[0] ... cad FFB1 H O L cad+1 A ... \0 cad[4] A D I O S \0 ... ... 13 Punteros y Arrays IV (arrays de punteros Arrays de punteros. Los punteros se pueden agregar en arrays. El acceso será: Como un array de punteros a arrays de una dimensión: utilizado con las cadenas de caracteres. *array [filas]; NORMALMENTE –por facilidad de uso-: typedef char cadena[columnas]; cadena array[filas]; Ver u9_e6.cpp 14 Punteros y arrays bidimensionales I Col 0 Col 1 Col 2 Col 3 Fila 0 1.45 -23.5 -14,08 17.3 Fila 1 20 2.95 0.082 6.023 Utilizando punteros, la declaración será: float *mat [4]; //array bidimensional En donde mat es un puntero a un grupo contiguo de arrays monodimensionales (vectores) de 4 elementos cada uno. Con subíndices Con punteros Valor mat[0][0] *(*(mat+0)+0) 1.45 mat[0][1] *(*(mat+0)+1) -23.5 mat[0][2] *(*(mat+0)+2) -14.08 mat[0][3] *(*(mat+0)+3) 17.3 mat[1][0] *(*(mat+1)+0) 20 mat[1][1] *(*(mat+1)+1) 2.95 mat[1][2] *(*(mat+1)+2) 0.082 mat[1][3] *(*(mat+1)+3) 6.023 15 Punteros y arrays bidimensionales II int x[10][20]; Ver: ardepunt.cpp. pmatcon.cpp Consideramos que es un array formado por 10 arrays unidimensionales (vectores) de 20 elementos cada uno * ( * ( x + 2 ) +5) equivale a x[2][5] ya que x + 2 es un puntero a la fila 3. Por tanto. El contenido de dicho puntero, *(x+2), es la fila 3. Si me desplazo 5 posiciones en esa fila llego a la posición *(x+2)+5, cuyo contenido es *(*(x+2)+5). Las siguientes expresiones con punteros son válidas: **x x[0][0] *(*(x+1)) x[1][0] **(x+1) x[1][0] *(*x+1) x[0][1] *(*(x+1)+1) x[1][1] 16 Punteros y arrays bidimensionales III int array[filas][columnas]; int array[F][C]; array[y][z] = 129; //es equivalente a: *(*array + columnas F y + z)) = 129; //asignación Con int array[2][5], ¿qué pondría si quisiera asignar 129 al elemento de la fila 1 y columna 2? *(*array + 5x1 + 1)) = 129; es decir, desde el origen del array avanza 6 posiciones de memoria: fila 0 fila 1 129 array[1][1] *(*(array+5+1)) *(*array + 6) 17 arrays dinámicos I Pasos para usar un array dinámico: Se declara un puntero en vez de un array. tipo * Variable_puntero; Se pide la memoria adecuada para el array a partir del puntero declarado anteriormente. Variable_puntero = (tipo*) malloc (tamaño*sizeof (tipo)); Variable_puntero = new TipoOjeto[(Expresion)]; Se debe liberar la memoria una vez que hemos terminado con ella. free (Variable_puntero); delete [ ] Variable_puntero; Diferencias entre una forma y otra: new y delete estan diseñados para POO new y delete son operadores por lo tanto no necesitan prototipo. new no necesita casting. 18 arrays dinámicos II Nos aseguramos de tener memoria. if (array == NULL) { <Código de error y/o exit (0) > } Procesamos en caso afirmativo. else { <Procesamos> free (array); // liberamos memoria } Ver: proyecto con AsigDin.cpp, arrays.h 19 Gestión dinámica de memoria MALLOC void * malloc (size_t TAM); REALLOC (cambia tamaño, no añade) void * realloc (void* viejo_bloque, size_t TAM-NUEVO); FREE void free (void * punt); NEW new TipoObjeto [(Expresion)]; DELETE delete [[ ]] PunteroObjeto; 20 /* asignación-reasignación de memoria dinámica REASIGMM.CPP*/ #include <stdio.h> #include <alloc.h> #include <string.h> int main(void) { char *str; /* reservamos 9 caracteres para la cadena*/ //str = (char *) malloc(10); str = new char [10]; /* colocamos la cadena Hello*/ strcpy(str, "Hello"); printf("Cadena %s\t en la dirección %p\n", str, str); str = (char *) realloc(str, 20); /* colocamos la cadena Hello, cruel world!*/ strcpy(str, "Hello, cruel wordl!"); printf("la cadena ahora %s\t su nueva dirección %p\n", str, str); /* liberamos la memoria*/ //free(str); delete [ ]str; return 0; } 21 ARGUMENTOS DE MAIN argc: número de argumentos pasados desde la línea de comandos. al menos vale 1, el nombre del programa argv: array de punteros (de longitud argc) a los argumentos enviados. env: array de punteros a las variables de entorno. argv[0] apunta al nombre del programa argv[argc-1] apunta al último de los argumentos la ultima apunta a NULL. si se usa, deben usarse tambien argc y argv. PASO DE PARÁMETROS DESDE: LA LINEA DE COMANDOS: a continuación del nombre del programa EL IDE: en la opción arguments de Run página: 367 y apellido.cpp 22 PUNTEROS A FUNCIONES Formato de declaración float (*Ppot) (float, float); Asignación del nombre de función a un puntero a función Ppot = potencia; Llamada a una función a través de su puntero res = (*Ppot) (num1,num2); // res = potencia (num1,num2); Paso de funciones como parámetros de funciones: ver página 361 y 362 ver puntfunc.cpp y pág. 360 23 Ordenación rápida : Quick –sort Búsqueda binaria. <stdlib.h> qsort void qsort (void *base, size_t NUMELEM, size_t NUM_BYTES, int (*comp) (const void *elem1, const void *elem2)); void *base: puntero a un array de cualquier tipo size_t NUMELEM: número de elementos del array size_t NUM_BYTES: número de bytes de cada elemento del array (*comp): puntero a la función que ordena y que debemos implementar en cada caso. bsearch: búsqueda binaria en un array ordenado void *bsearch (const void *key, const void *base, size_t NUMELEM, size_t NUM_BYTES, int (*fcmp)(const void* elem1, const void* elem2)); devuelve un puntero al primer elemento que coincida con Key. Si no existe devuelve NULL. (*fcmp): puntero a la función que compara un elemento del array con la clave. ver orden.cpp y ejercicios 6 y7 24 El ignorante afirma, el sabio duda y reflexiona. --Aristóteles-- 25