08 - Punteros en lenguaje C

Post on 23-Jun-2015

282 views 11 download

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

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

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

Los punteros se expresan como hexadecimales

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