Programación en c (iii parte)

Post on 28-Nov-2014

1.070 views 3 download

description

Fundamentos de Programación en C, última parte.

Transcript of Programación en c (iii parte)

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