Lenguaje C [Apuntadores y arreglos] M. en C. Sergio Luis Pérez Pérez UAM C UAJIMALPA , M ÉXICO, D. F. Trimestre 14-O Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 1 / 20 Apuntadores y direcciones Apuntadores y direcciones 1 Apuntadores y direcciones 2 Apuntadores y arreglos 3 Apuntadores a caracteres 4 Arreglos de apuntadores y multidimensionales Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 2 / 20 Apuntadores y direcciones Apuntadores y direcciones I Un apuntador es una variable que contiene la dirección de una variable. El operador unario ∗ es el operador de indirección o desreferencia, de manera que cuando se aplica un apuntador se tiene acceso al objeto que señala. El operador & sólo se puede aplicar a objetos que están en memoria, es decir variables y elementos. El operador & no puede aplicarse a expresiones, constantes o variables tipo registro. Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 3 / 20 Apuntadores y direcciones Apuntadores y direcciones II La declaración de un apuntador como: int ∗ ip; funciona como mnemónico y dice que la expresión *ip es un int. Ejemplo 1 int x = 1, y = 2, z[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int *ip, *iq, i; ip = &x;/*ip apunta a x*/ y = *ip;/*y ahora vale 1*/ *ip = 0;/*x ahora vale 0*/ ip = &z[0];/*ip apunta a 0 de z[0]*/ printf(“ %d %d %d %d %d\n”, x, y, *ip, z[0], ip); Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 4 / 20 Apuntadores y direcciones Apuntadores y direcciones III Ejemplo 1 continuación *ip = *ip + 10; iq = ip;/*iq apunta a lo mismo que ip*/ for(i = 0; i < 10; i++, ip++) printf(“ %d\n”, *ip); ip = iq;/*ip apunta a lo que apuntaba originalmente*/ Debido a que C pasa los argumentos por valor si se desea hacer una especie de paso por referencia es necesario pasar las variables como &variable pues debido a que & produce la dirección de una variable entonces &variable es un apuntador. intercambia(&a, &b) Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 5 / 20 Apuntadores y direcciones Apuntadores y direcciones IV Intercambia void intercambia(int *a, int *b){ int temporal = *a; *a = *b; *b = temporal; return; } Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 6 / 20 Apuntadores y arreglos Apuntadores y arreglos 1 Apuntadores y direcciones 2 Apuntadores y arreglos 3 Apuntadores a caracteres 4 Arreglos de apuntadores y multidimensionales Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 7 / 20 Apuntadores y arreglos Apuntadores y arreglos I La relación entre apuntadores y arreglos en el caso de lenguaje de C es muy estrecha. Cualquier operación que pueda lograrse por indexación de un arreglo también puede lograrse con apuntadores. La versión con apuntadores tenderá a ser más rápida pero difı́cil de entender. Ejemplo 2 int v[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, *pv; pv = &v[0]; for(i = 0; i < 10; i++) printf(“ %d %d\n”, *(pv+i), v[i]); Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 8 / 20 Apuntadores y arreglos Apuntadores y arreglos II Lo que ocurre es que en C al evaluarse v [i] se convierte inmediatamente a *(v+i). Sin embargo existe una diferencia entre el nombre de un arreglo y el de un apuntador. Las instrucciones pv = v y pv++ es válida para el ejemplo mencionado. Las instrucciones v = pv y v++ son ilegales. Los parámetros formales: char s[], char *s; en la definición de una función son equivalentes pero el último indica más explı́citamente que el parámetro es un apuntador. Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 9 / 20 Apuntadores y arreglos Apuntadores y arreglos III También es posible pasar a una función una posición de inicio diferente para el arreglo como: f (&a[2]) o bien f (a + 2) La declaración de parámetros puede ser: f (int a[]){. . . } o bien f (int ∗ a){. . . } A la función f no le interesa que a sea en realidad un arreglo más grande. Esto permite que se puedan hacer referencias hacia atrás sobre un arreglo siempre que las localidades sean válidas. a[−1], a[−2] Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 10 / 20 Apuntadores y arreglos Apuntadores y arreglos IV Un apuntador p es incomparable a un int salvo en el caso del cero que comúnmente se suele utilizar como la macro NULL definida en <stdio.h>. La instrucción p + + permite apuntar al elemento de adelante mientras que p+ = i al que se encuentra i posiciones adelante si i > 0. El número de posiciones que se avanza es siempre escalado al tamaño de a lo que apunta p, por ejemplo 1 para char o 4 si se trata de int de 4. Dos apuntadores pueden ser restados, por ejemplo con la finalidad de conocer el tamaño de un arreglo. Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 11 / 20 Apuntadores y arreglos Apuntadores y arreglos V Ejemplo: regresar la longitud de una cadena int strlen(char *s){ char *p = s; while(*p != ‘\0’) p++; return p - s; } No es legal sumar dos apuntadores, multiplicarlos o dividirlos, enmascararlos o agregarles un float o un double. Tampoco se pueden asignar dos apuntadores de diferente tipo a menos que haya una conversión forzosa de tipo. Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 12 / 20 Apuntadores a caracteres Apuntadores a caracteres 1 Apuntadores y direcciones 2 Apuntadores y arreglos 3 Apuntadores a caracteres 4 Arreglos de apuntadores y multidimensionales Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 13 / 20 Apuntadores a caracteres Apuntadores a caracteres I Una constante de cadena es un arreglo de caracteres. “Soy una cadena” Internamente dicha cadena termina con ‘\0’. Las cadenas constantes pueden ser tratadas como const char, sin embargo, si tratan de ser manipuladas entonces el comportamiento no está definido. char amensaje[] = “hola mundo”; char *pmensaje = “hola mundo”;/*instrucción obsoleta*/ const char *cpmensaje = “hola mundo”; amensaje es un arreglo y dado que tiene memoria asignada puede ser modificado. Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 14 / 20 Apuntadores a caracteres Apuntadores a caracteres II pmensaje es una instrucción obsoleta y tratar de modificar la cadena da un comportamiento indefinido. Sin embargo, se le puede apuntar a otro lado. cpmensaje es una constante de cadena y no puede ser modificada. Si se desea copiar una cadena t en otra s debe hacerse caracter por caracter. void copia(char *s, char *t){ int i = 0; while((s[i] = t[i]) ! = ‘\0’) i++; return; } Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 15 / 20 Apuntadores a caracteres Apuntadores a caracteres III La misma función se puede hacer simplemente manipulando los apuntadores y a lo que apuntan. void copia(char *s, char *t){ while((*s = *t) ! = ‘\0’) s++, t++; return; } Ejercicio: Intente reducir el tamaño de copia para lograr el mismo propósito. Los operadores ++ y – pueden ser utilizados en combinación con el operador unario ∗. *p++;/*devuelve a lo que apunta p y luego incrementa p*/ *–p;/*decrementa p y luego devuelve a lo que apunta*/ Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 16 / 20 Arreglos de apuntadores y multidimensionales Arreglos de apuntadores y multidimensionales 1 Apuntadores y direcciones 2 Apuntadores y arreglos 3 Apuntadores a caracteres 4 Arreglos de apuntadores y multidimensionales Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 17 / 20 Arreglos de apuntadores y multidimensionales Arreglos de apuntadores y multidimensionales I Si se desea tener un conjunto de palabras o lı́neas de texto almacenadas en C entonces se puede utilizar un apuntador a apuntadores. char **lineas;/*es un apuntador a apuntadores*/ char *lineas[1000];/*son 1000 apuntadores a caracteres*/ En el caso de **lineas primero tendrı́a que apartarse memoria para el número de apuntadores deseados y posteriormente para cada apuntador. La función void *malloc(size t size) permite apartar memoria para un apuntador por lo que la siguiente instrucción da el espacio para los 1000 apuntadores y luego para cada apuntador. Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 18 / 20 Arreglos de apuntadores y multidimensionales Arreglos de apuntadores y multidimensionales II lineas = (char**)malloc(1000*sizeof(char*)); for(i = 0; i < 1000; i++) lineas[i] = (char*)malloc(MAXTAM*sizeof(char)); Como lineas es el nombre de un arreglo entonces puede tratarse como apuntador. Lo mismo en el caso donde es un apuntador a apuntador. int escribir lineas(char *lineas[], int nlineas){ while(nlineas– > 0) printf(“ %s”, *lineas++); return; } Una forma de inicializar los arreglos de apuntadores a cadenas es la siguiente: Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 19 / 20 Arreglos de apuntadores y multidimensionales Arreglos de apuntadores y multidimensionales III const char *mes(int n){ static const char *nombre[] = {“mes invalido”, “enero”, . . . }; return n < 1||n > 12 ? nombre[0] : nombre[n]; } Los apuntadores a otros tipos de datos deben inicializarse con malloc, por ejemplo: int *v = {0, 1, 2, 3}; es una instrucción invalida. Tendrı́a que ser: v = (int*)malloc(4*sizeof(int)); v[0] = 1, v[1] = 2, v[2] = 3, v[3] = 4; Sergio Luis Pérez (UAM C UAJIMALPA) Curso de Lenguaje C 20 / 20