08 - Punteros en lenguaje C

72
1 08 - Punteros Diego Andrés Alvarez Marín Profesor Asociado Universidad Nacional de Colombia Sede Manizales

description

Si quiere descargar la presentación y los códigos fuente, dirijase a: http://programaciondecomputadoresunalmzl.wikispaces.com/codigos_y_diapositivas Le agradecería si me reporta los errores que encuentre en la diapositiva (daalvarez arroba unal punto edu punto co)

Transcript of 08 - Punteros en lenguaje C

Page 1: 08 - Punteros en lenguaje C

1

08 - Punteros

Diego Andrés Alvarez MarínProfesor Asociado

Universidad Nacional de ColombiaSede Manizales

Page 2: 08 - Punteros en lenguaje C

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

Page 3: 08 - Punteros en lenguaje C

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

Page 4: 08 - Punteros en lenguaje C

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().

Page 5: 08 - Punteros en lenguaje C

Tomado de: http://xkcd.com/138/

Los punteros se expresan como hexadecimales

Page 6: 08 - Punteros en lenguaje C

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.

Page 7: 08 - Punteros en lenguaje C

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.

Page 8: 08 - Punteros en lenguaje C
Page 9: 08 - Punteros en lenguaje C

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.

Page 10: 08 - Punteros en lenguaje C

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.

Page 11: 08 - Punteros en lenguaje C

11

Page 12: 08 - Punteros en lenguaje C

12

Page 13: 08 - Punteros en lenguaje C

13

Un truco con arrays de cadenas y aritmética de punteros

Page 14: 08 - Punteros en lenguaje C

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

Page 15: 08 - Punteros en lenguaje C

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

Page 16: 08 - Punteros en lenguaje C

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.

Page 17: 08 - Punteros en lenguaje C

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.

Page 18: 08 - Punteros en lenguaje C

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.

Page 19: 08 - Punteros en lenguaje C

19

Punteros a constantes

Page 20: 08 - Punteros en lenguaje C

20

Page 21: 08 - Punteros en lenguaje C

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.

Page 22: 08 - Punteros en lenguaje C

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

Page 23: 08 - Punteros en lenguaje C

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.

Page 24: 08 - Punteros en lenguaje C

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.

Page 25: 08 - Punteros en lenguaje C

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

Page 26: 08 - Punteros en lenguaje C

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)

Page 27: 08 - Punteros en lenguaje C

27

Punteros a uniones

Page 28: 08 - Punteros en lenguaje C

28

Punteros a estructuras

Page 29: 08 - Punteros en lenguaje C

29

Arrays de punteros

Es diferente que:int (*vec)[3];(puntero a un vector de 3 elementos enteros)

Page 30: 08 - Punteros en lenguaje C

30

Arrays de punteros vs punteros a arrays

Page 31: 08 - Punteros en lenguaje C

31

Puntero a matriz

Page 32: 08 - Punteros en lenguaje C

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.

Page 33: 08 - Punteros en lenguaje C

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.

Page 34: 08 - Punteros en lenguaje C

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

Page 35: 08 - Punteros en lenguaje C

35

Puntero a puntero

Page 36: 08 - Punteros en lenguaje C

36

Puntero a puntero

Page 37: 08 - Punteros en lenguaje C

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

Page 38: 08 - Punteros en lenguaje C

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 ()

Page 39: 08 - Punteros en lenguaje C

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 ()

Page 40: 08 - Punteros en lenguaje C

40

Regla de la espiral

Page 41: 08 - Punteros en lenguaje C

41

Regla de la espiral

Page 42: 08 - Punteros en lenguaje C

42

Otros punteros raros...

Page 43: 08 - Punteros en lenguaje C

43

Usar typedef a veces simplifica estos punteros complicados

Page 44: 08 - Punteros en lenguaje C

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

Page 45: 08 - Punteros en lenguaje C

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.

Page 46: 08 - Punteros en lenguaje C

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.

Page 47: 08 - Punteros en lenguaje C

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.

Page 48: 08 - Punteros en lenguaje C

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).

Page 49: 08 - Punteros en lenguaje C

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

Page 50: 08 - Punteros en lenguaje C

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)

Page 51: 08 - Punteros en lenguaje C

51

Asignación dinámica de matrices

Page 52: 08 - Punteros en lenguaje C

52

Acceso a los elementos de la matriz A por medio de aritmética de punteros

Page 53: 08 - Punteros en lenguaje C

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 *

Page 54: 08 - Punteros en lenguaje C

54

Tamaño de aquello a loque apunta el puntero A

Page 55: 08 - Punteros en lenguaje C

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.

Page 56: 08 - Punteros en lenguaje C

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.

Page 57: 08 - Punteros en lenguaje C

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

Page 58: 08 - Punteros en lenguaje C

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

Page 59: 08 - Punteros en lenguaje C

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.

Page 60: 08 - Punteros en lenguaje C

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

Page 61: 08 - Punteros en lenguaje C

61

Page 62: 08 - Punteros en lenguaje C

62

Page 63: 08 - Punteros en lenguaje C

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.

Page 64: 08 - Punteros en lenguaje C

64

Era mejor llamar a calloc()

Page 65: 08 - Punteros en lenguaje C

65

Page 66: 08 - Punteros en lenguaje C

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().

Page 67: 08 - Punteros en lenguaje C

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.

Page 68: 08 - Punteros en lenguaje C

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

Page 69: 08 - Punteros en lenguaje C

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.

Page 70: 08 - Punteros en lenguaje C

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

Page 71: 08 - Punteros en lenguaje C

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

Page 72: 08 - Punteros en lenguaje C

72

Hacer lista doblemente enlazada