Вы находитесь на странице: 1из 40

Fundamentos de Programacin

Apuntadores en C

MCC Enrique Ayala Franco

Temas
Distribucin de memoria en un programa. Introduccin al concepto de apuntador. Declaracin de apuntadores. Desreferencias. Aritmtica de apuntadores. Apuntadores y arreglos. Paso de parmetros a funciones por referencia.

Objetivo
General:
Aplicar el concepto de apuntador dentro del lenguaje de programacin ANSI C.

Especficos:
Introducir el concepto de apuntador. Aplicar aritmtica de apuntadores para acceder valores en arreglos.

Punteros o Apuntadores
Un apuntador o puntero es una variable que contiene una direccin de memoria. Por ejemplo, la direccin de otra variable .

Apuntadores
Apuntadores
Son poderosos pero difciles de manejar Se utilizan para manejar valores por referencia Tienen una relacin directa con los arreglos y con las cadenas (arreglos de caracteres) Necesarios para el manejo de memoria dinmica.

Una variable Apuntador


Normalmente una variable tiene un valor especifico (referencia directa al valor) Pero un apuntador contiene la direccin de memoria de otra variable como su valor. Un apuntador tiene la direccin de una variable que tiene un valor especfico (referencia indirecta)
count 7 countPtr count 7

INDIRECCIN: referencia de un valor a travs de un apuntador


5

Variable (referencia directa)

Apuntador (referencia indirecta)

Declaracin de apuntadores
Las variables puntero se declaran de la siguiente forma: tipo *nombre; Siendo nombre el identificador de la variable puntero, y tipo el tipo de variable a la que apunta. Por ejemplo:
char *m; int *n; float *p; // apuntador a char // apuntador a entero // apuntador a flotante

En estas declaraciones, las variables m, n y p son punteros que apuntan, respectivamente, a datos de tipo char, int y float.
6

Declaracin e Inicializacin
Para declarar mltiples apuntadores se requieren mltiples asteriscos:
int *myPtr1, *myPtr2;

Se pueden declarar apuntadores de cualquier tipo: a enteros, a caracteres, a estructuras, etc.


Inicializacin
Se deben inicializar con un valor 0, NULL, o alguna direccin
El valor 0 o NULL apunta a nada
Int *myPtr=NULL;
7

Operaciones con Apuntadores


Los operadores de punteros son:
& * direccin de en la direccin de

El operador * slo se puede aplicar a punteros


Las operaciones permitidas con punteros son:
Asignacin Incremento / Decremento Suma / Resta Comparacin
8

Operadores de apuntadores & (operador de direccin)


Regresa la direccin de memoria de su operando

Ejemplo
int y = 9; int *yPtr; yPtr = &y; // yPtr almacena la direccin de y

yPtr apunta a y
y 9 yPtr 500000 600000 600000 y 9

yPtr

La direccin de y es el valor de yPtr


9

Operadores de apuntadores * (indireccin o desreferencia)


Regresa un sinnimo de un objeto Es decir el valor de la variable a la cual se est apuntando *yPtr regresa y

Ejemplo:
*yPtr = 20; // asigna 20 a y Porque yPtr apunta a y

* y & son operadores inversos uno de otro


10

Mas ejemplos
Dadas las declaraciones
float x; float *p, *q;

la forma de asignar a p y q la direccin de x es:


p = &x; q = &x;

Ahora p y q almacenan la misma direccin de memoria: la de la variable x. El mismo efecto se consigue con la asignacin directa entre punteros:
p = &x; q = p;

No es correcta una sentencia como


p = x;

11

Ejemplo 1
#include <stdio.h> int main() { int x; /* variable entera */ int y; /* variable entera */ int *px; /* variable puntero a entero */ x = 5; px = &x; /* asigna a px la direccion de x */ y = *px; /* asigna a y el contenido de la direccion almacenada en px */ printf("x = %d\n", x); printf("y = %d\n", y); printf("*px = %d\n", *px); getch(); return 0; }
12

Ejemplo 2
#include <stdio.h> int main() { int a; // a es un entero int *ptrA; // ptrA es un apuntador a un entero a = 7; ptrA = &a; // a ptrA se le asigna la direccin de a printf("La direccin de a es %d \nEl valor de ptrA es %d", &a, ptrA); printf("\n\nEl valor de a es %d \nEl valor de *ptrA es %d", a, *ptrA);

printf("\n\nMuestra de que * y & son inverso uno de otro\n"); printf("\n *&ptrA = &*ptrA \n%d=%d",*&ptrA ,&*ptrA);
getch(); return 0; } // fin de main

13

Ejercicio
Dado el siguiente fragmento de cdigo:
float n1; float n2; float *p1; float *p2; n1 = 4.0; p1 = &n1; p2 = p1; n2 = *p2; n1 = *p1 + *p2;

Cunto vale n1 y n2?


14

Apuntadores con Arreglos


Es posible manejar aritmtica de apuntadores, siempre y cuando el apuntador este dirigido a un arreglo de cualquier tipo. Es decir, se requiere de posiciones de memoria consecutiva y un tamao uniforme de elementos. La aritmtica consiste en utilizar una sintaxis de apuntador + desplazamiento para indicar la posicin del elemento del arreglo a manejar. Y tambin se pueden sumar o restar valores enteros al apuntador, para cambiar la direccin del apuntador

15

Expresiones y Aritmtica con Apuntadores Aritmtica con apuntadores


Incremento y decremento (++ o --) Sumar o restar un entero ( + o += , - o -=) Los apuntadores se pueden restar uno de otro

Ejemplo: 5 elementos entero de un arreglo, usan 4 bytes cada uno


vPtr apunta al primer elemento v[ 0 ], el cual esta en la direccin 3000
vPtr = 3000

vPtr += 2; hace que vPtr apunte a 3008


vPtr apunta a v[ 2 ]
Ubicacin 3000 3004 3008 3012 3016

v[0]

v[1]

v[2]

v[3]

v[4]

Variable apuntador vPtr

16

Incremento / Decremento
Los operadores ++ y -- actan de modo diferente segn el tipo apuntado por el puntero. Si p es un puntero a caracteres (char *p) la operacin p++ incrementa el valor de p en 1. Si embargo, si p es un puntero a enteros (int *p), la misma operacin p++ incrementa el valor de p en 2 para que apunte al siguiente elemento, pues el tipo int ocupa dos bytes. Del mismo modo, para el tipo float la operacin p++ incrementa el valor de p en 4. Lo dicho para el operador ++ se cumple exactamente igual, pero decrementando, para el operador --.
17

Expresiones y Aritmtica con Apuntadores Restando apuntadores


Para obtener el nmero de elementos entre dos direcciones:
vPtr2 = v[ 2 ]; vPtr = v[ 0 ]; vPtr2 - vPtr == 2

Asignacin de apuntadores
Se pueden asignar (copiar el contenido), slo si son del mismo tipo ambos apuntadores Si no son del mismo tipo, se requiere el uso de casting

Excepcin a la regla: apuntador void


void (type void *)

Es un apuntador genrico, representa cualquier tipo No se requiere casting para convertir un apuntador a un apuntador void El apuntador void no puede ser desreferenciado

18

Comparacin
Pueden compararse punteros del mismo modo que cualquier otra variable, teniendo siempre presente que se comparan direcciones y NO contenidos.
int *p, *q; if (p == q) puts ("p y q apuntan a la misma posicin de memoria");

19

Puntero NULL
Cuando se asigna 0 a un puntero, este no apunta a ningn objeto o funcin. La constante simblica NULL definida en stdio.h tiene el valor 0 y representa el puntero nulo. Es una buena tcnica de programacin asegurarse de que todos los punteros toman el valor NULL cuando no apuntan a ningn objeto o funcin. int *p = NULL; Para ver si un puntero no apunta a ningn objeto o funcin:
if (p == NULL) printf("El puntero es nulo\n"); else printf("El contenido de *p es\n", *p);
20

Relacin entre arreglos y apuntadores


Estn muy vinculados
Un nombre de arreglo es como una apuntador constante Los apuntadores pueden manejar subndices como los arreglos

Accediendo a elementos de un arreglo con apuntadores


El elemento b[ n ] puede ser accedido como *( bPtr + n )
Se le llama Notacin Apuntador/Desplazamiento

La direccin de
&b[ 3 ] es lo misma que bPtr + 3

Los arreglos tambin pueden ser tratados como apuntadores


b[ 3 ] es lo mismo que *( b + 3 )

Los apuntadores pueden utilizar subndices Notacin Apuntador/Subndice


bPtr[ 3 ] es lo mismo que b[ 3 ]

21

Punteros y vectores
El nombre del vector representa la direccin del primer elemento del vector:
float vector[MAX_TAM]; vector == &vector[0]

El nombre del vector es realmente un puntero al primer elemento del vector


&x[0] x &x[1] (x+1) &x[2] (x+2) &x[i] (x+i)

Es decir, &x[i] y (x+i) representan la direccin del i-esimo elemento del vector x => x[i] y *(x+i) representan el contenido del i-esimo elemento del vector x.
22

Ejemplo 1/2
// Uso de subndices y notacin de apuntadores con arreglos. #include <stdio.h>

int main() { int i, j, offset1, offset2; int b[] = { 10, 20, 30, 40 }; int *bPtr = b; // inicializa bPtr para apuntar al arreglo b
// salida del array b usando subndices (notacin arreglo subndice) printf("Array b se imprime como:\n\n"); printf("Notacion Arreglo Subindices\n"); for ( i = 0; i < 4; i++ ) printf("b[%d] = %d \n", i, b[ i ] );

// Salida de arreglo b usando notacin apuntador desplazamiento printf("Notacion apuntador/ desplazamiento\n"); printf("el apuntador es el nombre del arreglo\n"); for ( offset1 = 0; offset1 < 4; offset1++ ) printf("* (b + %d )= %d \n", offset1, *( b + offset1 ) );
23

Ejemplo 2/2
// salida del array b usando subndices sobre bPtr printf("\nNotacion apuntador subindice\n"); for ( j = 0; j < 4; j++ ) printf("bPtr[%d] = %d \n", j, bPtr[ j ] ); printf("\nNotacion apuntador / desplazamiento\n"); // salida del array b usando notacin apuntador/desplazamiento sobre bPtr for ( offset2 = 0; offset2 < 4; offset2++ ) printf("* (bPtr + %d )= %d \n", offset2, *( bPtr + offset2 ) ); getch(); return 0; } // fin main
Salida: Con el arreglo y sus subndices b[0] = 10 b[1] = 20 b[2] = 30 b[3] = 40 Con notacin apuntador/desplazamiento *(b + 0) = 10 *(b + 1) = 20 *(b + 2) = 30 *(b + 3) = 40

Apuntador con notacin de subndices bPtr[0] = 10 bPtr[1] = 20 bPtr[2] = 30 bPtr[3] = 40


Notacin apuntador/desplazamiento *(bPtr + 0) = 10 *(bPtr + 1) = 20 *(bPtr + 2) = 30 *(bPtr + 3) = 40

24

Ejercicio
Hacer un programa que realice operaciones sobre un array utilizando un apuntador llamado aPtr y notacin apuntador desplazamiento. Los valores del arreglo a son: 2.2, 4.4, 6.6, 8.8, 10.
1. 2. 3. 4. 5. 6. 7. 8. 9. Imprimir el tamao de cada uno de los elementos del arreglo en bytes. Asignar al primer elemento el valor 3. Multiplicar el tercer elemento por 2. Restar del cuarto elemento, la suma del primero y el segundo. Imprimir todos los valores del arreglo. Obtener el menor de los nmeros en el arreglo y la direccin de memoria en donde est almacenado. Mover el apuntador al ltimo elemento y modificar su contenido a 0. Obtener el tamao o nmero de elementos del arreglo. Regresa el apuntador a la posicin inicial del arreglo.

Hacer una funcin que imprima todos los valores del arreglo utilizando como parmetro un apuntador a un arreglo y el nmero de elementos del mismo.
Probarla con el ejemplo anterior.
25

Apuntadores a caracteres
// Imprime de la a a la z utilizando un apuntador

#include <stdio.h> int main() { char var1 ; /*una variable del tipo caracter */ char *pchar; /* un apuntador a una variable del tipo caracter */ pchar = &var1 ; /*asignamos al apuntador la direccion de la variable */ for (var1 = 'a'; var1 <= 'z'; var1++) printf("%c", *pchar) ; /* imprimimos el valor de la variable apuntada */ getch(); return 0 ; }
26

Vectores y cadenas de caracteres con apuntadores


Una cadena de caracteres es un vector de caracteres cada elemento del vector almacena un carcter. Ejemplo: Funcin que copia una cadena en otra: void copiar(char *destino, char *fuente) { while (*fuente != '\0') { *destino = *fuente; destino++; fuente++ ; } *destino = '\0'; return; }

Completar el programa para probar la funcin anterior.


27

Arreglos de apuntadores
Un arreglo puede contener apuntadores
Normalmente se usan para almacenar arreglos de cadenas:
char *suit[ 4 ] = {"Hearts", "Diamonds", "Clubs", "Spades" };

Cada elemento de suit es un apuntador a cadena:


char * (un string)

suit[0] suit[1] suit[2] suit[3]

H D C S

e i l p

a a u a

r m b d

t o s e

s n \0 s

\0 d s \0

\0

El arreglo suit tiene un tamao fijo, pero los strings pueden ser de cualquier tamao.
28

Ejercicio
1. Hacer un programa que inicialice el arreglo suit del ejemplo anterior y que despus imprima su contenido. 2. Hacer una funcin que reciba un apuntador a cadena y regrese la longitud de la misma.
Prototipo de la funcin: int mystrlen(char *s);

29

Argumentos de la funcin main()


Ejecutar un programa con parmetros de entrada (en lnea de comandos) main (int argc, char *argv[ ])

argc: Entero que indica el nmero de parmetros tecleados (incluye el nombre del programa). argv[ ]: Matriz de cadenas de caracteres. Cada uno de los elementos argv[i] es una cadena que almacena un argumento.
30

Argumentos de la funcin main()


La variable argc vale 1 como mnimo, puesto que se cuenta el nombre del programa. Los parmetros se identifican mediante argv de la siguiente manera:
argv[0] cadena que almacena el nombre del programa. argv[1] cadena que almacena el primer parmetro. argv[2] cadena que almacena el segundo parmetro. ... ... argv[argc] vale cero (En realidad es un puntero nulo).

Para que los argumentos sean tratados como diferentes tienen que ir separados por uno o varios espacios blancos

31

Ejemplo
Programa que lista los parmetros, si los hay, de la lnea de rdenes. #include <stdio.h> int main (int argc, char *argv[ ]) { register int i; printf ("\nNombre del programa: %s", argv[0]); if (argc == 1) printf ("\nNo se han introducido parmetros"); else { printf ("\nParmetros en la lnea de rdenes: "); for (i = 1; i < argc; i++) printf ("\n%d: %s", i, argv[i]); } return 0; }

32

Ejercicio
Hacer un programa que reciba como argumentos de la funcin main() dos valores y que imprima el resultado de la suma de ambos. Si no recibe el nmero correcto de parmetros que imprima un mensaje de error.

33

Memoria dinmica
Cuando un vector se define como un puntero no se le pueden asignar valores ya que un puntero no reserva espacio en memoria.
float x[10] define un vector compuesto por 10 nmeros reales en este caso SI se reserva espacio para los elementos. float *x declara un puntero a float. No se reserva memoria.

Si se quiere que float x se comporte como un vector habr que reservar memoria para los 10 elementos: x = (float *) malloc(10 * sizeof(float));
debemos indicar el tipo de apuntador que deseamos utilizar (float*)

malloc(nb) (stdlib.h) reserva un bloque de memoria de nb bytes. Para liberar la memoria asignada se utiliza free() (stdlib.h) free(x); El uso de punteros permite definir vectores de forma dinmica.
34

Ejemplo
#include <stdio.h> #include <stdlib.h> #include <string.h> char *p , palabra[20] ; p = (char *)malloc(sizeof(char)*128) ; printf("Escriba su nombre : ") ; scanf("%s" , p ) ; strcpy(palabra , " Como le va " ) ; printf("%s%s" , palabra , p ) ; }
35

Verificar si tuvo xito malloc


Es conveniente averiguar si la peticin de memoria tuvo xito, esto se logra verificando si el apuntador es distinto de NULL o al contrario para indicar un error. if( ( p = (double *)malloc( sizeof(double) ) ) == NULL ) { printf("no hay mas Memoria !!" ) ; exit(1) ; }
36

Ejemplo (1/2)
Programa que calcula la media de un vector de tamao especificado de forma dinmica. #include <stdio.h> #include <stdlib.h> void leer_vector(int vector[], int dim); int media_vector(int vector[], int dim); main() { int *v_numeros; int dimension; int media; printf("Dimension del vector: "); scanf("%d", &dimension); v_numeros = (int *) malloc(dimension*sizeof(int)); leer_vector(v_numeros, dimension); media = media_vector(v_numeros, dimension); printf("La media es %d\n", media); free(v_numeros); }
37

Ejemplo (2/2)
Programa que calcula la media de un vector de tamao especificado de forma dinmica. void leer_vector(int vector[], int dim) { int j; for(j=0; j<dim; j++) { printf("Elemento %d: ", j); scanf("%d", &vector[j]); } return; } int media_vector(int vector[], int dim) { int j; int media = 0; for(j=0; j<dim; j++) media = media + vector[j]; return(media/dim); }
38

Apuntadores como resultado de una funcin


Las funciones que retornan apuntadores son por lo general aquellas que modifican un argumento que les ha sido pasado por direccin ( por medio de un apuntador ) , devolviendo un apuntador a dicho argumento modificado , las que reservan lugar en el Heap para las variables dinmicas, retornando un apuntador a dicho bloque de memoria . As podremos declarar funcines del tipo de:
char *funcion1( char * var1 ) ; double *funcion2(int i , double j , char *k ) ;

El retorno de las mismas puede inicializar apuntadores del mismo tipo al devuelto , distinto , por medio del uso del casting . Algunas funciones, tales como malloc() y calloc() definen su retorno como apuntadores a void : void *malloc( int tamano ) ; de esta forma al invocarlas, debemos indicar el tipo de apuntador que deseamos utilizar: p = (double *) malloc( 64 ) ;

39

Recursos
Ejemplos y ejercicios resueltos sobre apuntadores:
http://wiki.lidsol.net/wiki/index.php?title=Apuntadores_3# asignacion_de_memoria_dinamica_para_estructuras

Tutorial completo en:


http://wiki.lidsol.net/wiki/index.php?title=Tutorial_de_prog ramacion_en_C

40

Вам также может понравиться