Upload
marielacuriel
View
1.080
Download
3
Embed Size (px)
DESCRIPTION
Fundamentos de Programación en C, última parte.
Citation preview
Programación en C (III Parte)
09/04/2023
Realizado por Mariela CurielUniversidad Simón BolívarJunio, 2011
Contenido
ApuntadoresManejo de memoria dinámica.
Listas Línea de comandos del main()Entrada/Salida
09/04/2023 2
Contenido
FuncionesApuntadoresManejo de memoria dinámica.
Listas Línea de comandos del MainEntrada/Salida
09/04/2023 3
Apuntadores
Los apuntadores en el Lenguaje C, son variables que poseen la dirección de la ubicación en memoria de otras variables.
Declaración de un apuntador:tipo de variable apuntada *nombre_apunt;int *pint ;double *pfloat ;char *letra , *codigo , *caracter ;
Apuntadores
int x; /* se crea en memoria una variable entera */
int *p; /* se declara un apuntador que contendrá la dirección de una variable entera */
p = &x; /* al apuntador se le asigna la dirección de la variable entera (x) */
Apuntadores
int c, ...;int *p;
c = 63;p = &c;
0481216
202428
c: 63
p:4
Apuntadores
int c, ...;int *p;
c = 63;
0481216
202428
c: 63
p:
Apuntadores
int c,…;int *p;
c = 63;p = &c;*p = 40;
0481216
202428
c: 40
p:4
Apuntadores
IMPORTANTE: Cuando se declara un apuntador no apunta a ningún lugar. Es necesario inicializarlo antes de usarlo.
int *p; *p = 100; /* error */ El uso correcto es: int *p, x; p = &x; // se inicializa el apuntador
*p = 100; // se está dando un valor a lo apuntado por p.
Apuntadores
El símbolo & , se puede aplicar a variables, funciones , etc , pero no a constantes o expresiones.
Para hallar el valor apuntado se utiliza el operador (*). Así, son equivalentes:y = x ; y = *p; // tomando en cuenta la asignación de la lámina anterior.printf("%d" , x ) ;printf("%d" , *p) ;
Operaciones sobre Apuntadores
Incremento de apuntadores
int *dir;
....
dir + 1; /* Ok */
dir += 10; /* incrementa dir para apuntar 10 elementos. mas adelante */
Apuntadores
Cuando se incrementa un apuntador se incrementa en un bloque de memoria (depende del tipo). Ejem:
p++;
◦Un apuntador a carácter sumará un byte a la dirección.
◦Un apuntador a entero o float sumará 4 bytes a la dirección
Operaciones sobre Apuntadores
Comparación de apuntadores
Sólo se podrán comparar apuntadores del mismo tipo.int t[10];int *p;for (p=t; p < t+10;p++) *p = 1;
Operaciones sobre Apuntadores
Resta de apuntadores: la diferencia proporciona el número de elementos del tipo en cuestión, situados entre las dos direcciones.
int strlen(char *s){ char *p=s; while (*p != ‘\0’) p++; return p - s; }
Operaciones sobre Apuntadores
Asignaciones y el apuntador NULL
int *p, *t, a[10];p = a:t = p;p = NULL;
Apuntadores y Arreglos
El nombre de un arreglo , para el compilador C , es un APUNTADOR inicializado con la dirección del primer elemento del arreglo.
float var,conjunto[]={8.0,7.0,6.0,5.0);float *pf; pf = conjunto; /* equivale a hacer pf = &conjunto[0]*/ var1 = *pf; *pf = 25.1;
Apuntadores y Arreglos
Recuperar un elemento de un arreglo usando apuntadores:
int *pa, a[10];
pa = a; /* equivale a pa = &a[0]*/
también ....
x = *(pa + i) equivale a x = a[i]
Apuntadores y Arreglos
Un arreglo como apuntador:
a[i] puede escribirse como *(a + i). i.e. &a[i] = a + i.
El apuntador lo podemos tratar como arreglo:
pa[i] en lugar de *(pa + i).
Apuntadores y Arreglos
Sin embargo los apuntadores y arreglos son diferentes:
int a[10], *pa;
◦Un apuntador es una variable. Nosotros podemos hacer:
pa = a y pa++
◦Un arreglo no es una variable. Las instrucciones
a = pa y a++ SON ILEGALES.
Apuntadores y Arreglos
ASIGNACIONES ERRONEAS
int conjunto[3], lista[] = {5,6,7};
int *apuntador ;
apuntador = lista ; /* correcto */
conjunto = apuntador; /* ERROR */
lista = conjunto ; /* ERROR */
apuntador = &conjunto /* ERROR no se puede aplicar el operador “&” a una constante */
Arreglos de Apuntadores
char *flecha; define un apuntador a un caracter.
char *car[5]; define un arreglo de 5 apuntadores a caracteres.
Los arreglos de apuntadores pueden inicializarse de la misma forma que un arreglo común:
char *months = {``no month'', ``jan'', ``feb”, ...};
Apuntadores a Estructuras
Los apuntadores pueden servir para el manejo de estructuras, y su alojamiento dinámico.
Tienen además la propiedad de poder direccionar a los miembros de las mismas utilizando el operador (->) .
struct conjunto {
int a ;double b ;char c[5] ;} stconj ;stconj.a = 10 ;stconj.b = 1.15 ;stconj.c[0] = 'A' ;
Con apuntadores:
struct conjunto *pconj ;
pconj = (struct conjunto *)malloc( sizeof( struct conjunto )) ;
pconj->a = 10 ;
pconj->b = 1.15 ;
pconj->c[0] = 'A' ;
APUNTADORES COMO PARAMETROS DE FUNCIONES
Una estructura, se puede pasar a una función como argumento:struct conjunto {int a ;double b ;char c[5] ;} datos ; //declaración de la variable datos
void funcion( struct conjunto); //prototipo
. . .
funcion(datos); // llamada
APUNTADORES COMO PARAMETROS DE FUNCIONES
Otra forma equivalente es utilizar un apuntador a la estructura
struct conjunto {
int a ;
double b ;
char c[5] ;
} a; // declaración de la variable “a”
void una_funcion( struct conjunto *); //prototipo
una_funcion(&a); //llamada
APUNTADORES COMO RESULTADO DE UNA FUNCION
Podemos declarar funciones que devuelven apuntadores a un tipo de
datos:
char *funcion1(char * var1 ) ;
double *funcion2(int i, double j, char *k ) ;
struct item *funcion3( struct stock *puntst ) ;
APUNTADORES COMO RESULTADO DE UNA FUNCION
El retorno de las mismas puede inicializar apuntadores del mismo tipo al que se devuelve o de un tipo distinto.
void *malloc(int tamaño) ;
p = (double *)malloc( 64 ) ;
Apuntadores y Funciones
Cuando en el lenguaje C se pasan argumentos a las funciones, el pasaje de parámetros es por valor.
Los apuntadores se utilizan para realizar el pasaje de parámetros por referencia.
int a,b;
swap(a, b) /* NO FUNCIONA */.
Apuntadores y Funciones
Es necesario pasar como parámetro la dirección de las variables para que éstas puedan ser modificadas por la función
swap(&a, &b) /* Llamada */
. . .
void swap(int *px, int *py){
int temp; temp = *px /* lo apuntado por p*/ *px = *py; *py = temp;
}
APUNTADORES COMO PARAMETROS DE FUNCIONES
void una_funcion( struct conjunto *p);
…..
struct conjunto {
int a ;
double b ;
char c[5] ;
} a;
una_funcion(&a);
Prototipo
Declaración de la variable a
Llamada con la dirección de la variable
Apuntadores y Funciones
Cuando un arreglo se pasa a una función como parámetro lo que realmente se está pasando esla ubicación en memoria de su primer elemento:strlen(s) equivale strlen(&s[0])
La declaración de la función es:
int strlen(char s[]) ;
Y una declaración equivalente es :
int strlen(char *s);
porque char s[] es similar a char *s. llamada
Apuntadores y Funciones
strlen() es una función de la librería estándar que retorna la longitud de un string:
int strlen(char *s) { char *p = s; while (*p != `\0’);
p++; return p-s; }
llamada: char nombre[10]; strlen(nombre);
Arreglos Multidimensionales y Apuntadores
Un arreglo de dos dimensiones es realmente un arreglo de una dimensión donde cada elemento es en sí mismo un arreglo. De ahí la notación: a[n][m].
Los elementos del arreglo se almacenan por filas.
Cuando se pasa un arreglo de dos dimensiones a una función, se pasa el número de columnas, -- el número de filas es irrelevante.
Arreglos Multidimensionales y Apuntadores
Considere la siguiente matriz:
int a[5][35]
Para pasarla como argumento a una función, ésta se declara como:
f(int a[5][35]) {.....}
f(int a[][35]) {.....}
o incluso:
f(int (*a)[35]) {.....}
Arreglos Multidimensionales y Apuntadores
int (*a)[35]; declara un apuntador a un arreglo de 35 enteros.
int *a[35]; declara un arreglo de 35 apuntadores a enteros.
Arreglos Multidimensionales y Apuntadores
char *name[10]; char Aname[10][20]; Las instrucciones name[3][4] y Aname[3][4] son correctas en C.
Sin embargo
· Aname is un arreglo de 2D de 200 elementos.
· name tiene 10 elementos que son apuntadores no inicilizados.La ventaja es que los vectores de cada fila pueden ser de longitudes diferentes.
Memoria Dinámica y estructuras de datos
dinámicas
09/04/2023
Memoria Dinámica
La Funcción malloc se usa para obtener una porción contigua de memoria.
void *malloc(size_t number_of_bytes)
Retorna un apuntador del tipo void *.Si la memoria no se puede asignar retorna NULL
El tipo del argumento (size_t) esta definido en stdlib.h y es un tipo unsigned.
Memoria Dinámica
char *cp;cp = malloc(100);
La instrucción anterior solicita al sistema operativo 100 bytes consecutivos y asigna la dirección de comienzo de este bloque a al apuntador cp.
Memoria Dinámica
Es común usar la función sizeof() para especificar el número de bytes de un determinado tipo o estructura de datos.
int *ip;ip = (int *) malloc(100*sizeof(int));
Memoria Dinámica
La función sizeof puede usarse para encontrar el tamaño de cualquier tipo de datos, variable o estructura.
int i;struct COORD {float x,y,z};typedef struct COORD PT; sizeof(int), sizeof(i),sizeof(struct COORD) ysizeof(PT) son todos válidos
ip = (int *)malloc(100*sizeof(int));
Aquí haremos uso de la relación entre apuntadores y arreglos y trataremos la memoria reservada como un arreglo.
En lugar de
*ip = 100, podemos hacer:
ip[0] = 100;
o
for(i=0;i<100;++i) printf("%d",ip[i]);
Memoria Dinámica
Cuando se termina de usar una porción de memoria se debe liberar usando la función free().
free() toma un apuntador como argumento y libera la memoria direccionada por el apuntador.
Creación de Estructuras de datos dinámicas
Queremos almacenar 3 enteros, en una estructura de datos dinámica (lista).
Mostraremos cómo se hace de una forma sencilla:1. Primero crearemos tres variables de un
determinado tipo (struct list). Dichas variables contendrán el valor entero a almacenar y un apuntador inicializado en NULL.
2. Luego colocaremos el apuntador de una variable a “apuntar” a la siguiente.
09/04/2023
Estructuras autoreferenciadas
struct list{int data;struct list *next;};struct list a, b, c;a.data = 1;b.data = 2;c.data = 3;a.next = b.next = c.next = NULL;
a
b
c
1
2
3
NULL
NULL
NULL
Paso 1Graficamente.
Estructuras autoreferenciadas
a.next = &b;b.next = &c;
a b c
1 2 3 NULL
a.next -> data es 2a.next -> next -> data es 3
Paso 2
Otro Ejemplo
Ahora, cada elemento de la lista lo crearemos dinámicamente según se necesite.
Utilizaremos typedef, para crear tipos de datos que permiten una programación más ellegante.
Esta vez, cada elemento de la lista almacenará un carácter y el apuntador al próximo elemento de la lista.
09/04/2023
Estructuras autoreferenciadas
// Definición de tipos. Archivo list.h
#define NULL 0
typedef char DATA;struct linked_list{DATA d;struct linked_list *next;};typedef struct linked_list ELEMENT;typedef ELEMENT *LINK;
Estructuras autoreferenciadas
El programa de la siguiente lámina recibe como parámetro un arreglo de caracteres y los coloca en una lista.
Las variables head y tail apuntan en todo momento a la cabeza y cola de la lista respectivamente. Al comienzo apuntan a NULL.
09/04/2023
#include “list.h”LINK s_to_l (char s[]) {
LINK head = NULL, tail=NULL;int i;if (s[0] != ‘\0’) { head = (LINK) malloc(sizeof(ELEMENT));head -> d = s[0];tail = head;for (i = 1; s[i] != ‘\0’, ++i) { tail ->next = (LINK) malloc(sizeof(ELEMENT)); tail = tail->next; tail -> d = s[i];}tail ->next = NULL;return(head);
} }
Se recibe como parámetro El string “AB”
Ahead
tail
?
#include “list.h”LINK s_to_l (char s[]) {
LINK head = NULL, tail=NULL;int i;if (s[0] != ‘\0’) { head = (LINK) malloc(sizeof(ELEMENT));head -> d = s[0];tail = head;for (i = 1; s[i] != ‘\0’, ++i) { tail ->next = (LINK) malloc(sizeof(ELEMENT)); tail = tail->next; tail -> d = s[i];}tail ->next = NULL;return(head);
} }
Ahead
tail
?
head
tail
A B ?
LINK head = NULL, tail=NULL;int i;if (s[0] != ‘\0’) { head = (LINK) malloc(sizeof(ELEMENT));head -> d = s[0];tail = head;for (i = 1; s[i] != ‘\0’, ++i) { tail ->next = (LINK) malloc(sizeof(ELEMENT)); tail = tail->next; tail -> d = s[i];}tail ->next = NULL;return(head);
} }
head
tail
A B NULL
LINK head = NULL, tail=NULL;int i;if (s[0] != ‘\0’) { head = (LINK) malloc(sizeof(ELEMENT));head -> d = s[0];tail = head;for (i = 1; s[i] != ‘\0’, ++i) { tail ->next = (LINK) malloc(sizeof(ELEMENT)); tail = tail->next; tail -> d = s[i];}tail ->next = NULL;return(head);
} }
Argumentos del Main
Para poder tener acceso a los argumentos
de la línea de comandos el main se debe definir de la siguiente forma:
main(int argc, char **argv) · argc es el número total de argumentos
incluyendo el nombre de programa. · argv es un arreglo de strings. Cada posición del
arreglo contiene un argumento.
Línea de Comandos
#include<stdio.h>
main (int argc, char **argv) {
/* programa que imprime los argumentos de la línea de comandos */
int i;
printf(``argc = %d\n'',argc);
for (i=0;i<argc;++i)
printf(``argv[%d]:%s\n'',i,argv[i]);
}
Línea de Comandos
Si se invoca el programa (argum) con los siguientes argumentos:
argum f1 “f2” f3 4 stop!
La salida será:argc = 6argv[0] = argumargv[1] = f1argv[2] = f2argv[3] = f3argv[4] = 4argv[5] = stop!
Los caracteres “” se ignoran.
Bibliografía
Brian Kernighan y Dennis Ritchie. El Lenguaje de Programación C. Prentice Hall.
http://www.its.strath.ac.uk/cources/c
09/04/2023 58