Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Estrucutra de Datos y Algoritmos en C PDF
Estrucutra de Datos y Algoritmos en C PDF
Capítulo 2.
Se desea conocer las técnicas para diseñar estructuras de datos. Se repasan los conceptos de
tipos básicos, para luego desarrollar las herramientas de creación de nuevas estructuras,
haciendo énfasis en los conceptos de agrupación y vinculación. Dando ejemplos que
posteriormente se emplearán en el texto.
Para los tipos primitivos existen numerosos y variados operadores que permiten construir
expresiones con variables y constantes de dichos tipos. En determinados casos existen
mecanismos automáticos para convertir valores de un tipo en otro, sin embargo es preferible la
conversión explícita a través del molde o “cast”.
La estructura (struct) permite agrupar elementos de diferente tipo. El arreglo agrupa elementos
de igual tipo. El string agrupa caracteres.
Se dispone de una forma de definir variables de esos grupos básicos, y los mecanismos de
acceso a las componentes: el discriminador del campo (un punto) para las estructuras; y los
paréntesis cuadrados o corchetes para delimitar el índice de la componente del arreglo. Las
operaciones que están definidas para estos grupos básicos son muy reducidas, el programador
debe crear sus propias funciones o métodos para efectuar operaciones más complejas sobre
estos grupos básicos. En el caso de strings existe una biblioteca estándar de funciones que los
manipulan (ver Apéndice 2).
Las agrupaciones pueden ser tan complejas como sea necesario: ya que es posible crear arreglos
de estructuras, y también estructuras que contengan arreglos y strings.
2.2.2. Vínculos.
Pueden establecerse relaciones entre elementos de datos. Los siguientes diagramas ilustran las
formas básicas de vinculación: vínculos de orden o secuencia en listas, relaciones jerárquicas en
árboles, e interconexiones complejas en grafos.
árboles
listas
grafos
El programador debe disponer de elementos del lenguaje que le permitan crear nuevos tipos de
datos, y mediante estos tipos crear funciones o métodos que realicen operaciones sobre las
componentes.
Otro mecanismo para establecer vínculos es el puntero, que es una variable que contiene la
dirección de otra componente. La vinculación a través de punteros suele emplearse
preferentemente en situaciones dinámicas, en las cuales el número de las componentes varía
durante la ejecución del programa; esto debido a que se crean, insertan y descartan
componentes.
El acceso a componentes se realiza vía indirección de un puntero constante que es el nombre del
arreglo, con un offset dado por el índice del elemento.
Es práctica usual, definir mediante una constante el tamaño máximo del arreglo
#define MaxEntradas 10
A las funciones que manipulan arreglos se les suele pasar el arreglo por referencia.
Por ejemplo la función que imprime en una línea las componentes de un arreglo a.
Los ejemplos anteriores ilustran que el acceso a componentes de un arreglo se efectúa mediante
indirección con el puntero que es el nombre del arreglo.
La expresión: A[c] se interpreta como el contenido de la dirección:
A + c*(tamaño del tipo del arreglo).
Donde A es la dirección de la primera componente del arreglo.
Empleando el lenguaje:
A[c] equivale a: *(A+c) y A es equivalente a &A[0].
Definición de matrices.
Puede definirse una matriz de caracteres, arr, de R renglones y C columnas mediante:
#define R 8 //renglones
#define C 10 //columnas
char arr[R][C];
arr
arr[3][5]
arr[3]
La expresión: a[3][5], denota el sexto carácter del cuarto renglón. La cual puede escribirse,
usando notación con punteros, mediante: * ( *(arr+3) + 5 ).
Puede concluirse que arr es un puntero a un puntero a carácter.
Definición de arreglo de arreglos.
Empleando definición de tipos puede crearse el tipo renglón, como un arreglo de C caracteres.
Y mediante este nuevo tipo puede definirse una matriz ar, como un arreglo de renglones.
Definiciones que se muestran a continuación.
#define R 8 //renglones
#define C 10 //columnas
typedef char renglon[C];
renglon ar[R];
Con el mismo procedimiento se pueden estructurar organizaciones más complejas. Por ejemplo
una matriz cuyo contenido sea un arreglo.
Los arreglos aseguran que las componentes quedan adyacentes en memoria. En el caso de
matrices, el último elemento del primer renglón queda adyacente con el primer elemento del
segundo renglón.
Arreglo de punteros a renglones.
Un ejemplo de creación de estructuras de datos, empleando el lenguaje C, es diseñar un arreglo
de punteros pt, que apunten a los renglones de una matriz.
ar
pt
(*pt[2])[5]
Es preciso vincular los punteros del arreglo con los renglones, lo cual se efectúa con:
for(i=0; i<R; i++) pt[i]=&ar[i]; //llena arreglo de punteros a renglones.
Entonces: pt[2] contiene un puntero al tercer renglón. Lo cual puede escribirse: *(pt +2 ).
La expresión: *(pt[2]) es el tercer renglón, que en este caso es un arreglo de C caracteres; es
decir: *( *(pt+2)) es el valor del puntero al inicio de ese renglón. Y consecuentemente:
*( *(pt+2)) +5 es el puntero al sexto carácter del tercer renglón.
Finalmente para accesar al elemento ubicado en el tercer renglón y en la sexta columna, debe
indireccionarse el puntero anterior, lo cual puede escribirse: *( *( *(pt+2)) +5), o empleando
notación de arreglos: (*pt[2])[5] .
#define MaxEntradas 4
#define fin_de_lista -1
1 2 3 4
//molde. Declaración.
struct fecha
{ int dia;
int mes;
int agno;
};
//instancias.
struct fecha fecha1; //definición.
struct fecha fecha2={1,5,2004}; //definición e inicialización
Nótese que se requiere preceder con la palabra struct al nombre del molde.
En este ámbito, las definiciones de las instancias para las variables, quedan:
Fecha fecha3 = {1, 5, 2004}; //definición e inicialización.
pFecha pfecha3=&fecha3;
Observar que las definiciones no son precedidas por struct.
Es preferible pasar una referencia a la estructura, ya que esto ocupa menos espacio en el frame.
Sólo es preciso copiar un puntero.
void printfecharef(pfecha p) //paso por referencia.
{
printf(" Día = %d Mes = %d Año = %d \n", p->dia, (*p).mes, p->agno);
}
Ejemplos de uso:
printfecha(fecha3);
printfecharef(pfecha3);
Ejemplo de uso:
fecha3 = setfecha(15, 6, 2005);
También suele ser necesario desarrollar funciones para realizar operaciones de comparación
entre estructuras. El siguiente prototipo ilustra un operador de comparación “mayor o igual
que”, al cual se le pasan punteros a estructuras y devuelve un puntero a la estructura que es
mayor o igual que la otra, el diseño de la función se deja como tarea.
//instancias. Definiciones.
nodo nodo1={1,NULL};
nodo nodo2={2,NULL};
nodo nodo3={3,NULL};
pnodo lista=&nodo1;
lista
1 2 3
En caso de definir la lista en forma dinámica, sólo es preciso definir la variable lista. El espacio
para los nodos se solicita a través de llamados a malloc; debido a esto los nodos no tienen un
nombre y sólo se los puede accesar vía punteros.
En el caso del ejemplo, los nodos se han definido en forma estática, y tienen un nombre de
variable asociado.
La palabra static, que precede a la definición, explícitamente indica que la tabla debe
almacenarse en un segmento estático de la memoria. Es decir estará disponible, durante toda la
ejecución del programa.
0 String i
1
2
….
B-1 String j
String k
#define B 10 /* 10 celdas */
static celda hashtab[B]; /*arreglo de celdas */
static int ocupados; //ocupados de la tabla
2.5.3. Multiárboles.
La descripción de un nodo que pueda tener un número variable de punteros que apunten a sus
descendientes no es práctica. Ya que esto implica almacenar los vínculos en un arreglo, y si es
arreglo debe tener un tamaño fijo. El cual debe escogerse considerando el máximo número de
descendientes que soportará la estructura; mal empleando los recursos en el caso de un nodo sin
hijos, o de nodos con un pequeño número de descendientes. Además siempre estará el riesgo de
que aparezca un nodo con mayor número de descendientes que el tamaño máximo escogido.
2 3 4
5 6 7 8 9 10 11 12 13
El número variable de los vínculos asociados a un nodo pueden reemplazarse por sólo dos
vínculos: uno que relaciona el nodo con el primer descendiente izquierdo, y otro que vincula a
cada nodo con su hermano derecho. Nótese que en cada nivel puede observarse una lista de
nodos que parte del nodo ubicado más a la izquierda y termina en un vínculo nulo después del
último descendiente de un nodo.
Se ha considerado que se almacenará un entero como clave o valor distintivo del nodo. Se ha
agregado un puntero a una estructura que podría almacenar otros datos asociados al nodo, con el
campo datos_periféricos. También se agrega un puntero al padre, ya que esta información
reduce el tiempo de buscar al padre de un nodo determinado.
2.5.3.2. Descripción mediante arreglos de cursores.
Se describe a continuación los valores que describen el multiárbol, de la Figura 2.6, mediante
arreglos de cursores para el Hijo izquierdo y Hermano Derecho. Se ha considerado que un valor
cero indica que no hay hijo izquierdo o hermano derecho o padre; si se desea usar el nodo cero,
podría elegirse -1 como el cursor nulo.
Se ha agregado un arreglo de cursores para especificar el padre del nodo. Esta información
facilita la búsqueda del padre de un nodo dado.
Un diseño de la estructura de datos considera al multiárbol como una estructura cuyos campos
son los diferentes arreglos.
#define MAXNODOS 13
struct moldemultiarbol
{
int Hijo_izquierdo[MAXNODOS];
int Hermano_derecho[MAXNODOS];
int Padre[MAXNODOS];
int clave[MAXNODOS];
int dato1[MAXNODOS];
} arbol;
1 2 3
4 5 6 7 8 9 10 11 12
En un ambiente de grafos, es de interés describir un árbol que interconecta todos los vértices sin
formar circuitos. Se ilustra un árbol de un grafo orientado, en el que se muestra el número
asociado a cada vértice; se ha considerado el vértice 0, esto debido a que los arreglos en C,
parten de índice cero.
Vértice 0 1 2 3 4 5 6 7 8 9 10 11 12
Padre del vértice 0 0 0 0 1 1 2 2 2 3 3 3 3
La realidad suele ser más compleja que los casos idealizados que se exponen en los cursos
básicos.
El diseño de una estructura más realista, es la que describe los encabezados de los paquetes
ICMP e IP en un ambiente de red.
Primero se definen dos tipos básicos, con fines de compatibilidad. Se mapean los tipos del
lenguaje C, a tipos lógicos. Si los tamaños de los tipos básicos de un compilador para un
procesador específico son diferentes, basta cambiar el nombre de los tipos básicos. Toda la
codificación no es necesario modificarla, ya que queda en términos de u8_t y u16_t, que son los
tipos usados en el programa.
Problemas resueltos.
#include <stdlib.h>
#include <stdio.h>
#define MAX 5
typedef struct nn
{ int i1;
int i2;
struct nn * next;
} nodo, * pnodo;
pnodo lista1=NULL;
pnodo lista2=NULL;
pnodo getnodo(void)
{ pnodo p;
if( (p=(pnodo)malloc(sizeof(nodo)))==NULL) return(NULL); else return(p);
}
void crealista(void)
{ int i;
for(i=0; i<MAX; i++)
{ Push(&lista1, i, i+1); Push(&lista2, MAX-i, i-1); }
}
int main(void)
{ pnodo tt;
crealista();
printf(" %d \n", ( *(busca(lista1)) ).i1);
printf(" %d \n", busca(lista2)->i2);
if( (tt=busca2(lista1, 3, lista2)) !=NULL) printf(" %d \n", tt->i2);
return(0);
}
Solución.
a) En la función crealista, se tiene un ejemplo de uso de Push.
Consideremos el llamado: Push(&lista1, 0, 1).
Lista1
newnodo ?
ref
dato1 = 0
dato2 = 1
Figura P2.1.
Nótese que los argumentos son variables almacenadas en el stack, iniciadas con los valores de
los parámetros de la invocación. La variable local newnodo, al no estar inicializada en su
definición apunta a cualquier lado. Razón por la cual, conviene definirlas e inicializarlas
simultáneamente.
Después de un llamado exitoso a getnodo(); es decir, malloc asignó una estructura en el heap, un
diagrama de la situación es el siguiente:
Lista1
newnodo i1 = ?
ref i2 = ?
dato1 = 0 next = ?
dato2 = 1
Figura P2.2.
Lista1
newnodo i1 = 0
ref i2 = 1
dato1 = 0 next
dato2 = 1
Figura P2.3
Lista1
i1 = 0
i2 = 1
next
Figura P2.4
Datos heap
Lista1 i1=4 i1 = 3 i1 = 2 i1 = 1 i1 = 0
i2 = 5 i2 = 4 i2 = 3 i2 = 2 i2 = 1
next next next next next
Lista2 i1 =1 i1 = 2 i1 = 3 i1 = 4 i1 = 5
i2 = 3 i2 = 2 i2 = 1 i2 = 0 i2 = -1
next next next next next
Figura P2.5.
c) A la función busca, se le pasa la dirección del primer nodo de la lista. Si la lista es vacía,
retorna un puntero nulo. Si no es vacía, con pp recorre la lista, dejando qq apuntado al nodo
corriente y con pp al próximo. Cuando se llega con pp, al final de la lista, qq apunta al último
nodo de la lista.
d) La función busca2 intenta encontrar el valor j en el campo i1 del nodo apuntado por el
argumento p. Si la lista es vacía retorna un puntero nulo; en caso de encontrar el valor j, busca a
partir del nodo apuntado por q, el valor del campo i1 que sea igual al valor del campo i2 del
nodo donde quedó apuntando p.
Si lo encuentra, retorna un puntero al nodo que cumple la condición anterior; en caso contrario,
retorna un puntero nulo.
La invocación: busca2(lista1, 3, lista2), después del primer while, deja p apuntando al segundo
nodo de la lista1, el segundo while deja q apuntando al cuarto nodo de la lista2; ya que busca en
ésta el valor 4 en el campo i1.
El diseño de la función incurre en un error frecuente, en este tipo de problemas. ¿Qué ocurre si
el valor j no se encuentra en ningún campo i1 de la lista apuntada por p?.
Cuando p tome valor nulo, intentará leer p->i1 en las primeras direcciones de memoria, las
cuales suelen pertenecer a un segmento del sistema operativo. Lo más seguro que la ejecución
del proceso aborte debido a un error de segmentación.
Se podría corregir, cambiando:
while( p->i1 != j ) p = p->next;
Imprimiría:
0
-1
0
int btoi(char *str, int *pvalor) que convierta un string binario pasado en str (Ej. "110101") a un
entero a ser retornado en pvalor (Ej. 53). La función retorna 0 si no hay error y retorna -1 si el
string binario tiene un carácter que no es '0' o '1'.
Nota: 1101 (en binario) = 1x23+1x22+0x21+1x20 = 8 + 4 + 1 = 13 en decimal
/* Uso de la funcion */
#include <stdio.h>
#include <math.h>
#include <string.h>
int btoi(char *str, int *pvalor);
main()
{
int valor=0;
char str[10];
strcpy(str, "110101");
if( !btoi(str, &valor) ) printf("string binario: %s, int:%d\n", str, valor);
}
Solución.
Una alternativa es sumar las potencias presentes de dos, desde la menos significativa.
while(i >=0)
{
if (*(str + i) == '1')
{ temp += pot; printf(" %d %d \n",temp, pot);}
else if (*(str + i) == '0') ;
else return -1; //no es 1 ó 0
i--;pot*=2;
}
*pvalor = temp;
return 0;
}
Solución.
El cast (char) c se emplea, debido a que el segundo argumento de la función es de tipo entero,
para convertirlo a carácter y efectuar la comparación con un carácter del string. No es
necesario, ya que por defecto, los valores de tipo carácter son promovidos automáticamente a
enteros con signo. Por ejemplo se puede pasar equivalentemente el valor 65 ó 0x45 ó „A‟. Esto
permite pasar caracteres de control que no son imprimibles, como argumentos. La comparación
también se podía haber explicado según: ( (int) *s == c ). Es preferible usar cast, de tal modo
de comparar valores de igual tipo.
El cast en return (char *) s; no es necesario, ya que s es de tipo puntero a char.
b) Revisa y encuentra la primera ocurrencia del carácter (char) c, en el string apuntado por s;
retornado un puntero al carácter de s que es igual a (char) c. Si el string es nulo, retorna un
puntero nulo a carácter; también si no encuentra (char) c en el string.
a) Explicar que realiza la función. Indicando las condiciones en las que retorna un cero o un uno
b) Dar un ejemplo de uso. Definiendo las variables que sean necesarias.
Solución.
a) Compara dos strings. Retorna 1 sólo si los strings son iguales. Reconoce la igualdad de dos
strings nulos. Retorna cero, si los strings son diferentes. Si los caracteres, en la misma
posición, de s1 y s2 son iguales va recorriendo ambos strings.
Si los primeros caracteres de los strings fuesen iguales, retorna un cero si string s1 es más corto
que el string s2 (ya que no se cumple la condición del while); lo mismo sucede si string 1 es más
largo que el string s2.
b) char *s1="12345678";
char *s2="12345";
clave
p1 p2 p3
4 6
p1 p2 p3 p1 p2 p3
Figura P2.6.
Solución.
a)
typedef struct moldenodo
{ int clave;
struct moldenodo *p1;
struct moldenodo *p2;
struct moldenodo *p3;
} nodo , *pnodo;
b)
pnodo creanodo(int clave)
{ pnodo p;
if ((p = (pnodo) malloc(sizeof(nodo))) == NULL) {
printf ("Memoria insuficiente para crear nodo\n");
exit(1);
}
p->clave=clave;p->p2=NULL;p->p1=NULL;p->p3=NULL;
return(p);
}
Figura P2.7.
pn->p3=pn;
pn
5
Figura P2.8.
pn->p1=creanodo(3); pn
5
Figura P2.9.
pn->p1->p3=pn;
pn
5
Figura P2.10.
3
8
Figura P2.11.
pn->p2->p3=pn->p1;
pn
5
3
8
Figura P2.12.
c)
pn= creanodo(6); pn->p2=pn; pn->p3=pn;
pn->p1=creanodo(4); pn->p1->p1=pn->p1;
pn->p1->p3=pn; pn->p1->p2=pn;
pn
4 6
p1 p2 p3 p1 p2 p3
Figura P2.13.
#include <stdlib.h>
typedef struct nn
{ int x;
struct nn * p;
struct nn * q;
int y;
} t, *pt;
t w, z;
pt px=&w;
void main(void)
{ px->p=&z;
w.q = px->p;
px->q->q=px;
z.p = w.p;
w.x = z.x = 2;
(*px).y = 8;
(*(w.q)).y =9;
px=(pt) malloc(sizeof(t));
px->p=px->q = (pt ) 0;
px->x=px->y=12;
}
Solución.
px
w
z
x
x
p q
p q
y
y
Figura P2.13.
px
p q
y
w.q=px->p;
px->p =&z
p q
Figura P2.14.
px
p q
px->q->q=px
z
z.p=w.p p q
Figura P2.15.
px
x 2
p q
w.x=z.x=2 y 8 (*px).y=8;
x 2
p q
(*(w.q)).y=9
y 9
Figura P2.16.
px =(pt)malloc(sizeof(t)); px
x 2
p q
x 12
y 8
p q
z
y 12
x 2
px ->p=px->q=(pt)0;
p q
y 9
px ->x=px->y=12;
Figura P2.17.
Ejercicios propuestos.
#include <stdio.h>
int func(int *, float *, char);
void show();
void show()
{ printf("\ni1= %d i2= %d" ,i1,i2);
printf("\nf1= %f f2= %f",f1, f2); }
#include <stdio.h>
int a[5];
int i;
void arreglo(int j){ for(i=0;i<5; a[i]=j+i, i++); }
void show(void){for(i=0;i<5;i++) printf(" %3d" , a[i]); printf("\n");}
void main(void)
{ arreglo(1), show();
arreglo(2), show();
arreglo(a[2]+*a+3), show();
while(1);
}
void f1(int& i)
{ i = (i | 0x8000); }
void f2(int& i)
{ i = (i>>1); i = (i&0x7fff); }
void prt(int i)
{ int j;
for (j=15; j>=0; j--)(1<< j ) &i ? printf( '1') : printf( '0'); printf( '\n');
}
#include <stdio.h>
int f1(int& , int);
int f2(int *, int);
void main()
#include <stdio.h>
struct punto{
int x;
int y; };
struct molde{
int a[2];
char c;
punto p; };
void main()
{
molde m1={{2,4},'p',{3,5}}, m2={{5,6},'q',{7,1}};
molde *pm=&m2;
int *pi=&(m1.p.y);
printf(“%d \n”, *pi ) ;
printf(“%d \n”, cout << m2.a[1] + m1.p.x );
printf(“%d \n”, pm->a[1] + pm->p.x );
printf(“%d \n “, *(pi-1)) ;
pi = &m2.a[0];
printf(%d \n “, *(pi+1));
}
#include <stdio.h>
int main(void)
{
printf("%d ", f2(5, 3, 2));
return(0);
}
#include <stdio.h>
int arr[10];
int main(void)
{
f1(5, arr); f2(5, arr);
return(0);
}
#include <stdio.h>
int arr[10];
int *pi;
#define NULL ( (char *) 0)
int main(void)
{
f1(5, arr); pi=f2(5, arr, 8);
if(pi!=((int *) NULL)) printf("%d ", *pi); else printf("no se encuentra\n");
return(0);
}
#include <stdio.h>
void prtint(int i)
{ int j, k=1;
for (j=15; j>=0; j--) if( (k<<j)&i ) putchar(‟1‟); else putchar(‟0‟);
}
Mediante un arreglo de listas de los hijos de cada nodo. El arreglo debe tener una entrada por
cada nodo, además considerar que la raíz pueda se cualquier nodo.
Índice general.
CAPÍTULO 2. ............................................................................................................................................ 1
DEFINICIÓN DE ESTRUCTURAS DE DATOS EN C......................................................................... 1
2.1. TIPOS PRIMITIVOS. ............................................................................................................................. 1
2.2. MECANISMOS DE ESTRUCTURACIÓN. ................................................................................................. 1
2.2.1. Grupos básicos. ......................................................................................................................... 1
2.2.2. Vínculos. .................................................................................................................................... 2
2.3. EJEMPLOS BASADOS EN ARREGLOS. ................................................................................................... 3
2.3.1. Acceso a componentes del arreglo. ........................................................................................... 3
Definición de matrices. .................................................................................................................................... 4
Definición de arreglo de arreglos. .................................................................................................................... 5
Arreglo de punteros a renglones. ..................................................................................................................... 5
Arreglo de punteros a caracteres. ..................................................................................................................... 6
2.3.2. Lista simplemente enlazada en base a cursores. ....................................................................... 6
2.4. EJEMPLOS BASADOS EN ESTRUCTURAS. ............................................................................................. 7
2.4.1. Estructura para fecha. ............................................................................................................... 7
2.4.2. Lista simplemente enlazada en base a punteros. ....................................................................... 9
2.5. ESTRUCTURAS MÁS COMPLEJAS....................................................................................................... 10
2.5.1. Arreglo de listas. ..................................................................................................................... 10
2.5.2. Arreglo de estructuras. ............................................................................................................ 11
2.5.3. Multiárboles. ........................................................................................................................... 11
2.5.3.1. Descripción mediante punteros. ........................................................................................................ 12
2.5.3.2. Descripción mediante arreglos de cursores. ...................................................................................... 12
2.5.3.3. Descripción por arreglo de padres. .................................................................................................... 13
2.6. UN EJEMPLO REAL DE ESTRUCTURAS. .............................................................................................. 14
PROBLEMAS RESUELTOS. ........................................................................................................................ 15
P2.1. Se tiene el siguiente programa: ............................................................................................... 15
P2.2. Escribir una función: ............................................................................................................... 20
P2.3. Se tiene la siguiente función: ................................................................................................... 21
P2.4. Se tiene la siguiente función: ................................................................................................... 22
P2.5. Se tiene la estructura para un nodo, ........................................................................................ 22
P2.6. Se tiene el siguiente programa. ................................................................................................ 26
EJERCICIOS PROPUESTOS. ....................................................................................................................... 29
E1. Determinar qué imprimen los siguientes segmentos: ................................................................ 29
E2. Colocar paréntesis y evaluar las expresiones siguientes: ........................................................... 29
E3. Se tiene el siguiente programa: .................................................................................................. 30
E4. Escribir programa. ..................................................................................................................... 30
E5. Indicar que escribe el programa. ................................................................................................ 30
E6. Indicar qué imprime el programa. ............................................................................................. 31
E7. Indicar qué imprime el programa. .............................................................................................. 31
E8. Indicar qué imprime el programa. .............................................................................................. 31
E9. Indicar qué escribe el programa. ................................................................................................ 32
E10. Determinar la salida. ................................................................................................................ 32
E11. Determinar la salida. ................................................................................................................ 33
E12. Determinar la salida. ................................................................................................................ 33
Índice de figuras.