punteros
Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
punteros [2012/08/03 21:54] – [Aritmética de punteros] lmateu | punteros [2021/09/22 20:07] (actual) – [Big endian vs. Little endian] lmateu | ||
---|---|---|---|
Línea 1: | Línea 1: | ||
- | ===== Punteros ===== | + | ====== Punteros |
Además del identificador de las variables globales o automáticas, | Además del identificador de las variables globales o automáticas, | ||
Línea 16: | Línea 16: | ||
Para no entrar a usar valores numéricos de la direcciones en memoria graficaremos una dirección simplemente como una flecha hacia la variable de destino. | Para no entrar a usar valores numéricos de la direcciones en memoria graficaremos una dirección simplemente como una flecha hacia la variable de destino. | ||
- | ==== Desreferencia ==== | + | ===== Desreferencia |
El operador unario de prefijo ' | El operador unario de prefijo ' | ||
Línea 44: | Línea 44: | ||
{{ : | {{ : | ||
- | ==== Declaración de punteros con valor inicial ==== | + | ===== Declaración de punteros con valor inicial |
Al igual que con cualquier variable, al declarar un puntero es posible darle un valor inicial. | Al igual que con cualquier variable, al declarar un puntero es posible darle un valor inicial. | ||
Línea 59: | Línea 59: | ||
Por otra parte, en la asignación //*p= 2//, el asterisco sí es el operador de desreferencia y por lo tanto se está cambiando //a//, no //p//. | Por otra parte, en la asignación //*p= 2//, el asterisco sí es el operador de desreferencia y por lo tanto se está cambiando //a//, no //p//. | ||
- | ==== Ejemplo: función que intercambia los valores de 2 variables ==== | + | ===== Ejemplo: función que intercambia los valores de 2 variables |
Al igual que en los métodos de Java, los parámetros de las funciones de C se pasan por valor. | Al igual que en los métodos de Java, los parámetros de las funciones de C se pasan por valor. | ||
Línea 103: | Línea 103: | ||
Escriba la función swap_double que intercambia valores de variables de tipo double. | Escriba la función swap_double que intercambia valores de variables de tipo double. | ||
- | === Función que intercambia las direcciones de 2 punteros === | + | ===== Función que intercambia las direcciones de 2 punteros |
Suponga que ahora tiene este código: | Suponga que ahora tiene este código: | ||
Línea 110: | Línea 110: | ||
int main() { | int main() { | ||
int a= 1, b= 2; | int a= 1, b= 2; | ||
- | int *px= &a, *py= &b; | + | int *pa= &a, *pb= &b; |
swap_ptr(& | swap_ptr(& | ||
} | } | ||
</ | </ | ||
- | Ahora se busca que después de invocar swap_ptr, //px// apunte a //b// y //py// apunte a // | + | Ahora se busca que después de invocar swap_ptr, //pa// apunte a //b// y //pb// apunte a // |
< | < | ||
Línea 132: | Línea 132: | ||
Por lo tanto al asignar //*p= *q// se está cambiando el valor de px en el llamador (main). | Por lo tanto al asignar //*p= *q// se está cambiando el valor de px en el llamador (main). | ||
- | === Como entender la declaración de punteros === | + | ===== Como entender la declaración de punteros |
Hay que confesar que la declaración de un puntero simple //int *p// o uno doble //int %%**%%q// no suena natural. | Hay que confesar que la declaración de un puntero simple //int *p// o uno doble //int %%**%%q// no suena natural. | ||
Línea 149: | Línea 149: | ||
* Escriba la función swap_ptr_ptr que intercambia 2 variables de tipo // | * Escriba la función swap_ptr_ptr que intercambia 2 variables de tipo // | ||
- | ==== Comparación de punteros ==== | + | ===== Comparación de punteros |
Se puede determinar si 2 punteros apuntan a la misma variable comparando ambos punteros con los operadores binarios == y !=. Vea este ejemplo: | Se puede determinar si 2 punteros apuntan a la misma variable comparando ambos punteros con los operadores binarios == y !=. Vea este ejemplo: | ||
Línea 183: | Línea 183: | ||
Aun cuando p!=r, sí se cumple que *p == *r porque en el segundo caso se comparan los valores almacenados en las variables. | Aun cuando p!=r, sí se cumple que *p == *r porque en el segundo caso se comparan los valores almacenados en las variables. | ||
- | ==== El puntero nulo ==== | + | ===== El puntero nulo ===== |
Al declarar variables automáticas sin inicialización, | Al declarar variables automáticas sin inicialización, | ||
Línea 199: | Línea 199: | ||
} | } | ||
... más código ... | ... más código ... | ||
- | if (p == NULL) { | + | if (p != NULL) { |
*p= 50; | *p= 50; | ||
} | } | ||
Línea 207: | Línea 207: | ||
No es válido acceder a la dirección NULL en un proceso y por lo tanto si se desreferencia un puntero nulo se produce el error // | No es válido acceder a la dirección NULL en un proceso y por lo tanto si se desreferencia un puntero nulo se produce el error // | ||
- | + | ===== Punteros locos ===== | |
- | ==== Punteros locos ==== | + | |
El tipo del valor retornado por la siguiente función es un puntero. | El tipo del valor retornado por la siguiente función es un puntero. | ||
Línea 231: | Línea 230: | ||
* La memoria que ocupó alguna vez x sí fue reasignada y se le dió un nuevo valor distinto de 1. Al desreferenciar p se obtiene un valor incorrecto. | * La memoria que ocupó alguna vez x sí fue reasignada y se le dió un nuevo valor distinto de 1. Al desreferenciar p se obtiene un valor incorrecto. | ||
- | ==== Variables dinámicas ==== | + | **Ejercicio** |
+ | ¿Qué despliega el siguiente programa? | ||
+ | |||
+ | < | ||
+ | #include < | ||
+ | |||
+ | int *getvar(int x) { | ||
+ | return &x; /* Incorrecto: no haga esto! */ | ||
+ | } | ||
+ | |||
+ | int main() { | ||
+ | int *p= getvar(1); | ||
+ | int *q= getvar(2); | ||
+ | printf(" | ||
+ | } | ||
+ | </ | ||
+ | ===== Variables dinámicas: malloc/free ===== | ||
Una variable dinámica se crea explícitamente llamando a la función malloc. | Una variable dinámica se crea explícitamente llamando a la función malloc. | ||
Línea 239: | Línea 254: | ||
< | < | ||
int *getvar() { | int *getvar() { | ||
- | int *q= (int*)malloc(sizeof(int)); | + | int *q= malloc(sizeof(int)); |
*q= 1; | *q= 1; | ||
return q; | return q; | ||
Línea 248: | Línea 263: | ||
printf(" | printf(" | ||
free(p); | free(p); | ||
+ | return 0; | ||
} | } | ||
</ | </ | ||
- | Al invocar malloc se debe especificar el tamaño de la memoria requerida. | + | Al invocar malloc se debe especificar el tamaño de la memoria requerida. |
Note que al retornar getvar, se destruyen todas sus variables locales (automáticas). | Note que al retornar getvar, se destruyen todas sus variables locales (automáticas). | ||
Línea 259: | Línea 275: | ||
Las variables dinámicas se crean en una zona especial de la memoria de un proceso denominada el // | Las variables dinámicas se crean en una zona especial de la memoria de un proceso denominada el // | ||
- | === No hay recolector de basuras === | + | ==== No hay recolector de basuras ==== |
En Java no es necesario liberar la memoria porque posee un recolector de basuras que recicla automáticamente los objetos que ya no son accesibles por el programa. | En Java no es necesario liberar la memoria porque posee un recolector de basuras que recicla automáticamente los objetos que ya no son accesibles por el programa. | ||
- | ==== Aritmética de punteros ==== | + | ===== Aritmética de punteros |
La característica más importante de C es su flexibilidad con el manejo de memoria, la que se ve reflejada en la // | La característica más importante de C es su flexibilidad con el manejo de memoria, la que se ve reflejada en la // | ||
Línea 268: | Línea 284: | ||
Ningún otro lenguaje de programación ampliamente usado ofrece aritmética de punteros. | Ningún otro lenguaje de programación ampliamente usado ofrece aritmética de punteros. | ||
- | Para enteder | + | Para entender |
< | < | ||
- | int *p= (int*)malloc(10*sizeof(int)); | + | int *p= malloc(10*sizeof(int)); |
</ | </ | ||
- | ¿De que sirve pedir espacio para 10 enteros consecutivos en la memoria? | + | ¿De qué sirve pedir espacio para 10 enteros consecutivos en la memoria? |
{{ : | {{ : | ||
Línea 289: | Línea 305: | ||
</ | </ | ||
- | === Azucar sintáctico === | + | ==== Azucar sintáctico |
El problema es que escribir *(p+i) es pesado sintácticamente, | El problema es que escribir *(p+i) es pesado sintácticamente, | ||
Línea 298: | Línea 314: | ||
< | < | ||
- | int *p= (int*)malloc(10*sizeof(int)); | + | int *p= malloc(10*sizeof(int)); |
int s= 0; | int s= 0; | ||
int i; | int i; | ||
Línea 312: | Línea 328: | ||
< | < | ||
- | int *p= (int*)malloc(10*sizeof(int)); | + | int *p= malloc(10*sizeof(int)); |
int s= 0; | int s= 0; | ||
int i; | int i; | ||
Línea 325: | Línea 341: | ||
< | < | ||
int *p, *r, x; /* x no es puntero */ | int *p, *r, x; /* x no es puntero */ | ||
- | p= (int*)malloc(...); | + | p= malloc(...); |
... | ... | ||
r=p; | r=p; | ||
+ | ... | ||
free(p); | free(p); | ||
+ | ... | ||
x= *r; /* r es dangling reference */ | x= *r; /* r es dangling reference */ | ||
</ | </ | ||
- | Aquí r y p apuntan a la misma dirección de memoria. | + | Aquí r y p apuntan a la misma dirección de memoria. |
- | === Ejercicio | + | ==== Restricciones ==== |
- | El siguiente programa es absolutamente legal en C. ¿Qué hace? | + | Solo está permitido sumar o restar un entero a un puntero. |
- | + | ||
- | < | + | |
- | int *p= ( (int*)malloc(10*sizeof(int)) ) + 5; /* Ojo con el + 5 al final */ | + | |
- | int i; | + | |
- | for (i= -5; i<5; i++) | + | |
- | p[i]= 0; | + | |
- | </ | + | |
- | + | ||
- | Por supuesto, acá no se está fomentando este estilo de programación, | + | |
- | + | ||
- | === Restricciones === | + | |
- | + | ||
- | Solo está permitido sumar o restar un entero a un puntero. | + | |
< | < | ||
Línea 369: | Línea 374: | ||
En el caso de la resta de punteros, se cumple esta propiedad: %%(p + i) - p ≡ i%% | En el caso de la resta de punteros, se cumple esta propiedad: %%(p + i) - p ≡ i%% | ||
- | === Arreglos de C vs. arreglos en Java === | + | ==== Arreglos de C vs. arreglos en Java ==== |
Dado que en Java los arreglos corresponden a un objeto bien definido con índices que parten en 0 y terminan en el tamaño del arreglo menos 1, Java puede validar cada acceso y arrojar una excepción cuando el índice está fuera del rango permitido. | Dado que en Java los arreglos corresponden a un objeto bien definido con índices que parten en 0 y terminan en el tamaño del arreglo menos 1, Java puede validar cada acceso y arrojar una excepción cuando el índice está fuera del rango permitido. | ||
Línea 377: | Línea 382: | ||
Gracias a la aritmética de punteros es posible programar en C sistemas operativos, malloc/free o el recolector de basuras de Java. En contrapartida esta aritmética conduce a todo tipo de errores que son muy difíciles de diagnosticar. | Gracias a la aritmética de punteros es posible programar en C sistemas operativos, malloc/free o el recolector de basuras de Java. En contrapartida esta aritmética conduce a todo tipo de errores que son muy difíciles de diagnosticar. | ||
- | ==== Cast de punteros ==== | + | ===== Cast de punteros |
El significado de un cast de un dato primitivo es distinto del de un cast de un puntero. | El significado de un cast de un dato primitivo es distinto del de un cast de un puntero. | ||
Línea 391: | Línea 396: | ||
</ | </ | ||
- | Este código es perfectamente legal en C aunque es difícil predecir cuáles serán los valores de i y j. Ciertamente no es algo ni remotamente parecido a 3 o 3.14159, pero mientras que el compilador considera que //*p// es un variable real (de tamaño 8), la expresión //*q// corresponde a una variable entera de 4 bytes. | + | Este código es perfectamente legal en C aunque es difícil predecir cuáles serán los valores de i y j. Ciertamente no es algo ni remotamente parecido a 3 o 3.14159, pero mientras que el compilador considera que //*p// es una variable real (de tamaño 8), la expresión //*q// corresponde a una variable entera de 4 bytes. |
{{ : | {{ : | ||
- | Es importante destacar que en la mayoría de los casos asignar un puntero a otro puntero de distinto tipo conduce a un error de programación. | + | Es importante destacar que en la mayoría de los casos asignar un puntero a otro puntero de distinto tipo conduce a un error de programación. |
- | ==== Big endian vs. Little endian ==== | + | === Ejercicio === |
+ | |||
+ | El siguiente programa es absolutamente legal en C. ¿Qué hace? | ||
+ | |||
+ | < | ||
+ | int *p= ( (int*)malloc(10*sizeof(int)) ) + 5; /* Ojo con el + 5 al final */ | ||
+ | int i; | ||
+ | for (i= -5; i<5; i++) | ||
+ | p[i]= 0; | ||
+ | </ | ||
+ | |||
+ | Por supuesto, acá no se está fomentando este estilo de programación, | ||
+ | |||
+ | |||
+ | ===== Big endian vs. Little endian | ||
¿Qué valor retorna la siguiente función? | ¿Qué valor retorna la siguiente función? | ||
Línea 413: | Línea 432: | ||
{{ : | {{ : | ||
- | El valor retornado por esta función depende de una propiedad del hardware. | + | El valor retornado por esta función depende de una propiedad del hardware. |
Los únicos casos en donde esta distinción arquitectural es importante es cuando se graban archivos binarios o se transmiten datos binarios por la red. Es decir en vez de escribir los enteros en formato ascii en notación decimal, se escriben directamente los 4 bytes del entero. | Los únicos casos en donde esta distinción arquitectural es importante es cuando se graban archivos binarios o se transmiten datos binarios por la red. Es decir en vez de escribir los enteros en formato ascii en notación decimal, se escriben directamente los 4 bytes del entero. | ||
- | ==== Resumen ==== | + | |
+ | Por lo tanto la función de más arriba sí es útil para determinar en qué formato enviar valores por la red. | ||
+ | |||
+ | === Ejercicio === | ||
+ | |||
+ | Programe la función int '' | ||
+ | ===== Resumen | ||
La siguiente tabla resume las operaciones con punteros: | La siguiente tabla resume las operaciones con punteros: | ||
Línea 428: | Línea 453: | ||
|puntero nulo | '' | |puntero nulo | '' | ||
|aritmética de punteros | '' | |aritmética de punteros | '' | ||
- |
punteros.1344030870.txt.gz · Última modificación: 2012/08/03 21:54 por lmateu