Post on 23-Jun-2015
description
1
08 - Punteros
Diego Andrés Alvarez MarínProfesor Asociado
Universidad Nacional de ColombiaSede Manizales
2
Temario
● Declaración de punteros
● Inicialización de punteros
● Puntero NULL
● Aritmética con punteros
● Punteros constantes y a constantes
● Punteros void*
● Comparación de punteros
● Arrays y punteros
● Punteros a uniones y estructuras
● Arrays de punteros vs punteros a arrays
● Punteros a matrices
● Punteros a funciones
● Punteros a punteros
● malloc(), free()
● Memoria dinámica
● Memory leak y Valgrind
● Punteros restrict
PunterosLos punteros son un tipo de dato que guarda direcciones de memoria (de variables, constantes, funciones, etc) int* x; // se puede escribir tambien int * x o int *x
int *x, *y; // dos punteros enteros
int *x, y; // un puntero a entero y una variable entera
int* x, y; // un puntero a entero y una variable entera
Si no se inicializa un puntero a una dirección de memoria, este apunta a cualquier lugar de memoria y por lo tanto si se usa, usted podría hacer que el programa falle (crash: segment fault): http://en.wikipedia.org/wiki/Dangling_pointer
Tutorial
Un tutorial sobre punteros recomendado es:
Ted Jensen's Tutorial on Pointers and Arrays in C
http://home.earthlink.net/~momotuk/pointers.pdf
NOTA: este es un buen tutorial, sin embargo el único defecto que le veo es que no nombra el comando free() cuando se utiliza malloc().
Declaración de punteros
Se hace especificando el tipo de dato para el cual se guardará la dirección de memoria y un nombre para la variable:
Estas tres declaracionesson equivalentes.
7
Inicialización de punteros
Evite utilizar punteros que no han sido inicializados a una dirección de memoria conocida, ya que estos pueden hacer fallar el programa
Es una buena practica de programación inicializar el puntero a NULL en caso que no se sepa inmediatamente la dirección exacta a asignársele.
9
NULL● Es un valor que se le da a un puntero que no
apunta a ningún lado. NULL vale 0x0. Está definido en stdlib.h
● Se utiliza comúnmente para denotar el fin de una búsqueda o el fin de un procesamiento, o simplemente para denotar que no hubo éxito en alguna operación.
● Un puntero NULL es diferente de un puntero no inicializado: un puntero NULL no apunta a algún objeto; un puntero no inicializado puede apuntar a cualquier zona de la memoria.
10
Aritmética con punteros
int *puntero;
puntero++, puntero-- cada vez que se incrementa(decrementa) un puntero, este apunta a la posición de memoria del siguiente(anterior) elemento de su tipo base.
puntero+5 apunta 20 bytes más allá que puntero, ya que el tamaño del tipo base (int) es 4 bytes.
11
12
13
Un truco con arrays de cadenas y aritmética de punteros
14
Operaciones inválidas en aritmética con punteros
● Tomado de:http://www.c4learn.com/illegal-arithmetic-operations-with-pointer.html
● Suma, multiplicación, módulo o división de direcciones de memoria (la resta de punteros si está definida)
● AND, OR, XOR, NOT en punteros
15
Resta de punteros
Sí está definida. Para ello se define el tipo de datos ptrdiff_t el cual se encuentra declarado en stddef.h
Ver:● http://pubs.opengroup.org/onlinepubs/7908799/xsh/stddef.h.html● http://www.viva64.com/en/a/0050/● http://www.keil.com/support/man/docs/c166/c166_lib_ptrdiff_t.htm● http://stackoverflow.com/questions/7956763/variables-of-type-size-t-and-ptrdiff-t● http://www.gnu.org/software/libc/manual/html_node/Important-Data-Types.html
16
Punteros constantesAquí p es un puntero que no se le puede cambiar la dirección de memoria a la que se está refiriendo. Se define únicamente en su declaración y posteriormente no se le puede apuntar a otra variable.
17
Punteros constantes
Observe que el compilador dice que "p" es una variable de solo lectura, esto quiere decir que no podemos cambiar su contenido.
18
Punteros a constantesEn este caso, p apunta a una variable, pero la trata como una constante (variable de solo lectura), por lo que no se puede utilizar el operador * para cambiar el contenido de la variable a la que apuntamos. Esto nos sirve para pasar matrices por valor, no por referencia (comportamiento por defecto).
A pesar que var1 no es constante, el puntero la trata como tal.
19
Punteros a constantes
20
21
Punteros constantes a constantes
Es una mezcla de las dos anteriores: un puntero constante a constante es un puntero que no puede cambiar la dirección a la que apunta ni tampoco puede cambiar el contenido de la dirección a la que apunta.
Punteros voidUn puntero void es un puntero genérico, ya que puede apuntar a cualquier dirección de memoria y/o a cualquier tipo de dato. Se declara como un puntero normal, pero con un tipo void *:
void *p; // puntero void o puntero genérico
Tenga en cuenta que:
void *f; es un comando válidovoid f(); es un comando válidovoid f[]; es un comando erróneovoid f; es un comando erróneo
Para acceder al contenido de la memoria apuntado por el puntero void se debe utilizar el operador *, junto con un casting que indique el tipo que se espera leer.
Aritmética de punteros con un puntero void
GNU C extension:La aritmética con punteros void *, se trata igual que aquella con punteros char *. Con otros compiladores esto no es válido.
25
Los punteros se pueden comparar utilizando == o != para verificar si estos apuntan a la misma dirección de memoria o no.
Comparación de punteros
26
Punteros y arrays
El decir x[i] es equivalente a decir *(x+i). De hecho x solo es la dirección de memoria del primer elemento del array, es decir &(x[0]).
La relación entre arrays multidimensionales y punteros es array[i][j] == *(array + i*NCOL + j)
27
Punteros a uniones
28
Punteros a estructuras
29
Arrays de punteros
Es diferente que:int (*vec)[3];(puntero a un vector de 3 elementos enteros)
30
Arrays de punteros vs punteros a arrays
31
Puntero a matriz
32
Punteros a funciones
Para obtener la dirección de memoria de una función se puede hacer lo siguiente:
Observe que el nombre de una función sin el () es un puntero que apunta al código que ejecuta dicha función.
33
BUSCAR ...
En punteros a funciones se tiene claro que "sizeof(fp1)==4" (en 32 bits) ya que es la dimensión del puntero fp1, pero no se tiene conocimiento acerca de la dimensión de "sizeof (*fp1)" que seria la dimensión a lo que apunta fp1 (código que ejecuta dicha función), es necesario investigar este valor para dejarlo claro en clase.
34
ff es un puntero a una función que retorna un double y que tiene tres argumentos: 1) puntero a una función que recibe un double y devuelve un double, 2) un double y 3) otro double
35
Puntero a puntero
36
Puntero a puntero
37
Como interpretar punteros como:char *(*(**f[][8])())[];
f es un vector de vectores de 8 punteros a punteros a una función que retorna un puntero a un arreglo de punteros a carácter. Este tipo de declaraciones son complicadas de entender, pero a veces toca descifrarlas y utilizarlas
Ver:
http://unixwiz.net/techtips/reading-cdecl.html
http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html
http://c-faq.com/decl/spiral.anderson.html
38
Regla de la espiral
* puntero a() función que retorna[] array de[][] matriz de
Siempre resuelva lo que está entre paréntesis primero,ya que este se utiliza también para cambiar las precedencias
Nota: el * tiene menor precedencia que [] y ()
39
* puntero a ***() función que recibe *** y que retorna ***[] array de ***[][] matriz de ***
Siempre resuelva lo que está entre paréntesis primero,ya que este se utiliza también para cambiar las precedencias
Nota: el * tiene menor precedencia que [] y ()
40
Regla de la espiral
41
Regla de la espiral
42
Otros punteros raros...
43
Usar typedef a veces simplifica estos punteros complicados
44
Localización dinámica de memoria
Cuando se necesitan grandes tamaños de memoria, estos se pueden solicitar durante la ejecución al sistema operativo. El sistema operativo intentará proveer dicho bloque de memoria de un pedazo llamado el montón (heap). Dicha solicitud se hace utilizando la función malloc() y se libera usando free(). Ambas funciones están definidas en stdlib.h
45
#include <stdlib.h>void *malloc(size_t tam);
malloc() solicita tam bytes del montón y retorna un puntero a dicho bloque de memoria. La memoria no se borra. Si el tam==0, entonces malloc() retorna NULL. Si no se pudo asignar el bloque de memoria malloc() retorna NULL.
46
#include <stdlib.h>void free(void *p);
free() libera el espacio de memoria al que apunta p y que fue asignado previamente del montón con malloc(), calloc() or realloc(). Si se libera un puntero que no fue asignado previamente, entonces el programa falla. Si p==NULL no se hace nada. Se aconseja como buena práctica de programación asignarle al puntero un NULL después de haber llamado a free(); esto con el objeto de evitar posibles errores difíciles de encontrar en el código.
47
#include <stdlib.h>void *calloc(size_t n, size_t tam);
calloc() solicita n*tam bytes del montón y retorna un puntero a dicho bloque de memoria. La memoria se inicializa a 0. Si n==0 o tam==0, entonces calloc() retorna NULL. Si no se pudo asignar el bloque de memoria calloc() retorna NULL. (n: número de elementos, tam: tamaño de cada elemento)
Use preferiblemente malloc() a no ser que en verdad requiera inicializar la memoria en 0, ya que el borrado toma tiempo.
48
#include <stdlib.h>void *realloc(void *p, size_t tam);
realloc() reasigna el tamaño del bloque de memoria al que apunta p a tam bytes. En caso que se requiera más memoria, los contenidos anteriores serán copiados, la memoria nueva no se inicializará a cero y la memoria vieja será liberada.
Se asume que el puntero p fue devuelto previamente por malloc(), calloc() o realloc(). Si p==NULL el comando funciona como malloc(tam). Si tam==0, el comando es equivalente a free(p).
49
Definiendo arrays dinámicos conmalloc() y free()
x existe en lamemoria de pila
x existe en lamemoria del montón
El casting con malloc es opcional
50
Tenga en cuenta que con las direcciones de memoria de los elementos del array:&x[0] == x+0 == x&x[1] == x+1&x[2] == x+2&x[N-1] == x+N-1
Por lo tanto:x[0] == *(x+0)x[1] == *(x+1)x[2] == *(x+2)x[N-1] == *(x+N-1)
51
Asignación dinámica de matrices
52
Acceso a los elementos de la matriz A por medio de aritmética de punteros
53
Error!!!
Lo correcto es:*((int *)A + i)El casting se debe hacer porque A es un puntero a un array de punteros, es decir cada elemento de A es del tipo int [4]. La aritmética de punteros solo funciona si A es un puntero int *
54
Tamaño de aquello a loque apunta el puntero A
55
malloc(), realloc(), free()
Nota: perror() se encuentra en stdio.h y cumple una función similar afprintf(stderr, “bla bla bla ...\n”); aunque la primera es más poderosa y tiene otras opciones.
56
Punteros restrict http://en.wikipedia.org/wiki/Restrict
Fueron introducidos en el C99. La palabra restrict solo se puede aplicar a punteros. Indica que durante la duración de la declaración del puntero, todos los datos accesados a través (como por ejemplo puntero+4) de este no serán cambiados de ninguna otra forma utilizando otro puntero. Con esto se posibilita crear código mucho más veloz.
Utilizar la palabra restrict le PROMETE al compilador que usted no hará aliasing. Si se hace aliasing, se obtendrán resultados inesperados.
57
Ejemplo
En la WIKI hay unos ejemplos que usan restrict. No los pongo aquí porque no se refleja en verdad que su uso haga el programa más rápido. Pregunté en comp.lang.c y nadie me dió respuesta de ese comportamiento.
https://groups.google.com/forum/?fromgroups=#!topicsearchin/comp.lang.c/restrict/comp.lang.c/VNURlApSMmA
58
#include <string.h>void *memcpy(void * restrict dest, const void * restrict orig, size_t n);
memcpy() copia n bytes que empiezan en la dirección de memoria orig al bloque de memoria que empieza en la dirección de memoria dest. Las áreas no se pueden traslapar. Si las áreas se traslapan, use memmove().
memcpy() retorna un puntero a dest
Puntero aconstante
59
#include <string.h>void *memcpy(void * restrict dest, const void * restrict orig, size_t n);
Observe que como las áreas no se traslaparán por la promesa hecha por el puntero restrict, memcpy() es mucho más veloz que memmove(), en teoría, pero para mi (Diego) en la práctica eso no se cumple.
60
#include <string.h>void *memmove(void *dest, const void *src, size_t n);
memmove() copia n bytes desde la dirección de memoria que empieza en orig hacia el bloque de memoria que empieza en dest.
El procedimiento primero realiza una copia del bloque que empieza en orig a un área temporal de memoria que no se traslapa con orig o con dest. Luego esta información se copia a dest.
memmove() retorna un puntero a dest
61
62
63
#include <string.h>void *memset(void *s, int c, size_t n);memset() llena la memoria con los primeros n bytes de memoria del área a la que apunta s con la constante (byte) especificado por c.
memset() retorna el puntero s.
64
Era mejor llamar a calloc()
65
66
Errores por utilizar mal la localización dinámica de memoria
● Not checking for allocation failures. Memory allocation is not guaranteed to succeed. If there's no check for successful allocation implemented, this usually leads to a crash of the program or the entire system. Es muy peligroso utilizar punteros sin tener cuidado durante la programación. Un puntero al crearse apunta a una ubicación arbitraria, lo que podría hacer que el programa funcione de forma extraña: ver http://en.wikipedia.org/wiki/Dangling_pointer
● Memory leaks (fuga de memoria). Failure to deallocate memory using free leads to buildup of memory that is non-reusable memory, which is no longer used by the program. This wastes memory resources and can lead to allocation failures when these resources are exhausted. Ocurre cuando no se le hace free() a la memoria asignada con malloc().
67
Errores por utilizar mal la localización dinámica de memoria
Logical errors. All allocations must follow the same pattern: allocation using malloc(), usage to store data, deallocation using free(). Failures to adhere to this pattern, such as memory usage after a call to free() or before a call to malloc(), calling free twice ("double free"), etc., usually leads to a crash of the program.
68
Encontrar errores cometidos por punteros mal utilizados puede ser extramadamente frustrante
Fuente: http://xkcd.com/371/
Ver: http://en.wikipedia.org/wiki/Dangling_pointer
69
Valgrindhttp://valgrind.org/ http://en.wikipedia.org/wiki/Valgrind
Valgrind es un conjunto de herramientas libres (que corren bajo GNU/LINUX y Mac OS) y que ayudan en la depuración de problemas de memoria y rendimiento de programas.
La herramienta más usada es memcheck. Memcheck introduce código de instrumentación en el programa a depurar, lo que le permite realizar un seguimiento del uso de la memoria y detectar los siguientes problemas:
● Uso de memoria no inicializada.
● Lectura/escritura de memoria que ha sido previamente liberada.
● Lectura/escritura fuera de los límites de bloques de memoria dinámica.
● Fugas de memoria.
● etc.
70
Valgrind
● Ver Valgrind en acción:● http://www.youtube.com/watch?v=h8sgNW0IxtQ
● http://www.youtube.com/watch?v=7xJuBqhlChE
● http://www.youtube.com/watch?v=fvTsFjDuag8
● http://www.youtube.com/watch?v=aDKpqq7EYqQ
71
Alternativas a Valgrind para Windows
● http://code.google.com/p/drmemory/● http://latedev.wordpress.com/2012/05/19/valgrind-for-windows/● http://stackoverflow.com/questions/413477/is-there-a-good-valgrind-substitute-for-windows● http://en.wikipedia.org/wiki/Dynamic_program_analysis
72
Hacer lista doblemente enlazada