Vous êtes sur la page 1sur 72

08 - Punteros

Diego Andrs Alvarez Marn


Profesor Asociado
Universidad Nacional de Colombia
Sede Manizales

Temario

Declaracin de punteros

Inicializacin de punteros

Arrays de punteros vs
punteros a arrays

Puntero NULL

Punteros a matrices

Aritmtica con punteros

Punteros a funciones

Punteros constantes y a
constantes

Punteros a punteros

malloc(), free()

Punteros void*

Memoria dinmica

Comparacin de punteros

Memory leak y Valgrind

Arrays y punteros

Punteros restrict

Punteros a uniones y
estructuras

Punteros
Los 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 direccin de


memoria, este apunta a cualquier lugar de
memoria y por lo tanto si se usa, usted podra
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().

Los punteros se expresan como


hexadecimales

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

Declaracin de punteros
Se hace especificando el tipo de dato para el cual
se guardar la direccin de memoria y un nombre
para la variable:
Estas tres declaraciones
son equivalentes.

Inicializacin de punteros
Evite utilizar punteros que no han sido
inicializados a una direccin de memoria
conocida, ya que estos pueden hacer fallar el
programa

Es una buena practica de programacin inicializar


el puntero a NULL en caso que no se sepa
inmediatamente la direccin exacta a asignrsele.
7

NULL

Es un valor que se le da a un puntero que no


apunta a ningn lado. NULL vale 0x0. Est
definido en stdlib.h
Se utiliza comnmente para denotar el fin de
una bsqueda o el fin de un procesamiento, o
simplemente para denotar que no hubo xito
en alguna operacin.
Un puntero NULL es diferente de un puntero no
inicializado: un puntero NULL no apunta a
algn objeto; un puntero no inicializado puede
apuntar a cualquier zona de la memoria.

Aritmtica con punteros


int *puntero;
puntero++, puntero-- cada vez que se
incrementa(decrementa) un puntero, este apunta
a la posicin de memoria del siguiente(anterior)
elemento de su tipo base.
puntero+5 apunta 20 bytes ms all que puntero,
ya que el tamao del tipo base (int) es 4 bytes.

10

11

12

Un truco con arrays de cadenas y


aritmtica de punteros

13

Operaciones invlidas en aritmtica


con punteros

Tomado de:
http://www.c4learn.com/illegal-arithmetic-operations-with-pointer.html

Suma, multiplicacin, mdulo o divisin de


direcciones de memoria (la resta de punteros
si est definida)
AND, OR, XOR, NOT en punteros

14

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/x

http://www.viva64.com/en/a/0050/

http://www.keil.com/support/man/docs/c166/c166

http://stackoverflow.com/questions/7956763/varia

15
http://www.gnu.org/software/libc/manual/html_no

Punteros constantes
Aqu p es un puntero que no se le puede cambiar
la direccin de memoria a la que se est
refiriendo. Se define nicamente en su
declaracin y posteriormente no se le puede
apuntar a otra variable.

16

Punteros constantes
Observe que el compilador dice que "p" es una
variable de solo lectura, esto quiere decir que no
podemos cambiar su contenido.

17

Punteros a constantes
En 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.

18

Punteros a constantes

19

20

Punteros constantes a constantes


Es una mezcla de las dos anteriores: un puntero
constante a constante es un puntero que no puede
cambiar la direccin a la que apunta ni tampoco puede
cambiar el contenido de la direccin a la que apunta.

21

Punteros void
Un puntero void es un puntero genrico, ya que
puede apuntar a cualquier direccin 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 genrico
Tenga en cuenta que:
void *f;
void f();
void f[];
void f;

es un comando vlido
es un comando vlido
es un comando errneo
es un comando errneo

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.

Aritmtica de punteros con un


puntero void
GNU C extension:
La aritmtica con
punteros void *, se
trata igual que
aquella con punteros
char *. Con otros
compiladores esto no
es vlido.

Comparacin
de punteros
Los punteros se
pueden comparar
utilizando == o !=
para verificar si
estos apuntan a la
misma direccin
de memoria o no.

25

Punteros y arrays
El decir x[i] es equivalente a decir *(x+i). De
hecho x solo es la direccin de memoria del
primer elemento del array, es decir &(x[0]).
La relacin entre arrays multidimensionales y
punteros es array[i][j] == *(array + i*NCOL + j)

26

Punteros a uniones

27

Punteros a estructuras

28

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

29

Arrays de punteros vs
punteros a arrays

30

Puntero a
matriz

31

Punteros a funciones
Para obtener la direccin de memoria de una
funcin se puede hacer lo siguiente:

Observe que el nombre de una funcin sin el () es


un puntero que apunta al cdigo que ejecuta
dicha funcin.

32

BUSCAR ...

En punteros a funciones se tiene claro que


"sizeof(fp1)==4" (en 32 bits) ya que es la
dimensin del puntero fp1, pero no se tiene
conocimiento acerca de la dimensin de "sizeof
(*fp1)" que seria la dimensin a lo que apunta fp1
(cdigo que ejecuta dicha funcin), es necesario
investigar este valor para dejarlo claro en clase.
33

ff es un puntero a
una funcin que
retorna un double y
que tiene tres
argumentos: 1)
puntero a una
funcin que recibe
un double y devuelve
un double, 2) un
double y 3) otro
double

34

Puntero a puntero

35

Puntero a puntero

36

Como interpretar punteros como:


char *(*(**f[][8])())[];
f es un vector de vectores de 8 punteros a
punteros a una funcin que retorna un puntero a
un arreglo de punteros a carcter. 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

37

Regla de la espiral
Nota: el * tiene menor precedencia que [] y ()

*
()
[]
[][]

puntero a
funcin que retorna
array de
matriz de

Siempre resuelva lo que


est entre parntesis primero,
ya que este se utiliza tambin
para cambiar las precedencias

38

*
()
[]
[][]

puntero a ***
funcin que recibe *** y que retorna ***
array de ***
matriz de *** Nota: el * tiene menor precedencia que [] y ()

Siempre resuelva lo que


est entre parntesis primero,
ya que este se utiliza tambin
para cambiar las precedencias

39

Regla de la espiral

40

Regla de la espiral

41

Otros punteros raros...

42

Usar typedef a veces simplifica


estos punteros complicados

43

Localizacin dinmica de memoria


Cuando se necesitan grandes tamaos de
memoria, estos se pueden solicitar durante la
ejecucin al sistema operativo. El sistema
operativo intentar proveer dicho bloque de
memoria de un pedazo llamado el montn (heap).
Dicha solicitud se hace utilizando la funcin
malloc() y se libera usando free(). Ambas
funciones estn definidas en stdlib.h
44

#include <stdlib.h>
void *malloc(size_t tam);
malloc() solicita tam bytes del montn 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.

45

#include <stdlib.h>
void free(void *p);
free() libera el espacio de memoria al que apunta
p y que fue asignado previamente del montn 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 prctica de
programacin asignarle al puntero un NULL
despus de haber llamado a free(); esto con el
objeto de evitar posibles errores difciles de
encontrar en el cdigo.
46

#include <stdlib.h>
void *calloc(size_t n, size_t tam);
calloc() solicita n*tam bytes del montn 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: nmero de elementos, tam: tamao de
cada elemento)
Use preferiblemente malloc() a no ser que en
verdad requiera inicializar la memoria en 0, ya
que el borrado toma tiempo.
47

#include <stdlib.h>
void *realloc(void *p, size_t tam);
realloc() reasigna el tamao del bloque de
memoria al que apunta p a tam bytes. En caso
que se requiera ms memoria, los contenidos
anteriores sern 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).

48

Definiendo arrays dinmicos con


malloc() y free()

x existe en la
memoria de pila

El casting con malloc es opcional

x existe en la
memoria del montn
49

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[1]
x[2]
x[N-1]

==
==
==
==

*(x+0)
*(x+1)
*(x+2)
*(x+N-1)

50

Asignacin dinmica de matrices

51

Acceso a los elementos


de la matriz A por medio
de aritmtica de
punteros

52

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 aritmtica de punteros
solo funciona si A es un puntero
int *

53

Tamao de aquello a lo
que apunta el puntero A

54

malloc(), realloc(), free()

Nota: perror() se encuentra en


stdio.h y cumple una funcin
similar a
fprintf(stderr, bla bla bla ...\n);
55
aunque la primera es ms
poderosa y tiene otras opciones.

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 duracin de la declaracin del puntero,
todos los datos accesados a travs (como por
ejemplo puntero+4) de este no sern cambiados
de ninguna otra forma utilizando otro puntero.
Con esto se posibilita crear cdigo mucho ms
veloz.
Utilizar la palabra restrict le PROMETE al
compilador que usted no har aliasing. Si se hace
aliasing, se obtendrn resultados inesperados. 56

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 ms rpido.
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.

57

#include <string.h>
void *memcpy(void * restrict dest, const void * restrict orig, size_t n);

memcpy() copia n
bytes que
empiezan en la
direccin de
memoria orig al
bloque de
memoria que
empieza en la
direccin de
memoria dest.
Las reas no se
pueden traslapar.
Si las reas se
traslapan, use
memmove().
memcpy() retorna
un puntero a dest

Puntero a
constante

58

#include <string.h>
void *memcpy(void * restrict dest, const void * restrict orig, size_t n);

Observe que como las reas no se traslaparn


por la promesa hecha por el puntero restrict,
memcpy() es mucho ms veloz que memmove(),
en teora, pero para mi (Diego) en la prctica eso
no se cumple.

59

#include <string.h>
void *memmove(void *dest, const void *src, size_t n);
memmove() copia n bytes desde la direccin 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
informacin se copia a dest.
memmove() retorna un puntero a dest

60

61

62

#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.
63

Era mejor
llamar a
calloc()

64

65

Errores por utilizar mal la


localizacin dinmica 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 programacin. Un
puntero al crearse apunta a una ubicacin arbitraria, lo que
podra hacer que el programa funcione de forma extraa: 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 nonreusable 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
66
no se le hace free() a la memoria asignada con malloc().

Errores por utilizar mal la


localizacin dinmica 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.

67

Encontrar errores cometidos por punteros mal


utilizados puede ser extramadamente frustrante

Fuente: http://xkcd.com/371/
Ver: http://en.wikipedia.org/wiki/Dangling_pointer

68

Valgrind
http://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 depuracin de problemas de
memoria y rendimiento de programas.
La herramienta ms usada es memcheck. Memcheck introduce cdigo
de instrumentacin 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 lmites de bloques de memoria


dinmica.

Fugas de memoria.

etc.

69

Valgrind

Ver Valgrind en accin:

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

70

Alternativas a Valgrind para


Windows

http://code.google.com/p/drmemory/

http://latedev.wordpress.com/2012/05/19/valgrind

http://stackoverflow.com/questions/413477/is-the

http://en.wikipedia.org/wiki/Dynamic_program_an

71

Hacer lista doblemente enlazada

72

Vous aimerez peut-être aussi