Luis hernandez 22310621
-
Upload
luis-miguel -
Category
Education
-
view
183 -
download
2
description
Transcript of 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
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.
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.
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.
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:
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
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. ? "
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++ ) ;
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
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 : ") ;
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] ;
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 :
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 ) ;
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]: ");
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");
}
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)
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;
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;
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");
}
}
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;
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;
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;
}