Luis hernandez 22310621

22
Apuntadores Y Direcciones De Memoria República Bolivariana de Venezuela Ministerio del Poder Popular Para La Defensa Universidad Nacional Experimental Politécnica De Las Fuerzas Armadas UNEFA Yaracuy Extensión Nirgua Profesor: Luis Sequera Integrante: Luis Hernández Programación I

description

Programacion I- Apuntadores

Transcript of Luis hernandez 22310621

Page 1: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

República Bolivariana de Venezuela

Ministerio del Poder Popular Para La Defensa

Universidad Nacional Experimental Politécnica De Las Fuerzas Armadas

UNEFA –Yaracuy

Extensión Nirgua

Profesor: Luis Sequera

Integrante:

Luis Hernández

Programación I

Page 2: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

Punteros Y Direccionamiento De Memoria

Un puntero es una variable que contiene una dirección de memoria.

Normalmente, esa dirección es la posición de otra variable de memoria. Si una variable

contiene la dirección de otra variable, entonces se dice que la primera variable apunta a

la segunda.

Si una variable va a contener un puntero, entonces tiene que declararse como tal.

Una declaración de un puntero consiste en un tipo base, un * y el nombre de la variable.

La forma general es: tipo *nombre;

Donde tipo es cualquier tipo válido y nombre es el nombre de la variable

puntero. El tipo base del puntero define el tipo de variables a las que puede apuntar.

Técnicamente, cualquier tipo de puntero puede apuntar a cualquier dirección de la

memoria, sin embargo, toda la aritmética de punteros esta hecha en relación a sus tipos

base, por lo que es importante declarar correctamente el puntero.

Existen dos operadores especiales de punteros: & y *. El operador de dirección

(&) devuelve la dirección de memoria de su operando. El operador de indirección (*)

devuelve el contenido de la dirección apuntada por el operando.

Después de declarar un puntero, pero antes de asignarle un valor, éste contiene

un valor desconocido; si en ese instante lo intenta utilizar, probablemente se estrellará,

no sólo el programa sino también el sistema operativo. Por convenio, se debe asignar el

valor nulo a un puntero que no este apuntando a ningún sitio, aunque ésto tampoco es

seguro.

Page 3: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

Asignación de punteros

Como en el caso de cualquier otra variable, un puntero puede utilizarse a la

derecha de una declaración de asignación para asignar su valor a otro puntero. Por

Ejemplo:

int x;

int *p1,*p2;

p1=&x;

p2=p1;

Tanto p1 como p2 apuntan a x.

Aritmética de Punteros

Existen sólo dos operaciones aritméticas que se puedan usar con punteros: la

suma y la resta.

Cada vez que se incrementa un puntero, apunta a la posición de memoria del

siguiente elemento de su tipo base. Cada vez que se decrementa, apunta a la posición

del elemento anterior. Con punteros a caracteres parece una aritmética normal, sin

embargo,el resto de los punteros aumentan o decrecen la longitud del tipo de datos a los

que apuntan.

Por ejemplo, si asumimos que los enteros son de dos bytes de longitud y p1 es

un puntero a entero con valor actual 2000. Entonces, después de la expresión p1++; p1

contiene el valor 2002, no 2001.

No pueden realizarse otras operaciones aritméticas sobre los punteros más allá

de la suma y resta de un puntero y un entero. En particular, no se pueden multiplicar o

dividir punteros y no se puede sumar o restar el tipo float o el tipo double a los punteros.

Page 4: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

Punteros Y Arrays

Existe una estrecha relación entre los punteros y los arrays. Considérese el

siguiente fragmento:

char cad[80], *p1;

p1=cad;

Aquí, p1 ha sido asignado a la dirección del primer elemento del array cad. Para

acceder al quinto elemento de cad se escribe cad[4] o *(p1+4).

Un nombre de array sin índice devuelve la dirección de comienzo del array, que

es el primer elemento. El compilador traduce la notación de arrays en notación de

punteros.

Es decir, al crear un array se genera un puntero (en realidad una constante de

puntero) con el mismo nombre que apunta a la dirección del primer elemento del array.

Arrays de Punteros

Los punteros pueden estructurarse en arrays como cualquier otro tipo de datos.

La declaración, por ejemplo, para un array de punteros a enteros de tamaño 10 es: int

*x[10];

Para asignar la dirección de una variable entera llamada var al tercer elemento

del array de punteros se escribe:x[2]=&var;

Se puede encontrar el valor de var de la forma: var de la forma: *x[2];Si se

quiere pasar un array de punteros a una función, se puede utilizar el mismo método que

se utiliza para otros arrays: llamar simplemente a la función con el nombre del array sin

índices. Así se pasa el puntero que apunta al array.

No se pasa un puntero a enteros, sino un puntero a un array de punteros a

enteros.

Page 5: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

Indirección Múltiple

Se puede hacer que un puntero apunte a otro puntero que apunte a un valor de

destino. Esta situación se denomina indirección múltiple o punteros a punteros. Una

variable que es puntero a puntero tiene que declararse como tal. Esto se hace colocando

un * adicional en frente del nombre de la variable. Por ejemplo, la siguiente declaración

inicial indica al compilador que ptr es un puntero a puntero de tipo float:float **ptr;

Funciones de Asignación Dinámica

Los punteros proporcionan el soporte necesario para el potente sistema de

asignación dinámica de memoria de C. La asignación dinámica es la forma en la que un

programa puede obtener memoria mientras se está ejecutando.

Como ya se ha visto, a las variables globales se les asigna memoria en tiempo de

compilación y las locales usan la pila. Sin embargo, durante la ejecución no se pueden

añadir variables globales o locales, pero existen ocasiones en las que un programa

necesita usar cantidades de memoria variables.

El centro del sistema de asignación dinámica está compuesto por las funciones

(existentes en la biblioteca stdlib.h) malloc(), que asigna memoria; y free() que la

devuelve.

El prototipo de la función malloc() es: stdlib.h) malloc(), que asigna memoria; y

free() que la devuelve.void *malloc(size_t número de bytes);

Tras una llamada fructífera, malloc() devuelve un puntero, el primer byte de

memoria dispuesta. Si no hay suficiente memoria libre para satisfacer la petición de

malloc(), se da un fallo de asignación y devuelve un nulo. El fragmento de código que

sigue asigna 1000 bytes de memoria:

Page 6: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

char *p;

p = (char *) malloc(1000);

Después de la asignación, p apunta al primero de los 1000 bytes de la memoria

libre. El siguiente ejemplo dispone espacio para 50 enteros. Obsérvese el uso de sizeof

para asegurar la portabilidad: p apunta al primero de los 1000 bytes de la memoria libre.

El siguiente ejemplo dispone espacio para 50 enteros. Obsérvese el uso de sizeof para

asegurar la portabilidad:

int *p;

p= (int *) malloc(50*sizeof(int));

La función free() es la opuesta de malloc() porque devuelve al sistema la

memoria previamente asignada. Una vez que la memoria ha sido liberada, puede ser

reutilizada en una posterior llamada a malloc(). El prototipo de la función free() es:

void free (void *p);

free(p);

Tipo De Punteros

Puntero genérico: es aquel que no apunta a ningún tipo de dato

void *nombrepuntero;

Se declaran así para que posteriormente se les pueda hacer apuntar a cualquier tipo de

dato.

Puntero nulo: es aquel que no apunta a ningún dato

tipodato *nombrepuntero = NULL;

NULL es una constante definida en stdio.h. Se utiliza para indicar situaciones de error

Page 7: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

Puntero constante: es aquel que se declara como tal y, por tanto, siempre apunta a

la misma posición

tipodato *const nombrepuntero;

El contenido, el dato apuntado, si puede cambiar. Si es el dato lo que se declara

como constante se escribe

const tipodato *nombrepuntero;

Si el puntero y el dato al que apunta se declaran constantes

const tipodato *const nombrepuntero

PUNTEROS A STRINGS

No hay gran diferencia entre el trato de punteros a arrays , y a strings , ya que

estos dos últimos son entidades de la misma clase . Sin embargo analicemos algunas

particularidades . Así como inicializamos un string con un grupo de caracteres

terminados en '\0' , podemos asignar al mismo un puntero :

p = "Esto es un string constante " ;

Esta operación no implica haber copiado el texto , sino sólo que a p se le ha

asignado la dirección de memoria donde reside la "E" del texto . A partir de ello

podemos manejar a p como lo hemos hecho hasta ahora . Veamos un ejemplo

#include

#define TEXTO1 "¿ Hola , como "

#define TEXTO2 "le va a Ud. ? "

Page 8: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

main()

{

char palabra[20] , *p ;

int i ;

p = TEXTO1 ;

for( i = 0 ; ( palabra[i] = *p++ ) != '\0' ; i++ ) ;

p = TEXTO2 ;

printf("%s" , palabra ) ;

printf("%s" , p ) ;

return 0 ;

}

Definimos primero dos strings constantes TEXTO1 y TEXTO2 , luego

asignamos al puntero p la dirección del primero , y seguidamente en el FOR copiamos

el contenido de éste en el array palabra , observe que dicha operación termina cuando el

contenido de lo apuntado por p es el terminador del string , luego asignamos a p la

dirección de TEXTO2 y finalmente imprimimos ambos strings , obteniendo una salida

del tipo : " ¿ Hola , como le va a UD. ? " ( espero que bien ) .

Reconozcamos que esto se podría haber escrito más compacto, si hubieramos

recordado que palabra tambien es un puntero y NULL es cero , así podemos poner en

vez del FOR

while( *palabra++ = *p++ ) ;

Page 9: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

Vemos que aquí se ha agregado muy poco a lo ya sabido , sin embargo hay un

tipo de error muy frecuente , que podemos analizar , fíjese en el EJEMPLO siguiente , ¿

ve algun problema ? .

( CON ERRORES )

#include

char *p , palabra[20] ;

printf("Escriba su nombre : ") ;

scanf("%s" , p ) ;

palabra = "¿ Como le va " ;

printf("%s%s" , palabra , p ) ; }

Pues hay dos errores , a falta de uno , el primero ya fue analizado antes , la

expresión es correcta pero , el error implícito es no haber inicializado al scanf("%s" , p )

puntero p , el cual sólo fué definido , pero aun no apunta a ningun lado válido . El

segundo error está dado por la expresión : palabra = " ¿ Como le va " ; ( también visto

anteriormente ) ya que el nombre del array es una constante y no puede ser asignado a

otro valor .

¿Como lo escribiríamos para que funcione correctamente ?

(CORRECTO)

#include

#include

Page 10: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

#include

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 ) ; }

Observe que antes de scanf() se ha inicializado a p, mediante el retorno de

malloc() y a al array palabra se le copiado el string mediante la función vista

anteriormente.

Debemos aclarar también que, la secuencia de control %s en el printf() impone

enviar a la pantalla un string, estando éste apuntado por el argumento siguiente al

control, éste puede ser tanto el nombre de un array, como un puntero, ya que ambos

explicitan direcciones.

Una forma alternativa de resolverlo , sería:

#include

main()

{

char p[20] , *palabra ;

printf("Escriba su nombre : ") ;

Page 11: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

scanf("%s" , p ) ;

palabra = "¿ Como le va " ;

printf("%s%s" , palabra , p ) ; }

Obsérvese , que es idéntico al primero , con la salvedad que se ha invertido las

declaraciones de las variables , ahora el puntero es palabra y el array es p . Ambas

soluciones son equivalentes y dependerá del resto del programa , cual es la mejor

elección .

PUNTEROS Y FUNCIONES

La relación entre los punteros y las funciones , puede verse en tres casos

distintos , podemos pasarle a una función un puntero como argumento (por supuesto si

su parámetro es un puntero del mismo tipo ) , pueden devolver un puntero de cualquier

tipo , como ya hemos visto con malloc() y calloc() , y es posible también apuntar a la

dirección de la función , en otras palabras , al código en vez de a un dato.

PUNTEROS COMO PARAMETROS DE FUNCIONES .

Supongamos que hemos declarado una estructura , se puede pasar a una función

de la siguiente manera:

struct conjunto {

int a ;

double b ;

char c[5] ;

Page 12: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

} datos ;

void una_funcion( struct conjunto datos );

Hicimos notar, en su momento, que en este caso la estructura se copiaba en el

stack y así era pasada a la función, con el peligro que esto implicaba, si ella era muy

masiva, de agotarlo.

Otra forma equivalente es utilizar un puntero a la estructura :

struct conjunto {

int a ;

double b ;

char c[5] ;

} *pdatos ;

void una_funcion( struct conjunto *pdatos ) ;

Con lo que sólo ocupo lugar en el stack para pasarle la dirección de la misma.

Luego en la función, como todos los miembros de la estructuras son accesibles por

medio del puntero, tengo pleno control de la misma.

Un ejemplo de funciones ya usadas que poseen como parámetros a punteros son:

scanf(puntero_a_string_de_control , punteros_a_variables)

printf(puntero_a_string_de_control , variables )

En ambas vemos que los strings de control son , como no podría ser de otro

modo , punteros , es decir que los podríamos definir fuera de la función y luego

pasarselos a ellas :

Page 13: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

p_control = "valor : %d " ;

printf( p_control , var ) ;

PUNTEROS COMO RESULTADO DE UNA FUNCION

Las funciones que retornan punteros son por lo general aquellas que modifican

un argumento, que les ha sido pasado por dirección ( por medio de un puntero ) ,

devolviendo un puntero a dicho argumento modificado , ó las que reservan lugar en el

Heap para las variables dinámicas , retornando un puntero a dicho bloque de memoria .

Así podremos declarar funciónes del tipo de:

char *funcion1( char * var1 ) ;

double *funcion2(int i , double j , char *k ) ;

struct item *funcion3( struct stock *puntst ) ;

El retorno de las mismas puede inicializar punteros del mismo tipo al devuelto ,

ó distinto , por medio del uso del casting . Algunas funciones , tales como malloc() y

calloc() definen su retorno como punteros a void :

void *malloc( int tamano ) ;

De esta forma al invocarlas , debemos indicar el tipo de puntero de deseamos

p = (double *)malloc( 64 ) ;

Page 14: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

Ejercicio 1:

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef struct agenda_telefonica

{

char _nombre[25];

char _apellido[25];

char _numero[12];

struct agenda_telefonica *siguiente_registro;

}tipo_agenda;

typedef tipo_agenda *tipo_nodo,*tipo_lista;

int opcion,posicion,resp,i;

char nombre[25],apellido[25],numero[12];

void insertar(tipo_lista *r_lista,char r_nombre[],char r_apellido[],char

r_numero[]);

void borrar(tipo_lista *r_lista,char r_nombre[]);

void mostrar(tipo_lista *lista);

int main()

{clrscr();

tipo_lista lista = NULL;

do

{

printf("\n\n\n\n\n\t\t\t! M E N U !\n\n");

printf("\t\t1) Nuevo Registro\n\n");

printf("\t\t2) Mostrar Agenda Telefonica\n\n");

printf("\t\t3) Borrar Registro\n\n");

printf("\t\t4) Cerrar el programa\n\n");

printf("\t\t----> OPCION [1-4]: ");

Page 15: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

scanf("%i",&opcion);

system("clear");

switch(opcion)

{

case 1:

printf("\n\n\n\n\n\t\t\t! NUEVO REGISTRO

!\n\n");

printf("\tNombre : ");

scanf("%s",nombre);

printf("\n\n\tApellido : ");

scanf("%s",apellido);

printf("\n\n\tNumero movil :");

scanf("%s",numero);

system("clear");

printf("\n\n\n\n\n\t\t\t! NUEVO REGISTRO

!\n\n");

printf("\tNombre\t\tApellido\t\tNumero\n\n\t%s\t\t%s\t\t%s\n",nombre,

apellido,numero);

printf("\t-----------------------------------------------");

printf("\n\n\t\t\tDesea Guardar este

registro?\n\t\t\t1) Si\n\t\t\t2) No\n\t\t\tOPCION: ");

scanf("%i",&resp);getchar();

if (resp==1)

{

insertar(&lista,nombre,apellido,numero);

printf("\n\n\t\tSu Registro ha sido

guardado exitosamente\n");

getchar();

system("clear");

}

Page 16: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

else

{

printf("\n\n\t\tEl registro no

fue guardado");

getchar();

system("clear");

}

break;

case 2:

printf("\n\n\n\n\t\t ***** A G E N D A

*****\n");

mostrar(&lista);getchar();

getchar();

system("clear");

break;

case 3:

mostrar(&lista);

printf("\n\n\t\tIntroduzca el nombre a

BUSCAR : ");

scanf("%s",nombre);

borrar(&lista,nombre);

}

}while(opcion<4);

printf("\t\tCreado por : !Luis Hernandez!\n\n");

getchar();

return 0;

}

void insertar(tipo_lista *r_lista,char r_nombre[],char r_apellido[],char

r_numero[])

{

tipo_nodo primer,ultimo,anterior;

primer = *r_lista;

if (primer == NULL)

Page 17: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

{

primer == malloc(sizeof(tipo_agenda));

if(primer != NULL)

{

strcpy(primer->_numero,r_numero);

strcpy(primer->_nombre,r_nombre);

strcpy(primer->_apellido,r_apellido);

primer->siguiente_registro = NULL;

*r_lista = primer;

}

}

else

{

ultimo == malloc(sizeof(tipo_agenda));

if (ultimo != NULL)

{

strcpy(ultimo->_numero,r_numero);

strcpy(ultimo->_nombre,r_nombre);

strcpy(ultimo->_apellido,r_apellido);

if(strcmp((*r_lista)->_nombre,ultimo-

>_nombre)>0)

{

ultimo->siguiente_registro = *r_lista;

*r_lista = ultimo;

}

else

{

anterior = *r_lista;

Page 18: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

while (anterior-

>siguiente_registro && strcmp(anterior->siguiente_registro-

>_nombre,ultimo->_nombre)<0)

anterior = anterior-

>siguiente_registro;

ultimo->siguiente_registro =

anterior->siguiente_registro;

anterior->siguiente_registro

= ultimo;

}

}

}

}

void mostrar(tipo_lista *lista)

{

tipo_nodo nodo_muestra=*lista;

printf("\t\t__________________________________\n");

while(nodo_muestra != NULL)

{

printf("\t\t|Nombre : | %s\n\t\t|Apellido : | %s\n\t\t|Numero :

| %s\n",nodo_muestra->_nombre,nodo_muestra->_apellido,nodo_muestra-

>_numero);

printf("\t\t|________________________________\n");

nodo_muestra = nodo_muestra->siguiente_registro;

}

}

void borrar(tipo_lista *r_lista,char r_nombre[])

{

tipo_nodo nodo,anterior;

nodo= *r_lista;

Page 19: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

anterior = NULL;

while(nodo && strcmp(nodo->_nombre,r_nombre)<0)

{

anterior = nodo;

nodo = nodo->siguiente_registro;

}

if(!nodo || strcmp(nodo->_nombre,r_nombre)!=0)

{

printf("\n\n\t\t\"%s\" no se encuentra en la

lista\n\n",r_nombre);getchar();

getchar();

system("clear");

return;

}

else {getchar();

/* Borrar el nodo */

if(!anterior) /* Primer elemento */

*r_lista = nodo->siguiente_registro;

else /* un elemento cualquiera */

anterior->siguiente_registro = nodo-

>siguiente_registro;

free(nodo);

printf("\n\t\t\"%s\" ha sido eliminado de la

lista",r_nombre);

getchar();

system("clear");

}

}

Page 20: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

Ejercicio 2:

#include <conio.h>

#include <stdio.h>

#define N_VENDEDORES 18

#define N_PRODUCTOS 10

/* Función que muestra el menú del programa y retorna

el número de menú elegido por el usuario */

int mostrarMenu();

/* Función que calcula el total de todas las ventas*/

double mostrarTotal(double v[][N_PRODUCTOS]);

/* Función que calcula el total de ventas de un vendedor*/

double totalVendedor(double v[][N_PRODUCTOS], int nVendedor);

int main(){

clrscr();

double ventas[N_VENDEDORES][N_PRODUCTOS]={0};

int resp=mostrarMenu();

int nvend, nprod;

double cantidad;

while(resp!=4){

switch(resp){

case 1:

printf("Numero de vendedor: ");scanf("%d",&nvend);

printf("Numero de producto: ");scanf("%d",&nprod);

printf("Cantidad ventida: ");scanf("%lf",&cantidad);

ventas[nvend][nprod]=cantidad;

break;

Page 21: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

case 2:

printf("Numero de vendedor: ");scanf("%d",&nvend);

printf("Ventas total del vendedor %d=%.2lf\n",

nvend,totalVendedor(ventas,nvend));

break;

case 3:

printf("Total de ventas=%.2lf\n",

mostrarTotal(ventas));

break;

}

resp=mostrarMenu();

}

getch();

}

int mostrarMenu(){

int resp;

printf("\n\n");

printf("1) A¤adir datos\n");

printf("2) Mostrar total de vendedor\n");

printf("3) Mostrar total de ventas\n");

printf("4) Salir\n");

scanf("%d",&resp);

return resp;

}

double mostrarTotal(double v[][N_PRODUCTOS]){

double resp=0;

int i,j;

for(i=0;i<N_VENDEDORES;i++){

for(j=0;j<N_PRODUCTOS;j++){

resp+=v[i][j];

}

}

return resp;

Page 22: Luis hernandez   22310621

Apuntadores Y Direcciones De Memoria

}

double totalVendedor(double v[][N_PRODUCTOS], int nVendedor){

double resp=0;

int i;

for(i=0;i<N_PRODUCTOS;i++){

resp+=v[nVendedor][i];

}

return resp;

}