Informática
Programación en C
1.- INTRODUCCIÓN
El C es un lenguaje de programación ampliamente extendido, en gran parte debido a su gran versatilidad. El C es ideal para escribir programas de muy diferentes tipos, tales como Sistemas operativos, ensambladores, interpretes de lenguajes, compiladores de lenguajes, editores de texto, controladores de red, bases de datos, programas de modem, etc. Son varias las características que hacen del C uno de los lenguajes de propósito general más utilizados:
Permite una fácil manipulación de bits, bytes y de direcciones de memoria, así como de funciones de E/S.
El C es un lenguaje estructurado, es decir, utiliza bloques o conjuntos de sentencias ordenadas lógicamente (por ejemplo el BASIC o el FORTRAN son lenguajes no estructurados), lo cual contribuye a la legibilidad de los programas haciendo más fácil su escritura y mantenimiento.
El C se compila, no se interpreta. Un intérprete es un programa que traduce linea por linea las sentencias de otro programa (p.ej en BASIC) al llamado código máquina. Cada vez que se quiera ejecutar el programa se tiene que ejecutar también el intérprete. Un compilador lee el programa entero y lo convierte en código máquina. El proceso de compilación es más largo, pero una vez compilado un programa, su ejecución es mucho más rápida y además no es necesario compilarlo cada vez que se ejecuta.
Como cualquier otro lenguaje de programación, C trabaja con variables, constantes, operadores, etc que constituyen las expresiones. Un conjunto de expresiones constituye una función. El conjunto de estas funciones es lo que da lugar a un programa completo en C. Todos estos conceptos se analizan con detalle en los siguientes apartados 2 al 7.
2.- VARIABLES, CONSTANTES, OPERADORES Y EXPRESIONES
Las variables y las constantes son manipuladas por los operadores para formar expresiones. Esta es la base de toda la programación. En este apartado se explican estos conceptos en lo que se refiere al lenguaje C.
2.1.- VARIABLES
Los nombres de las variables en C pueden llevar uno o varios caracteres, debiendo ser el primero una letra y los siguientes pueden ser letras, números o el carácter de subrayado, teniendo en cuenta que no puede coincidir con el nombre de una función o el de una palabra clave del C.
TIPOS DE DATOS
Hay 7 tipos de variables predefinidos, que se muestran en la Tabla 2.1:
Tipo | Número de bits | Rango |
char | 8 | o a 255 |
int | 16 | -32768 a 32767 |
short int | 8 | -128 a 127 |
unsigned int | 16 | 0 a 65535 |
long int | 32 | 4294967296 a 4294967295 |
float | 32 | 3.4E-38 a 4.3E38 (7 digitos de precisión) |
double | 64 | 1.7E-308 a 1.7E308 (12 dígitos de precisión) |
Algunas versiones de C permiten aplicar los modificadores short, long y unsigned a otros tipos de datos además del int.
DECLARACIÓN DE VARIABLES
Antes de usar una variable hay que declararla. Basicamente hay tres sitios donde se deben declarar las variables: dentro de las funciones (variables locales), en la definición de los parámetros de la función (parámetros formales), o fuera de todas las funciones (variables globales). Ejemplos:
/*Programa para ilustrar variables locales*/
Funcion1()
{
int variable_local;
variable_local=1;
}
Funcion2()
{
printf("%d",variable_local); /*el valor de variable_local que se imprimirá ya no será 1 porque ha sido declarada en función1(), y su valor ha sido destruído al salir de ésta*/
}
Parámetros formales:
Como veremos más adelante, si una función tiene argumentos, éstos deben de haber sido declarados. Su declaración se hace despues del nombre de la función y antes de abrir la llave:
Funcion1(parametro1,parametro2)
int parametro1, parametro2; /*declaración*/
{
int suma;
suma=parametro1+parametro2
.
.
.
}
Los parámetros formales también son dinámicos, y su valor se destruye al salir de la función. (También pueden declararse directamente dentro del paréntesis).
Variables globales:
Si declaramos una variable fuera de toda función podrá accederse a ella desde cualquiera. Si en una función se declara una variable local con el mismo nombre que una global existente, mientras estemos en esa función se hará referencia a la variable local. Por ejemplo:
int var=0 ; /*variable global inicializada a 0*/
main ()
{
var=7;
.
.
}
Funcion1()
int var;
var=10; /*var tomará el valor 10, no 7 ni 0*/
.
.
}
La expresión mediante la cual asignamos un determinado valor a una variable recibe el nombre de sentencia de asignación, cuya forma general es:
nombre_variable=expresión; (ej,: x=10)
Las asignaciónes en C se hacen de derecha a izquierda, esto es, la variable toma el valor de la expresión.
ARREGLOS
El arreglo o formación es otra clase de variable que se utiliza con frecuencia en C. Un arreglo es un identificador que referencia una colección de datos del mismo tipo, con el mismo nombre. Los datos pueden ser numéricos (int, float...) o caracteres. Este es un ejemplo de arreglo unidimensional de caracteres: supongamos que queremos guardar la cadena de caracteres "HOLA" en un arreglo. Como la palabra "HOLA" tiene 4 letras necesitamos un arreglo de 5 elementos (uno más para el caracter \0 -ver constantes de cadena de caracteres, ap 2.2-) tal como se describe en la siguiente tabla:
Elemento del arreglo | Carácter de la cadena |
letra[0] | H |
letra[1] | O |
letra[2] | L |
letra[3] | A |
letra[4] | \0 |
El código en C de este programa se describe en el prog1 (ver Apéndice).
2.2.- CONSTANTES
C tiene cuatro tipos básicos de constantes: enteras, en coma flotante, de caracter y de cadena de caracteres. Las dos primeras se llaman constantes de tipo numérico.
Constantes enteras: son números con valor entero (p.ej. 0 1 127 32767).
Constantes en coma flotante: es un número que contiene un punto decimal, un exponente o ambos.(p.ej. 0.2 532.654 2E-8 .4533e7)
Constantes de carácter: un sólo carácter encerrado con comillas simples.
(p.ej. 'a' 'X' '7' '¿' ' ') Prácticamente todas las computadoras personales utilizan el conjunto de caracteres ASCII en el cual cada carácter individual se codifica numéricamente con su propia combinación unica de 7 bits.
Constantes de cadena de caracteres: Una constante de cadena de caracteres consta de cualquier número de caracteres consecutivos (o ninguno), encerrados entre comillas dobles. (p.ej. "hola" "1-3-1978" "F=m*a" " "). El compilador inserta automáticamente un carácter nulo (\0) al final de toda constante de cadena de caracteres, como último carácter de ésta. Éste carácter no aparece cuando se visualiza la cadena, pero es muy útil para identificar rápidamente el final de una cadena de caracteres.
2.3.- OPERADORES
Un operador es un símbolo que indica al compilador que se lleven a cabo específicas manipulaciónes matemáticas o lógicas. Hay varios tipos de operadores en C. Destacamos los aritméticos, los relacionales y los lógicos.
OPERADORES ARITMÉTICOS
La lista de operadores aritméticos permitidos en C se muestran en la siguiente tabla:
Operador | Acción |
- | resta |
+ | suma |
* | multiplicación |
/ | división |
% | resto de división entera |
-- | decremento |
++ | incremento |
Para ilustrar como funcionan en C estos operadores, ver prog2 (ver también prog6). Los operadores incremento y decremento se estudirarán con más detalle en el capítulo de estructuras de control.
OPERADORES RELACIONALES Y LÓGICOS
La clave de los conceptos de operadores relacionales y lógicos es la idea de cierto y falso. Los operadores relacionales y lógicos se muestran en la siguiente tabla:
Operador relacional | Acción | Operador lógico | Acción |
> | mayor que | && | AND |
>= | mayor o igual que | || | OR |
< | menor que | ! | NOT |
<= | menor o igual que | ||
== | igual | ||
!= | distinto |
Todas las expresiones relacionales y lógicas producen como resultado un 0 o un 1. Por ejemplo, el siguiente fragmento de programa imprime el número 1 en la pantallla (ver también prog3):
int x;
x=11;
printf("%d",x>10);
No debe confundirse el operador relacional == con el operador de asignación = ya mencionado. Hay otros operadores interesantes como el operador sizeof, que devuelve el tamaño en bytes del tipo de dato entre paréntesis.
num_bytes=sizeof(int); (num_bytes tomaría el valor 2)
Otros operadores de especial importancia son los operadores de puntero & y *.
En C, un puntero es la dirección de memoria de una variable. El operador & devuelve la dirección de memoria del operando. El operador * devuelve el valor de la variable ubicada en la diección que le siga. Todo esto se desarrollará ampliamente en el tema de punteros.
2.4.- EXPRESIONES
Como hemos dicho, los operadores, las constantes y las variables son los constituyentes de las expresiones. Una expresión en C es cualquier combinación válida de estas piezas. Cuando en una expresión se mezclan constantes y variables de distintos tipos, se convierten a un tipo único. En general el compilador de C convertirá todos los operandos al tipo del mayor operando. Además podemos añadir espacios, tabulaciones y paréntesis para hacer más legible la expresión sin que por ello se produzcan errores o se disminuya la velocidad de ejecución de la expresión. Existen abreviaturas para las expresiones del tipo:
variable = variable operador expresión (p.ej: x=x+2)
Esa expresion es equivalente a esta otra abreviada:
variable operador = expresión (p.ej: x+=2)
3.- FUNCIONES DE ENTRADA Y SALIDA (por consola)
Hay dos formas de leer y escribir datos en un ordenador: por consola (mediante operaciones sobre el teclado y la pantalla) y sobre ficheros en disco. En este capítulo sólo explicaremos las principales funciones por consola.
En C toda la E/S es orientada a carácter: no se pueden leer o escribir cadenas y números directamente, sólo bytes. Además, en C no hay funciones incorporadas para realizar operaciones E/S. En realidad estas funciones se encuentran en la librería estándar de C. Se puede acceder a una función de entrada/salida desde cualquier sitio de un programa, simplemente escribiendo el nombre de la función, seguido de una lista de argumentos (si los tiene) entre paréntesis. Las principales funciones de E/S son: getchar, putchar, gets, puts, scanf, printf,
3.1.- Getchar (Entrada de un caracter)
Esta función no requiere argumentos, pero, como en todas las funciones, es necesario que un par de paréntesis vacíos sigan a la palabra:
variable de carácter = getchar();
La función getchar se puede también utilizar para leer cadenas de varios caracteres, leyendo en un bucle la cadena carácter.
3.2.- Putchar (salida de un carácter)
Esta función transmite un carácter al monitor, que estará representado por una variable tipo carácter. La referencia a la función putchar es de la forma:
putchar (variable de carácter)
La función putchar se puede utilizar para visualizar una constante de cadena de caracteres almacenando la cadena dentro de un arreglo de tipo carácter, y, mediante un bucle, escribir los caracteres uno a uno.
Hay otras funciones más potentes que getchar y putchar, llamadas gets y puts, capaces de leer y escribir de forma similar cadenas enteras de caracteres. (ver prog4)
3.3.- Scanf (introducción de datos)
Esta función se puede utilizar para introducir cualquier combinación de valores numéricos, caracteres sueltos y cadenas de caracteres. Su forma general es:
scanf (cadena de control, arg1, arg2,..., argn)
donde cadena de control hace referencia a una cadena de caracteres que contiene información sobre el formato de los datos (especificadores de formato) y arg1, arg2,...,argn son punteros que indican las direcciones de memoria en donde se encuentran los datos. Los especificadores de formato de uso más común son:
%c | carácter simple |
%s | cadena |
%d | entero con signo |
%u | entero sin signo |
%f | coma flotante sin exponente |
%e | coma flotante con exponente |
Tambien se utilizan los prefijos l (entero largo o doble precisión con %f y %e) y h (entero corto) para modificar el tipo de tato. (ejs: %ld, %lf %hu).
Todas las variables utilizadas para recibir valores a través de scanf() se pasan por sus direcciones. Esta es la forma en que el C crea un "paso por referencia". Por ejemplo, si queremos leer un entero dentro de la variable numero, se utilizaría esta llamada a scanf():
scanf ("%d",&numero);
Las cadenas se leerán sobre arreglos de caracteres, y el nombre del array, sin ningún índice, es la dirección del primer elemento del arreglo. Por tanto, para leer una cadena dentro del arreglo de caracteres cadena, se utilizaría:
scanf ("%s",cadena);
En este caso, cadena ya es un puntero y no es necesario que vaya precedido por el operador &.
3.4.- Printf
Esta es una función de salida genérica por consola que se proporciona con el compilador de C. La forma genérica de printf() es:
printf ("cadena de control", lista de argumentos);
La cadena de control contiene los comandos de formato que le indican a printf() como debe visualizar el resto de los argumentos sobre la pantalla, así como la cantidad de argumentos existientes. Tenemos ejemplos de estas funciones en cualquier programa de los vistos hasta ahora. En concreto, en el prog5 se utiliza la función printf con 4 argumentos.
4.- ESTRUCTURAS DE CONTROL
En este capítulo se estudian varias de las sentencias de control de los programas en C. Se incluyen las sentencias condicionales if y switch y las construcciones de bucles while, for y do/while.
4.1.- SENTENCIAS CONDICIONALES
LA SENTENCIA "IF"
El formato general de la sentencia If es:
if (condición)
{
sentencias /*bloque 1*/
}
else /*opcional*/
{
sentencias /*bloque2*/
}
Si la condición es cierta (cualquier valor distinto de 0), se ejecutará el primer bloque de sentencias; si no, se ejecuta el segundo bloque, si existe.
Una construcción común de programación es la escalera if-else-if, cuyo esquema es el siguiente:
if (condición)
{
sentencia;
}
else if (condición)
{
sentencia;
.
.
else
{
sentencia;
}
Las condiciones se evaluan de arriba abajo. Tan pronto como se encuentre una condición cierta, se ejecuta la sentencia asociada y se salta el resto de la escalera. El último else actua como condición por defecto, en caso de que ninguna de las anteriores se cumpla.
LA SENTENCIA "SWITCH"
La estructura switch es parecida a la escalera if-else-if, pero es menos confusa y más elegante. El formato general de la sentencia switch es el siguiente:
switch (variable)
{
case expresión1:
sentencia;
case expresión2:
sentencia;
.
.
default:
sentencia;
}
La instrucción default es opcional, y se ejecuta cuando no se cumple ninguna de las anteriores. Mediante switch sólo podemos probar una igualdad, mientras que con if se puede evaluar una expresión relacional o lógica. Un ejemplo sencillo de cómo funciona el switch se detalla en el prog6. (ver también prog14).
4.2.- Bucles o estructuras repetitivas
Los bucles permiten que se ejecuten un conjunto de instrucciones hasta que se cumple una cierta condición. Esta condición puede estar predefinida, como en el bucle for, o no como en los bucles while y do-while.
EL BUCLE FOR
El formato general de for para repetir una o más sentencias es:
for (inicialización; condición; incremento)
{
sentencia 1;
.
.
sentencia n;
}
El bucle for se continuará ejeutando mientras que las pruebas de ejecución sean ciertas. Cuando la condición sea falsa, la ejecución del programa se reanudará en la sentencia siguiente al bloque for.
EL BUCLE WHILE
El formato general de la sentencia es:
while (condición)
{
sentencia;
}
La sentencia se ejecuta mientras que la condición sea cierta.
EL BUCLE DO-WHILE
A diferencia de los bucles for y while, el bucle do-while comprueba su condición al final del bucle, por lo que siempre se ejecutará al menos una vez. Su formato general es:
do
{
sentencias;
} while (condición);
BREAK Y EXIT()
La sentencia break y la función de librería exit() permiten forzar una salida desde dentro del bucle, saltándose la condición normal del bucle.
Cuando se encuentre la sentencia break dentro de un bucle, el bucle se finaliza inmediatamente y el control del programa se pasa a la siguiente sentencia despues del bucle.
La función exit() originará una terminación inmediata del programa y una vuelta al sistema operativo.
5.- ARREGLOS
El arreglo o array proporciona una forma de referirse a los elementos individuales de un conjunto, utilizando el mismo nombre de una variable para todos los elementos pero empleando distintos índices.
5.1.- DECLARACIÓN DE ARREGLOS
Como el resto de variables, los arrays tienen que ser declarados para que el compilador conozca el tipo y el tamaño que queremos para el arreglo. Por ejemplo:
int elementos[10];
En este caso int especifica el tipo de array, la palabra elementos es un puntero hacia el primer elemento del array y el [10] especifica cuántas variables de tipo int tendrá nuestro arreglo.
5.2.- INTRODUCCIÓN Y LECTURA DE DATOS EN UN ARREGLO
Para introducir datos en un arreglo se suele utilizar el bucle for, que hará que la función scanf se ejecute reiteradamente para pedir al usuario que introduzca valores para cada elemento del arreglo. Para imprimir los elementos de un arreglo construimos un bucle similar en el cual se repita la función printf tantas veces como elementos haya en el arreglo. (Ver ejemplo en el apartado 5.5)
5.3.- LECTURA DE UN NÚMERO DESCONOCIDO DE ELEMENTOS
Cuando no sabemos de antemano el número de elementos que necesitamos introducir en un arreglo podemos utilizar los bucles while o do-while de modo que se interrumpa la entrada de datos en el momento que no se cumpla una determinada condición. La variable que comprueba si se cumple la condicíon para que se ejecute el bucle se denomina centinela. Como ejemplo de centinela vease el prog7.
5.4.- INICIALIZACIÓN DE ARREGLOS
Al igual que podemos inicializar variables a un valor determinado, podemos inicializar los elementos de un arreglo asignandoles determinados valores iniciales y así evitar tener que introducirlos cada vez. Este tipo de arreglos sólo se pueden declarar como variables globales (fuera de toda función) o como variables tipo static: (variables permanentes dentro la función donde se declaran) Por ejemplo:
static int vector[6] = {13, 11, 7, 5, 3, 1};
Haría la siguiente asignación:
Elemento del arreglo | Valor asignado |
vector[0] | 13 |
vector[1] | 11 |
vector[2] | 7 |
vector[3] | 5 |
vector[4] | 3 |
vector[5] | 1 |
Como ejemplo de esto ver prog8 (descomposición en factores primos).
5.5.- ARREGLOS BIDIMENSIONALES
Los arreglos pueden tener más de una dimensión, lo cual es muy util para representar matrices y operar con ellas. El ejemplo siguiente muestra como introducir los elementos de una matriz 3 por 3, y su visualización en pantalla. Además, ver programa prog9 en el que se multiplican dos matrices tres por tres.
El C permite arreglos de más de dos dimensiones. El límite dependerá del compilador. Pero los arrays de más de tres dimensiones no se suelen utilizar ya que el almacenamiento de todos los elementos de un array está asignado en memoria permanentemente durante la ejecución del programa. Por tanto, cuando son necesarios arrays multidimensionales grandes, se deben asignar dinámicamente los bits y las partes del array que sean necesarios, y utilizar punteros.
/*programa que introduce y visualiza los elementos de una matriz 3 * 3*/
#include <stdio.h>
#include <math.h>
main ()
{
int A[3][3]; /*declaración del arreglo de la matriz*/
int i,j; /*declaración de los contadores*/
for (i=0;i<3;i++) /*bucles para introducir los elementos de la matriz*/
{
for (j=0;j<3;j++)
{
printf("introduce elemento (%d,%d)\n",i,j); /*visualiza el elemento a introducir*/
scanf("%d",& A[i][j]); /*guarda el elemento en el arreglo*/
}
}
for (i=0;i<3;i++) /*bucle para imprimir los elementos de la matriz*/
{
for (j=0;j<3;j++)
{
printf("%d ",A[i][j]);
}
printf("\n");
}
}
6.- PUNTEROS
Un puntero es una variable que contiene la dirección de memoria de otra variable (se dice que "apunta" a esa variable). Para acceder a los datos de los punteros se utilizan los operadores * ("contenido de...") y & ("dirección de memoria de...").
Para utilizar punteros, éstos tienen que habler sido declarados. Por ejemplo, si x es un puntero a float, se indica:
float *x;
Como ejemplo de como funciona un puntero véase el ejemplo1:
#include <stdio.h>
void main ()
{
int numero=225;/*declaracion y asignacion de la cantidad 225 a la variable numero*/
int *punt_numero; /*declaracion de la variable punt_numero como puntero*/
punt_numero=№/*asignacion de la direccion de memoria de numero al puntero*/
printf("El contenido de la dirección de memoria %x es %d.\n",punt_numero,*punt_numero);
/*la dirección de memoria se expresa como variable hexadecimal*/
}
La salida por pantalla de este programa podría ser la siguiente:
El contenido de la dirección de memoria FFF2 es 225.
6.1.- EXPRESIONES CON PUNTEROS
Los punteros se pueden utilizar en la mayoría de las expresiones válidas de C, pero presentan algunos casos particulares:
- En los operadores ++ y --, el incremento o decremento dependerá del tipo de dato al que apunte el puntero. Por ejemplo, un puntero a entero de valor puntero=1000, tras aplicarle el operador ++:
puntero=puntero++;
tomará el valor 1002.
- También podemos sumar o restar enteros a los punteros:
puntero=puntero + 5;
ahora puntero valdría 1012. Aparte de incrementar o sumar y restar enteros a punteros no podemos hacer más operaciones aritméticas con ellos: no se pueden multipicar o dividir punteros ni sumarlos o restarlos entre si,y no se les pueden aplicar operadores lógicos (a no ser en casos concretos, como comparar los elementos de un arreglo).
6.2.- ARREGLOS Y PUNTEROS
Existe una estrecha relación entre los arrays y los punteros. De hecho, el ordenador sólo sabe utilizar punteros, y el compilador traduce toda la notación de arreglos a notación de punteros. Por ejemplo, veamos el mismo programa (ejemplos 2 y 3) que imprime los valores de un array en notación de arreglos y de punteros:
/*programa que imprime los valores de un arreglo*/
#include <stdio.h>
void main()
{
static int array[]={1,2,3,5,7};
int i;
for (i=0;i<5;i++)
{
printf("%d ", array[i]);
}
}
_______________________________
/*programa que utiliza punteros para imprimir valores del arreglo*/
#include <stdio.h>
void main()
{
static int array[]={1,2,3,5,7};
int i;
for (i=0;i<5;i++)
{
printf("%d ", *(array+i)); /*se podría poner también array[i]*/
}
}
La salida de ambos programas es exactamente la misma:
1 2 3 5 7
En el segundo programa vemos la utilización del operador suma con punteros: *(array+i). Esto significa, dentro de la expresión, "imprime el contenido del elemento array[0] + i" donde i varía desde 0 hasta 4. Pero no se incrementan las direcciones de memoria una a una, sino de dos en dos, como corresponde a un int. Cabe destacar que para señalar el elemento array[0] sólo hemos utilizado la palabra array. Es decir:
&array[0] es equivalente a array
6.3.- PASO POR REFERENCIA
Se pueden pasar punteros a funciones como argumentos. Cuando se pasa un argumento por valor, el dato es copiado a la función, y cualquier alteración hecha al dato dentro de la función no es devuelta a la rutina llamadora. Pero cuando pasamos por referencia se pasa la dirección del dato, lo cual permite que el dato sea alterado globalmente desde dentro de la función. Esto se analizará con detalle en el siguiente capítulo.
6.4.- ASIGNACIÓN DINÁMICA DE MEMORIA.
La definición convencional de un arreglo produce la reserva de un bloque fijo de memoria al principio de la ejecución del programa, pero si el arreglo se representa en términos de una variable puntero no se produce ninguna asignación inicial de memoria. Para asignar memoria a este tipo de arreglos se usa la asignación dinámica de memoria, mediante la función de biblioteca malloc. Por ejemplo, imaginemos que necesitamos un arreglo con capacidad para 10 enteros. Podemos reservar la memoria declarando un arreglo llamado x:
int x[10];
En este caso ya hemos reservado espacio en memoria para 10 enteros. Sin embargo si declaramos la variable como un puntero:
int *x;
Podemos asignar el mismo espacio en memoria mediante la función malloc:
x=(int *) malloc (10 * sizeof (int));
con la ventaja de que, mediante la asignación dinámica de memoria podemos reservar la memoria que necesitemos durante la ejecución del programa y luego liberarla, mediante la función free. (ver programas de ordenación). Si además necesitamos que el arreglo quede inicializado a 0, podemos utilizar la función calloc, que funciona de la misma forma, pero asigna automáticamente el valor 0 a todos los elementos.
6.5.- PUNTEROS A PUNTEROS
Un puntero a puntero almacena la dirección de memoria donde se encuentra una variable puntero. Para ver cómo funcionan, considerese el ejemplo4:
void main()
{
int x,*p,**q;
x=10;
p=&x;
q=&p;
printf("%d",**q);/*imprime el valor de x*/
El puntero p guarda la dirección de memoria de x, cuyo contenido es 10. Por tanto *p es 10. El puntero q guarda la dirección de memoria de p, y por tanto **q sera también 10.
6.6.- ARREGLOS DE PUNTEROS
Un arreglo multidimensional puede ser expresado como un arreglo de punteros (punteros a punteros) en vez de como un puntero a un grupo de arreglos contiguos. Esto permite un acceso más rápido a los datos y facilita las operaciones con matrices. Como ejemplo de todo esto y de la asignación dinámica de memoria, vease el programa prog10 que suma dos matrices guardadas en arreglos de punteros.
7- FUNCIONES
7.1.- SINTAXIS. PASO POR VALOR Y PASO POR REFERENCIA
Las funciones son bloques con los que se constituyen los programas en C. El formato general de una función es el siguiente:
nombre_función (lista de parámetros)
declaraciones de parámetros;
{
cuerpo de la función;
}
Las funciones devuelven un valor, que puede ser especificado por la sentencia return o, si no se especifica un valor, devuelve 0. Hay que distinguir entre las funciones de librería y las funciones definidas por el usuario. Todos los programas en C tienen al menos una función de usuario: el main(). Las funciones pueden ser llamadas desde otras funciones de dos formas distintas: por valor y por referencia. Para ilustrar cómo hace una llamada por valor, vease el ejemplo5:
#include <stdio.h>
void intercambio(int a,int b);
void main()
{
int x,y;
x=777;
y=10;
printf("Valor inicial: x=%d y=%d\n",x,y);
y=intercambio(x,y);
printf("Despues del intercambio: x=%d y=%d\n",x,y);
}
void intercambio(int a,int b)
{
int aux;
aux=a;
a=b;
b=aux;
printf("Dentro de intercambio: x=%d y=%d\n",a,b);
}
En realidad esta función sólo intercambia los valores dentro de la función intercambio, ya que a y b son variables locales y no se pasan a la función main(). Para conseguir que los valores se cambien también en la función main recurrimos al paso por referencia: (ejemplo6)
#include <stdio.h>
void intercambio(int *a,int *b); /*declaración de la función intercambio*/
void main()
{
int x,y;
x=777;
y=10;
printf("Valor inicial: x=%d y=%d\n",x,y);
printf("Direcci¢n de memoria: &x=%p &y=%p\n",&x,&y);
intercambio(&x,&y); /*llamada a intercambio,pasandole los punteros de x e y*/
printf("\nDespués del intercambio: x=%d y=%d\n",x,y);
printf("Dirección de memoria: &x=%p &y=%p\n",&x,&y);
}
void intercambio(int *a,int *b)
{
int aux;
aux=*a;
*a=*b;
*b=aux;
printf("\nDentro de intercambio: x=%d y=%d\n",*a,*b);
printf("Direcci¢n de memoria: &x=%p &y=%p\n",&a,&b);
}
Se han incluido las direcciónes de memoria de las variables x e y para que se pueda ver que realmente se intercambian los contenidos de los punteros, mientras que estos no son afectados. Para ver un ejemplo de paso por valor y por referencia en el mismo programa, ver prog11.
7.2.- L LAMADA DE FUNCIONES CON ARREGLOS
Un elemento de un array utilizado como un argumento se trata igual que cualquier otra variable sencilla. También podemos pasar todo un arreglo a una función simplemente pasando la dirección del primer elemento del arreglo. Considérese el ejemplo7:
#include <stdio.h>
void visualiza (int *numero);/*declaración de la función visualiza*/
void main()
{
static int arreglo[10]={1,2,3,4,5,6,7,8,9,0};/*definimos los elementos del arreglo*/
int i;
visualiza(arreglo);
}
void visualiza(int *numero) /*función visualiza*/
{
int i;
for(i=0;i<10;i++)
{
printf("%d ",numero[i]);/*imprime los elementos del arreglo*/
}
}
Finalmente, para darnos cuenta de la importancia de las funciones para la legibilidad y claridad de los programas, y para facilitar su modificación consideremos el siguiente problema:
En el prog8 habíamos conseguido descomponer cualquier número tipo unisigned int en factores primos. Pero esto no era del todo cierto, ya que sólo habíamos definido 7 factores primos (hasta el 17), y números como el 961 eran considerados como irreducibles, ya que el programa considera irreducible cualquier número que no sea divisible por un primo hasta 17. La forma de mejorar el programa es aparentemente sencilla: calcular primero todos los números primos hasta el dado y luego dividirlo por cada uno de ellos. Entonces veríamos que 961 es divisible por 31, y por tanto no es primo. En el prog12 tenemos un posible algoritmo para el cálculo de números primos hasta uno dado (a nosotros no se nos ocurrió otro mejor). Pero el problema surge a la hora de integrar este programa dentro del anterior: necesitamos guardar los números primos en un arreglo. Pero no sabemos a priori cuantos números primos va a haber hasta el final del programa, así que necesitamos una asignación dinámica de memoria. Aún así, no podremos guardar los números en el arreglo a la vez, a no ser que hagamos una asignación de memoria individual para cada elemento (lo cual intentamos pero no fue posible, ya que en cada asignación se borran todos los datos guardados). Así que necesitamos que el programa haga dos veces la misma operación: una para saber cuanta memoria necesitamos y otra para guardar los valores. Además, y puestos a añadir, podemos hacer que el usuario elija entre visualizar simplemente los números primos o descomponer también en factores el número que introduce. Si hacemos todo esto de forma desordenada, improvisando centinelas y contadores sobre la marcha, abusando del goto, sin emplear funciones como el switch, que facilitarían la legibilidad, etc, tenemos el prog13. El prog13 funciona, pero dos horas después de terminarlo no tienes ni idea de por qué.
Pues bien, ahora que conocemos la programación modular y el paso de arreglos a funciones, vamos a intentar que el prog13 sea mucho más fácil de leer y sirva de compendio de casi todo lo estudiado hasta ahora. Así conseguimos el prog14.
Analizando detenidamente el prog14 vemos que, en principio es más largo. Además, las funciones de calcular primos, guardar y descomponer se parecen mucho, pero hay una mejora importante: la función main es mucho más corta y fácil de seguir, y en general todo el programa es mucho mas legible (con excepción de la dificultad añadida de las variables locales). Además, hemos añadido sin dificultad una tercera opción al programa simplemente borrando una linea del case 1 y pasando opcion como parámetro. En el prog13 sería mucho más dificil añadir cualquier cosa sin perderse. Por tanto, vemos que trabajar con funciones es indispensable para programar en C. Para ver además un programa que trabaja con matrices y funciones, ver prog15.
8.- ORDENAR UN ARREGLO
8.1.- ¿QUÉ ES ORDENAR?:
Ordenar es un acto intuitivo que consiste en organizar un numero determinado de elementos de una forma tal que ,cuando vayan a ser recuperados para su utilización, sean fácilmente localizables. De una forma más matemática, ordenar consiste en:
Dados n elementos {a1,a2,...,an} permutarlos formando un nuevo conjunto {ak1,ak2,...,akn} de tal forma que dada una función f de ordenación se verifique:{ f(ak1) f(ak2) ... f(akn)}.
8.2-¿POR QUÉ EXISTEN TANTOS ALGORITMOS DE ORDENACIÓN?:
La razón simplemente es que, al igual que en las matemáticas, casi ningún problema tiene un sólo método de resolución. No obstante siempre existe uno mejor que otro (más corto y fácil de operar) en función de los datos de que se dispone y el resultado al que se quiere llegar. Esto que ocurre en matemáticas ocurre por igual en la programación, ya que unos algoritmos poseen determinadas ventajas frente a otros en función de lo que se quiera hacer.
La ordenación de arrglos se realiza desde la memoria interna rápida del ordenador. De esta forma se puede disponer de todos los elementos a ordenar cuando queramos. Como ejemplo intuitivo supongamos que disponemos de una pequeña mesa y queremos ordenar un palo de la baraja de menor a mayor. Como no tenemos un elevado número de elementos podemos ponerlos todos a nuestra vista sobre la mesa y comenzar a ordenarlos.
ð Ordenación por selección directa: (Véase Seleccio.C)
Es el método que normalmente usamos para ordenar una lista de forma directa. Consiste en coger el primer elemento y compararlo con el resto de los elementos. Cuando encuentra un elemento menor se coge ese elemento y se sigue comparando. Una vez comparados todos se coloca el elemento más pequeño en el lugar en el que estaba el elemento inicial a comparar, y este (el inicial) se coloca donde estaba el menor. Como ejemplo obsérvese la siguiente tabla:
Lista desordenada | 3 | 4 | 6 | 5 | 2 | 1 |
1º iteración | 3 | 4 | 6 | 5 | 2 | 1 |
2º iteración | 1 | 4 | 6 | 5 | 2 | 3 |
3º iteración | 1 | 2 | 6 | 5 | 4 | 3 |
4º iteración | 1 | 2 | 3 | 5 | 4 | 6 |
5º iteración | 1 | 2 | 3 | 4 | 5 | 6 |
Lista ordenada | 1 | 2 | 3 | 4 | 5 | 6 |
El elemento en negrita es el que se coge y se compara.
El proceso seguido en la tabla es el siguiente:
1. Se coge el primer elemento (3) y se compara:
ð Con el 4: Al ser mayor no intercambia el valor y pasa al siguiente.
ð Con el 6: Al ser mayor no intercambia el valor y pasa al siguiente.
ð Con el 5: Al ser mayor no intercambia el valor y pasa al siguiente.
ð Con el 2: Al ser menor que (3) se coge el valor de 2 y se compara con los restantes.
ð Con el 1: Al ser menor que (2) se coge el valor 1 y se compara con el resto. Como es el menor se le coloca en la 1º posición.
2. El resto de las iteraciones siguen el mismo proceso pero cada vez se va cogiendo un elemento más avanzado ya que el anterior ya ha quedado ordenado.
CÓDIGO DE LA FUNCIÓN DEL MÉTODO SELECCIÓN DIRECTA EN C
/*Declaración de la funcion*/
void Seleccion(int *,int);
void Seleccion(int *lista,int num_elem)
{
int i,j,aux,pos_min=0;
int minimo;
/*Coge el elemento a comparar; cada vez una posición más avanzado*/
for (i=0;i<num_elem;i++)
{
minimo=lista[i];
/*Recorre el arreglo desde la posición del elemento inicial hasta el final*/
pos_min=i;
for(j=i;j<num_elem;j++)
{
/*Si encuentra uno menor que el valor que cogió en un principio coge ese valor y su posición para seguir comparando*/
if(minimo>lista[j]) {
minimo=lista[j];
pos_min=j;
}
}
*/Intercambia la posición del valor que comparaba en un principio con la del menor encontrado*/
aux=lista[i];
lista[i]=minimo;
lista[pos_min]=aux;
}
}
ð Ordenación por inserción directa: (Véase Insercio.C)
Este método es el usado por los jugadores de cartas y consiste en coger el 2º elemento del arreglo y compararlo con el anterior y si este es mayor intercambiarlos y seguir comparando realizando el mismo proceso hasta el final de la lista. Ilustremos el mismo ejemplo de antes.
Lista desordenada | 3 | 4 | 6 | 5 | 2 | 1 |
1º iteración | 3 | 4 | 6 | 5 | 2 | 1 |
2º iteración | 3 | 4 | 6 | 5 | 2 | 1 |
3º iteración | 3 | 4 | 6 | 5 | 2 | 1 |
3 | 4 | 5 | 6 | 2 | 1 | |
3 | 4 | 5 | 2 | 6 | 1 | |
4º iteración | 3 | 4 | 2 | 5 | 6 | 1 |
3 | 2 | 4 | 5 | 6 | 1 | |
2 | 3 | 4 | 5 | 6 | 1 | |
2 | 3 | 4 | 5 | 6 | 1 | |
2 | 3 | 4 | 5 | 1 | 6 | |
2 | 3 | 4 | 1 | 5 | 6 | |
5º iteración | 2 | 3 | 1 | 4 | 5 | 6 |
2 | 1 | 3 | 4 | 5 | 6 | |
1 | 2 | 3 | 4 | 5 | 6 | |
Lista ordenada | 1 | 2 | 3 | 4 | 5 | 6 |
ð Coge el 4 y compara con el 3 y como no es mayor no hace nada
ð Coge el 6 y compara con el 4 y como no es mayor no hace nada
ð Coge el 5 y compara con el 6 y como no es mayor no hace nada
ð Coge el 2 y compara con el 6 y como es mayor se intercambian
ð Coge el 2 y compara con el 5 y como es mayor se intercambian
ð Coge el 2 y compara con el 4 y como es mayor se intercambian
ð Coge el 2 y compara con el 3 y como es mayor se intercambian
ð Coge el 2 y no compara más porque se ha acabado el arreglo.
ð Coge el 1 y compara con el 6 y como es mayor se intercambian.
ð Coge el 1 y compara con el 5 y como es mayor se intercambian.
ð Coge el 1 y compara con el 4 y como es mayor se intercambian.
ð Coge el 1 y compara con el 3 y como es mayor se intercambian.
ð Coge el 1 y compara con el 2 y como es mayor se intercambian.
ð Coge el 1 y no compara más porque se ha acabado el arreglo.
El rendimiento de este método puede mejorarse considerablemente mediante la utilización del método de búsqueda binaria. Si uno se da cuenta de que la secuencia a1,.....,ai-1 está siempre ordenada, se puede agilizar el método buscando el lugar adecuado de inserción. El método de búsqueda binaria parte del elemento central de a1,.....,ai-1 y mediante bisección encuentra el lugar de inserción. (Véase Insbinar.C)
CÓDIGO DE LA FUNCIÓN DEL MÉTODO INSERCIÓN EN C
/*Declaración de la función*/
void Insercion(int *,int);
void Insercion(int *lista,int num_elem)
{
int i,j,actual;
/*Coge el elemento a comparar desde el 2º hasta el final cada vez una posición más avanzado*/
for (i=1;i<num_elem;i++)
{
/* Va comparando “actual” con los anteriores e intercambiándose hasta que encuentra uno que no es mayor que él o hasta que llega al final del arreglo*/
actual=lista[i];
for(j=i;actual<lista[j-1] && j>0;j---)
{
lista[j]=lista[j-1];
}
lista[j]=actual;
}
}
CÓDIGO DE LA FUNCIÓN DEL MÉTODO DE INSERCIÓN BINARIA EN C
/*Declaración de la función*/
void InsercionBinaria(int *,int);
void InsercionBinaria(int *lista,int num_elem)
{
int i,j,izq,der,cent,aux;
for (i=0;i<num_elem;i++)
{
aux=lista[i];
izq=0;
der=i-1;
/*Busca el lugar donde insertar el elemento. Coge el elemento central. Si el elemento a comparar es menor disminuye el lugar por la derecha, sino aumenta por la izquierda . Así hasta que encuentra dos elementos entre los que coloca el elemento a insertar*/
while(izq<der)
{
cent=(izq+der)/2;
if(aux<lista[cent])
{
der=cent-1;
}
else
{
izq=cent+1;
}
}
/*Va intercambiando el elemento hasta ese lugar*/
for(j=i-1;j>=izq;j--)
{
lista[j+1]=lista[j];
}
lista[izq]=aux;
}
}
ð Ordenación por intercambio directo: (Véase Burbuja.C)
Este método,también llamado de la burbuja) se basa en la comparación e intercambio dos a dos. Veamos un ejemplo ilustrativo:
Lista desordenada | 3 | 4 | 6 | 5 | 2 | 1 |
j=0
i=0 | 3 | 4 | 6 | 5 | 2 | 1 |
i=1 | 3 | 4 | 6 | 5 | 2 | 1 |
i=2 | 3 | 4 | 6 | 5 | 2 | 1 |
i=3 | 3 | 4 | 5 | 6 | 2 | 1 |
i=4 | 3 | 4 | 5 | 2 | 6 | 1 |
FIN | 3 | 4 | 5 | 2 | 1 | 6 |
j=1
i=0 | 3 | 4 | 5 | 2 | 1 | 6 |
i=1 | 3 | 4 | 5 | 2 | 1 | 6 |
i=2 | 3 | 4 | 5 | 2 | 1 | 6 |
i=3 | 3 | 4 | 2 | 5 | 1 | 6 |
FIN | 3 | 4 | 2 | 1 | 5 | 6 |
j=2
i=0 | 3 | 4 | 2 | 1 | 5 | 6 |
i=1 | 3 | 4 | 2 | 1 | 5 | 6 |
i=2 | 3 | 2 | 4 | 1 | 5 | 6 |
FIN | 3 | 2 | 1 | 4 | 5 | 6 |
j=3
i=0 | 3 | 2 | 1 | 4 | 5 | 6 |
i=1 | 2 | 3 | 1 | 4 | 5 | 6 |
FIN | 2 | 1 | 3 | 4 | 5 | 6 |
j=4
i=0 | 2 | 1 | 3 | 4 | 5 | 6 |
LISTA ORDENADA | 1 | 2 | 3 | 4 | 5 | 6 |
El funcionamiento básico es coger el primer elemento y compararlo con el siguiente. Si este es menor se intercambian. En la siguiente iteración coge el segundo y lo compara con el siguiente. Así hasta el final del arreglo. Para que el método sea más rápido, hay que tener en cuenta que en cada iteración queda ordenado un elemento y no es necesario volver a compararlo en la siguiente iteración.
CÓDIGO DE LA FUNCIÓN DEL MÉTODO DE LA BURBUJA DE EN C
void Burbuja(int *,int);
/*Declaración de la función*/
void Burbuja(int *lista,int num_elem)
{
int i,j,aux;
for (j=0;j<num_elem-1;j++)
{
for(i=0;i<num_elem-j-1;i++)
{
/*Intercambia elementos*/
if(lista[i]>lista[i+1])
{
aux=lista[i];
lista[i]=lista[i+1];
lista[i+1]=aux;
}
}
}
}
ð Ordenación por inserción con incrementos decrecientes:(Véase Shell.C)
Este método se basa en la comparación y ordenación de elementos distantes x y también se denomina método de Shell en honor a su inventor. Esta distancia se llama salto. La comparación se va realizando con el mismo salto hasta que ya no se produzca ningún cambio; en ese momento se cambia el salto y realiza el mismo proceso. Como ejemplo veamos la siguiente ordenación:
Lista desordenada | 3 | 4 | 6 | 5 | 2 | 1 |
Cogeremos el salto =número de elementos/2. El salto en primer lugar es por tanto 3. Para coger los siguientes saltos iremos dividiendo por dos los anteriores, es decir salto1=salto/2.(Conviene recordar que se trata de una división entera y por tanto 3/2=1)
Salto=3 | 3 | 4 | 6 | 5 | 2 | 1 |
3 | 4 | 6 | 5 | 2 | 1 | |
3 | 2 | 6 | 5 | 4 | 1 | |
Fin 1ª vuelta. Como hay intercambios da otra vuelta más. | 3 | 2 | 1 | 5 | 4 | 6 |
3 | 2 | 1 | 5 | 4 | 6 | |
3 | 2 | 1 | 5 | 4 | 6 | |
Fin 2ª vuelta. Como no hay intercambios, se cambia el valor del salto. | 3 | 2 | 1 | 5 | 4 | 6 |
Salto=1 (igual que el método de burbuja) | 3 | 2 | 1 | 5 | 4 | 6 |
2 | 3 | 1 | 5 | 4 | 6 | |
2 | 1 | 3 | 5 | 4 | 6 | |
2 | 1 | 3 | 5 | 4 | 6 | |
2 | 1 | 3 | 4 | 5 | 6 | |
2 | 1 | 3 | 4 | 5 | 6 | |
Fin 1ª vuelta. Como hay intercambios da otra vuelta más. | 2 | 1 | 3 | 4 | 5 | 6 |
1 | 2 | 3 | 4 | 5 | 6 |
Ahora continuaría comparando el resto de elementos dos a dos y aún haría otra pasada en la que ya no habría cambios.
CÓDIGO DE LA FUNCIÓN DEL MÉTODO DE SHELL EN C
/*Declaración de la función*/
void Shell(int *,int);
void Shell(int *lista,int num_elem)
{
int salto=num_elem,aux,hay_cambio,i;
do
{
salto/=2;
do
{
hay_cambio=0;
for(i=0;(i+salto)<num_elem;i++)
{
/*Intercambio*/
if(lista[i]>lista[i+salto])
{
aux=lista[i];
lista[i]=lista[i+salto];
lista[i+salto]=aux;
hay_cambio=1;
}
}
}while (hay_cambio);
}while (salto>1);
}
ð El programa comienza ordenando los elementos separados por una distancia que viene dada por el salto. Una vez ordenados dichos elementos, la variable hay_cambio toma el valor 0, con lo que el programa sale del bucle de ordenación y divide el salto por 2, ordenando de nuevo los elementos separados por el nuevo salto, y así sucesivamente hasta que el salto=1 y termina de ordenar por el método de la burbuja (de una sola pasada) los elementos que falten.
ð Ordenación por partición: (Véase Quicksor.C)
Este método, también denominado Quicksort por su rapidez, consiste en tomar un elemento (pivote) del arreglo y dividir la lista en dos: una parte con los elementos mayores que el pivote y otra parte con los menores y una vez obtenidas estas dos partes ordenarlos por el método de la burbuja. Para obtener las dos listas se sigue el siguiente procedimiento:
Una vez tomado el pivote (usaremos como pivote el primer elemento de la lista), se recorre el arreglo de izquierda a derecha hasta encontrar un elemento mayor que el pivote y entonces se busca de derecha a izquierda hasta encontrar uno menor. A continuación se intercambian los dos elementos y se continua este proceso hasta hallar un elemento en el que confluyan los dos recorridos. Este elemento se supone que estará colocado aproximadamente en la mitad del arreglo.
Como ejemplo ilustrativo véase la siguiente tabla:
Lista desordenada | 3 | 4 | 6 | 5 | 2 | 1 |
Pivote=3 | 4 | 6 | 5 | 2 | 1 |
izquierda (3>4) | derecha (1<3) | ||||
1 | 6 | 5 | 2 | 4 | |
izquierda (3>6) | derecha (2<3) | ||||
1 | 2 | 5 | 6 | 4 | |
izq=der |
La lista queda dividida en dos partes aproximadamente iguales (en este caso iguales):
Lista A | 1 | 2 | 3 |
Lista B | 5 | 6 | 4 |
Ahora estas dos listas se ordenan por separado mediante cualquier otro método. En nuestro ejemplo utilizaremos el de Shell por ser el más potente de los anteriormente descritos.
CÓDIGO DE LA FUNCIÓN DEL MÉTODO QUICKSORT EN C
void Quicksort(int *lista,int num_elem)
{
int *listaA,*listaB,pivote=lista[0];
/*Reserva de dinámica de memoria. En el peor de los casos no habrá división y la lista más grande tendrá el tamaño de la original*/
int tam_listaA=0,tam_listaB=0,i;
listaA=(int*)malloc(num_elem*sizeof(int));
listaB=(int*)malloc(num_elem*sizeof(int));
if (listaA==NULL)
{
printf (“Error en asignación de memoria de la listaA”);
}
if (listaB==NULL)
{
printf (“Error en asignación de memoria de la listaB”);
}
for(i=0;i<num_elem;i++)
{
/* Manda los elementos menores o iguales que el pivote a la listaA*/
if(lista[i]<=pivote)
{
listaA[tam_listaA]=lista[i];
tam_listaA++;
}
/* Manda los elementos mayores que el pivote a la listaB*/
else
{
listaB[tam_listaB]=lista[i];
tam_listaB++;
}
}
/* Ordena cada lista por separado mediante el método de Shell (puede hacerse con otro cualquiera)*/
Shell (listaA,tam_listaA);
Shell (listaB,tam_listaB);
/* Manda los elementos (ya ordenados) de la listaA a la las primeras posiciones de la lista inicial*/
for (i=0;i<tam_listaA;i++)
{
lista[i]=listaA[i];
}
/* Manda los elementos (ya ordenados) de la listaB a la las posiciones no ocupadas por listaA de la lista inicial*/
for (i=0;i<tam_listaB;i++)
{
lista[i+tam_listaA]=listaB[i];
}
/* Libera la memoria reservada para listaA y listaB*/
free(listaA);
free(listaB);
}
El método quicksort se hace tanto más eficiente cuanto más parecidos son los tamaños de las sublistas. Estos tamaños dependen del pivote que se elija. Así cuando el pivote sea el elemento que queda en el centro de la lista el método alcanzará su mayor grado de eficiencia. Definamos ese valor ideal del pivote como centro=num_elem/2. A este elemento ideal se le denomina mediana y se define como el elemento del arreglo que es menor (o igual) que la mitad de los elementos y es mayor (o igual) que la otra mitad. Por ejemplo la mediana del arreglo 2,1,3,5,4 es el 3. Dependiendo de cómo sea el pivote respecto a la mediana se tienen los siguientes tres casos:
1) Si el pivote es demasiado pequeño:
Cuando esto ocurre el valor centro deseado es mayor que el pivote con el que se ha partido la lista. Para resolverlo debería repetirse el proceso de división para los elementos a la derecha del pivote. Esto puede expresarse mediante el siguiente diagrama:
izquierda j i centro derecha
2) Si el pivote es demasiado grande:
En este caso centro es menor que el pivote escogido. Por tanto la partición queda desequilibrada y para equilibrarla hay que volver a partir los elementos situados a la izquierda del pivote. El diagrama que representa esta situación es:
izquierda centro j i derecha
3) Si el pivote es el adecuado:
Que el pivote sea el adecuado significa que parte el arreglo en dos listas iguales de tamaño. Si esto sucede es porque el pivote es exactamente la mediana. El esquema es el siguiente:
izquierda j i derecha
centro
CÓDIGO DE LA FUNCIÓN PARA LA OBTENCIÓN DE LA MEDIANA EN C
(Véase Mediana.C)
/*Declaración de la función*/
int Mediana(int *,int);
int Mediana(int *lista,int num_elem)
{
int izquierda,derecha,i,j,aux,centro;
izquierda=0;
derecha=num_elem;
centro=(num_elem/2);
while (izquierda<derecha)
{
i=izquierda;
j=derecha;
do
{
/*Traslada el índice extremo izquierdo hacia la derecha*/
while (lista[i]<lista[centro])
{
i++;
}
/*Traslada el índice extremo derecho hacia la izquierda*/
while (lista[centro]<lista[j])
{
j--;
}
/*Intercambio*/
if (i<=j)
{
aux=lista[i];
lista[i]=lista[j];
lista[j]=aux;
i++;
j--;
}
/*Establece los nuevos límites del arreglo */
}while (i<=j);
if (j<centro)
{
izquierda=i;
}
if (centro<i)
{
derecha=j;
}
}
return (lista[centro]);
}
Aplicando este método para obtener el pivote se obtiene el Quicksort mejorado. (Véase Qsortmej.C) COMPARACIÓN DE LOS DISTINTOS MÉTODOS DE ORDENACIÓN
La eficiencia de un método de ordenación de arreglos puede medirse en función del número C de comparaciones y M de intercambios de elementos. En primer lugar compararemos entre si a los tres métodos directos (selección, inserción, intercambio) que, si bien no son los mejores, poseen algunas ventajas sobre los más sofisticados:
1.Sus programas son cortos y fáciles de entender. Por tanto no ocupan mucha memoria.
2. Son suficientemente rápidos para un número de elementos suficientemente pequeño. Sin embargo son enormemente lentos para arreglos de un tamaño grande.
La siguiente tabla muestra los valores mínimos, máximos y medios de comparaciones ( C ) e intercambios (M) en función del número de elementos (n) de los distintos métodos dirtectos:
MÍNIMO | MEDIO | MÁXIMO | |
Selección | C= (n2-n)/2 | C= (n2-n)/2 | C= (n2-n)/2 |
directa | M= 3(n-1) | M= n(ln n+ 0.57) | M= n2/4+3(n-1) |
Inserción | C= n-1 | C= (n2+n-2)/4 | C= (n2-n)/2-1 |
directa | M= 2(n-1) | M= (n2-9n-10)/4 | M= (n2+3n-4)/2 |
Intercambio | C= (n2-n)/2 | C= (n2-n)/2 | C= (n2-n)/2 |
directo | M= 0 | M= (n2-n)x0.75 | M= (n2-n)x1.5 |
Todos estos métodos elementales realizan un número de movimientos del orden de n2, ya que esencialmente mueven cada elemento una posición en cada paso. Así pues cualquiera de los otros métodos avanzados tienen como principal característica el hecho de que mueve los elementos una distancia mayor de cada vez.
No obstante los métodos avanzados poseen tal grado de sofisticación que no es posible encontrar una fórmulas matemáticas suficientemente comprensibles. Las relaciones más relevantes para expresar el esfuerzo de computación de estos métodos son cin1.2 para el método de Shell (cuando el salto se toma como ...31,15,7,3,1) y cilog (n) para el quicksort y del montículo. A los métodos avanzados se les denomina también logarítmicos en ocasiones, por ser su esfuerzo de computación del orden de n log (n).
MÉTODO | ESTADO DEL ARREGLO | |||||
ORDENADO | ALEATORIO | INVERSO | ||||
Nº de elementos | 10000 | 5000 | 10000 | 5000 | 10000 | 5000 |
Selección | 10,934066 | 2,582418 | 10,989011 | 2,637363 | 13,461538 | 3,186813 |
Burbuja | 15,989011 | 3,846154 | 25,879121 | 6,318681 | 38,956044 | 9,560440 |
Inserción | 0 | 0 | 9,175824 | 2,252747 | 19,065934 | 4,615385 |
Inserción Binaria | 0,109890 | 0,054945 | 6,593407 | 1,593407 | 13,461538 | 3,241758 |
Shell | 0,054945 | 0 | 0,439560 | 0,164835 | 0,164835 | 0,054945 |
Quicksort | 0,054945 | 0 | 0,329670 | 0,169890 | 0,109890 | 0,054945 |
Quicksort mejorado | 0,054945 | 0 | 0,274725 | 0,109890 | 0,054945 | 0 |
Para una mejor comprensión de cuáles son los mejores métodos veánse la siguiente tabla experimentale realizada en un ordenador 486/DX4. Los tiempos que en ella aparecen han sido medidos en segundos.
ð Se pude observar claramente que el peor método de ordenación es el de la burbuja en cualquiera de los casos expuestos.
ð También se ve como la mejora del método de inserción binaria sobre el de inserción es bastante significativa cuando el número de elementos es elevado aunque contraproducente en el improbable caso de querer ordenar un arreglo ya ordenado.
ð Comparando los métodos directos entre sí llegamos a la conclusión de que el mejor, en caso de que el arreglo esté en orden inverso, es el de selección, mientras que para el resto de los casos lo esel de inserción (observaciones que podían ser predichas en la anterior tabla).
ð Comparando el método Quicksort y el Quicksort mejorado llegamos a la conclusión de que la mejora no es muy significativa.
ð El Quicksort es, de todos los métodos (en especial de los avanzados), el mejor aunque no se separa mucho del de Shell.
9.- BUSCAR UN ELEMENTO
En ocasiones es necesario encontrar un determinado elemento dentro de un arreglo. Existen para este fin dos métodos principales:
ð Búsqueda secuencial.
ð Búsqueda binaria.
Estas dos funciones se encuentran en el archivo Buscar.C.
9.1.- BÚSQUEDA SECUENCIAL:
Este método es muy intuitivo y consiste simplemente en introducir el elemento a buscar y hacer que el programa recorra el arreglo elemento por elemento hasta encontrar el buscado y, una vez encontrado que nos devuelva su posición dentro del arreglo.
La función para este método es la siguiente:
/*Declaración de la función*/
int BusqSecuencial(int *,int,int);
void BusqSecuencial (int *lista, int tam, int buscado, int *posicion)
{
int i,j=0;
for(i=0;i<tam;i++)
{
if (lista[i]==buscado)
{
posicion[j]=i;
j++;
}
if (j==0)
{
printf (“Ese elemento no está en el arreglo.\n);
exit (0);
}
}
if (lista[0]==buscado)
{
posicion[0]=-1;
}
}
Esta función introduce en un arreglo las posiciones en las que aparece el elemento. Para aplicar esta función hay que tener en cuenta que si el elemento buscado se encuentra en la posición 0 de la lista, el valor dentro del arreglo posición será -1. Además debe tenerse en cuenta que todas aquellas posiciones del arreglo posicion en las que no se halla incluido ningún valor tendrán por defecto el valor 0 (para ello es recomendable el uso de calloc par la reserva de memoria).
9.2.- BÚSQUEDA BINARIA:
Este método, en el cual se apoya la inserción binaria, procede cogiendo un elemento situado en el medio del arreglo. Si el elemento buscado es menor que el situado en el centro, situa el extremo derecho en el elemento anterior al central; por contra si es mayor, situa el extremo izquierdo en la posición siguiente a la del elemento central. Como se puede deducir de este algoritmo, para poder aplicar la búsqueda binaria es totalmente imprescindible que el arreglo este ordenado.
/*Declaración de la función*/
El código de la función es el siguiente:
void BusqBinaria(int *,int,int ,int *)
void BusqBinaria(int *arreglo,int tam,int buscado,int *posicion)
{
int bajo,central,alto;
int i,j,z=0;
bajo=0;
alto=tam;
/*Va partiendo el arreglo para encontrar el elemento buscado*/
central=0;
while (bajo<=alto&&arreglo[central]!=buscado)
{
central=(alto+bajo)/2;
if (buscado<arreglo[central])
{
alto=central-1;
}
else
{
bajo=central+1;
}
}
j=central;
/*Busca a la izquierda de central por si el elemento estuviese repetido*/
while (arreglo[j]==buscado&&j>=0)
{
if (j==0&&j>=0)
{
posicion[z]=-1;
j--
}
else
{
posicion[z]=j;
z++;
j--;
}
}
if (arreglo[central]!=buscado)
{
printf (“Ese elemento no está en la lista o el arreglo no está ordenado.\n”);
}
j=central+1;
z++;
/*Busca a la derecha de central por si el elemento estuviese repetido*/
while (arreglo[j]==buscado&&j<=tam)
{
posicion[z]=j;
z++;
j++;
}
}
Al igual que en la función para la búsqueda secuencial, esta función introduce las posiciones en las que se encuentra el elemento en un arreglo (posicion). El arreglo en el que se introducen las posiciones tiene las mismas peculiaridades que antes;es decir:
ð Si el elemento se encuentra en la posición 0 del arreglo, el arreglo posicion contendrá el valor -1.
ð Todos las posiciones del arreglo posicion en las que no se haya introducido un valor tendrán el valor 0 por defecto.(Se debe usar la función calloc para reservar la memoria del puntero y así inicializar todas las posiciones a 0.).
Estas dos peculiaridades son las que hay que tener en cuenta a la hora de usar esta función (además de que el arreglo debe estar ordenado) para usarla. Como ejemplo vease el proyecto Binaria.prj incluido en el disquete.
¿Qué método es mejor?:
A la vista de los resultados obtenidos al comparar los métodos de ordenación, es lógico pensar que el método de búsqueda binaria será mucho más rápido que el de búsqueda secuencial. Como ya dijimos, un método que compara elemento por elemento es mucho más lento que aquel que lo hace entre elementos más distantes porque el número de comparaciones se hace muy elevado.
| NÚMERO DE COMPARACIONES |
Búsqueda secuencial | (N+1)/2 |
Búsqueda binaria | (1+log2N)/2 |
Como ejemplos de programas de ordenación y búsqueda véanse los programas Prog16.C y Prog17.C además del proyecto Poblacio.prj.
10.- CONSERVAR LOS DATOS
En muchas ocasiones los datos que usamos para ejecutar un determinado programa son siempre los mismos. En esos casos no es muy práctico tener que introducir siempre los mismos datos una y otra vez. Otras veces nos puede interesar conservar los datos obtenidos después de ejecutar el programa para futuras aplicaciones.
Con lo que hemos estudiado hasta ahora no podemos hacer esto, ya que cada vez que el programa terminaba de ejecutarse se borraban los datos que estaban en la memoria. La mejor solución sería poder guardar esos datos en dispositivos periféricos (disquetes, CD-ROMs, disco duro...)en forma de archivos.
Bien esto va a ser lo que estudiemos ahora: ARCHIVOS.
En primer lugar hay que destacar que los programas que utilizan archivos son más lentos que aquellos que cogen los datos directamente de la memoria.
Existen dos tipos principales de archivos: los de texto y los binarios. Existen algunas diferencias entre estos dos tipos de archivos pero ambos se abren igual.
10.1.- APERTURA , CREACIÓN Y CIERRE DE ARCHIVOS:
Para abrir o crear un archivo lo primero que hay que hacer es declarar un “puntero” (aunque no es exactamente un puntero) de archivos que apunte al lugar donde se encuentra el archivo en el dispositivo periférico:
FILE *puntero; (El prototipo de FILE viene definido en stdio.h>
Para abrir el archivo se utiliza la función fopen definida en stdio.h de la siguiente forma:
puntero=fopen(“nombre_archivo”, “tipo_archivo”);
El “nombre_archivo” puede ser una variable introducida por el usuario.
Los diferentes tipos de archivo que se pueden abrir vienen definidos en la siguiente tabla:
Tipo de archivo | Significado |
“r” | Abre un archivo ya existente en modo de lectura |
“w” | Crea un archivo en modo escritura. Si el archivo que se abre ya existe, el contenido anterior de éste será borrado por completo. |
“a” | Abre un archivo para añadir datos al final. Si el archivo a abrir no existe se creará uno nuevo. |
“r+” | Abre un archivo ya existente tanto para lectura como para escritura |
“w+” | Crea un archivo nuevo para lectura y escritura. En caso de abrir un archivo ya existente se borrará la información |
“a+” | Abre un archivo existente para leer y añadir. Si el nombre especificado no corresponde a un archivo existente se abrirá uno nuevo. |
Para cerrar un archivo se utiliza la función fclose definida en stdio.h. Su uso es de la siguiente forma:
fclose(puntero);
Es importante reseñar que antes de terminar el programa hay que cerrar todos los archivos abiertos.
La principal diferencia entre los archivos de texto y los binarios radica en la forma de escribir y leer los datos en ellos contenidos:
10.2.- Archivos de texto:
Estos archivos contienen caracteres consecutivos. Estos caracteres pueden ser tanto letras(forman cadenas de caracteres) como números (pero números vistos como caracteres). Las funciones que se utilizan par escritura y lectura son fprintf y fscanf respectivamente. Los parámetros de estas funciones son los siguientes:
fprintf (puntero,”%tipo de dato”,donde esta almacenado);
Ej: fprintf (original, “%d”, lista[2]);
fscanf (puntero,”%tipo de dato”,&donde lo mandamos);
Ej: fscanf (original,”%d”, &lista[2]);
Cuando usamos cualquiera de estas funciones, despues de escribir/leer el dato el puntero avanza automáticamente al siguiente elemento. Para avanzar a posiciones determinadas dentro del fichero existen varias funciones aunque la más destacable es fseek definida en stdio.h.
La sintaxis de la función es la siguiente:
fseek (puntero, nueva posición, desde donde empezar);
ð Nueva posición es la posicion contada a partir de desde_donde_empezar en bytes..(Es un valor de tipo long).
ð Desde donde empezar puede tener tres valores:
SEEK_SET:Busca desde el comienzo del archivo.
SEEK_CUR:Busca desde loa posición actual.
SEEK_END:Busca desde el final del archivo.
Existe otra función (ftell) que nos devuelve la posición en la que nos encontramos dentro del archivo en ese momento. Su sintaxis es:
ftell (puntero); (devuelve un valor de tipo long).
En muchas ocasiones queremos leer el fichero hasta el final, pero no sabemos donde esta éste. Para ello existe una función eof(puntero); definida en io.h que nos devuleve los siguientes valores:
ð 1 Cuando está al final del archivo.
ð 0 Si no está al final de la función.
ð -1 Si hay algún error.
Como ejemplos de archivos de texto véasen los programas Prog18.C, Prog19.C, Prog20.C y Prog21.C.
10.3.- Archivos binarios:
Estos constan de bloques de bytes contiguos. Son por tanto archivos escritos en código binatio como su nombre indica. En este tipo de archivos las funciones que de lectura y escritura que se usan son fwrite y fread. La sintaxis de estas funciones es:
fwrite (&var,tamaño_var,número_bloques,puntero);
ð Var: Puntero al bloque de datos.
ð Tamaño var: Tamaño del bloque de datos (sizeof()).
ð Número bloques:Número de bloques a trasferir.
ð Puntero: Puntero al archivo donde se archivaran los datos.
Otra de las diferencias de estos archivos es la forma de encontrar el final del archivo, para lo que se usa la función feof(puntero) (Esta función puede utilizarse tanto para archivos binarios como de texto). La función devuelve los siguientes valores:
ð 0 si no nos encontramos al final del archivo.
ð Cualquier otro valor si nos encontramos al final del archivo.
Las funciones de búsqueda (ftell,fseek) anteriormente mencionadas, sirven igualmente para este tipo de archivos. Sirvan como ejemplos de archivos binarios los programas Prog22.C, Prog23.C, Prog24.C y Prog25.C hechos con estructuras.
11.- ESTRUCTURAS
Una estructura es un tipo de variable como puede ser (int,floata,char...) que es definida por el usuario y cuya característica más importante es que está compuesta por miembros (elementos int, float, otras estructuras, arreglos..). La declaración de las estructuras se hace fuera del main de la siguiente forma:
struct biblioteca
{
char libro [ ];
char editorial [ ];
...
...
};
Una vez definida la estructura podemos declarar dentro del main variables que pertenezcan a ese tipo de dato:
main()
{
struct biblioteca nuevo; /*Dos variables de tipo natacion con todos los miembros que ella contiene*/
...
}
Una vez definida la estructura y declaradas las variables es sencillo llamar a cualquier miembro sin más que escribir nuevo.libro a la hora de mandar un dato a ese miembro.
Como ya se dijo anteriormente es posible declarar una estructura dentro de otra (siempre y cuando haya sido definida antes).
struct prestado
{
char nombre [20];
int dia;
int mes;
int año;
};
struct biblioteca
{
char libro [50];
char editorial [20];
struct prestado dejado;
};
A la hora de utilizar el nombre de alguien a quien se le ha prestado el libro, se escribiría: nuevo.dejado.nombre.
Como ejemplos véanse los programas Prog22.C, Prog23.C, Prog24.C y Prog25.C ya citados anteriormente.
APÉNDICE
Índice de programas
-
Prog1: Introduce una cadena decaracteres en un arreglo.
-
Prog2: Divide dos enteros y muestra el resto de su división entera.
-
Prog3: Comprueba si una expresión es cierta o falsa.(1 ó 0)
-
Prog4: Lee y escribe cadenas de caracteres con los comandos gets y puts.
-
Prog5: Suma tres números y los visualiza (printf con tres parámetros).
-
Prog6: Coge dos números y pregunta que operación desea hacer con ellos (sumar,restar,multiplicar, dividir y resto entero).
-
Prog7: Introduce caracteres en un arreglo hasta que el usuario introduce un valor determinado (`0') .
-
Prog8: Descompone un número en factores primos.
-
Prog9: Multiplica matrices de 3 * 3.
-
Prog10: Suma de matrices (con punteros).
-
Prog11: Intercambia el valor de dos variables.
-
Prog12: Calcula números primos.
-
Prog13: Calcula números primos y descompone en factores.
-
Prog14: Calcula números primos y descompone en factores (mediante funciones).
-
Prog15: Suma, multiplica o traspone matrices. (Mediante funciones)
-
Prog16: Obtine la clasificación de una prueba de natación.
-
Prog17: Ordena una lista de palabras de la A-Z o de la Z-A.
-
Prog18: Ordena una lista de palabras cogidas del archivo PALABRAS.TXT.
-
Prog19: Guarda los años de nacimiento de los individuos de una población en el archivo FECHAS.TXT.
-
Prog20: Programa que dice que tipo de país es y describe la pirámide de población a partir del archivo creado por Prog19.
-
Prog21: Programa que guarda estructuras en archivos de texto.
-
Prog22: Programa que recoge y guarda en un archivo binario (BILBIOTE.BIN)los datos de unos libros.
-
Prog23: Programa que visualiza los datos del archivo BIBLIOTE.BIN.
-
Prog24: Programa que añade datos al archivo BIBLIOTE.BIN.
-
Prog25: Programa que cambia los datos de algún registro del archivo BIBLIOTE.BIN.
Índice de proyectos
-
Binaria.prj: Proyecto que contiene los programas:
Binaria.c
Buscar.c
Para ejecutar el proyecto es necesario que el archivo Buscar.h se encuentre en tc\include.
Este proyecto busca las posiciones en las que se encuentra un elemento dentro de un arreglo ordenado.
-
Poblacio.prj: Proyecto que contiene los programas:
Poblacio.c
Buscar.c
Shell.c
Para ejecutar este proyecto es necesario que el archivo Buscar.h y el Ordena.h de encuentren en tc\include.
Este proyecto busca los individuos nacidos en un determinado año dentro de un arreglo y calcula el porcentaje que representa del total.
-
Speed.prj: Proyecto que contiene los programas:
Burbuja.c
Insbinar.c
Insercio.c
Mediana.c
Qsortmej.c
Quicksor.c
Seleccio.c
Shell.c
Velocida.c
Para ejecutar este proyecto es necesario que el archivo Ordena.h se encuentre en tc\include.
Este proyecto calcula el tiempo que tardan los distintos métodos de ordenación en ordenar un número de elementos.
Índice de archivos usados por los programas
• Palabras.txt: Lista de alabras usada por el prog18.c
• Fechas.txt: Archivo creado por el Prog19.c y usado opr el Prog29.c
• Bibliote.bin: Archivo creado por Prog22.c y usado por Prog23.c, Prog24.c y Prog25.cEjemplo1.c
#include <stdio.h>
void main ()
{
int numero=225;/*declaracion y asignacion de la cantidad 225 a la variable numero*/
int *punt_numero; /*declaracion de la variable punt_numero como puntero*/
punt_numero=№/*asignacion de la direccion de memoria de numero al puntero*/
printf("El contenido de la direcci¢n de memoria %x es %d\n",punt_numero,*punt_numero);
/*la direcci¢n de memoria se expresa como variable hexadecimal*/
}
Ejemplo2.c
#include <stdio.h>
void main()
{
static int array[]={1,2,3,5,7};
int i;
for (i=0;i<5;i++)
{
printf("%d\n", array[i]);
}
}
Ejemplo3.c
#include <stdio.h>
void main()
{
static int array[]={1,2,3,5,7};
int i;
for (i=0;i<5;i++)
{
printf("%d ", *(array+i));
}
}
Ejemplo4.c
void main()
{
int x,*p,**q;
x=10;
p=&x;
q=&p;
printf("%d",**q);/*imprime el valor de x*/
}
Ejemplo5.c
#include <stdio.h>
intercambio(int a,int b);
void main()
{
int x,y;
x=777;
y=10;
printf("Valor inicial: x=%d y=%d\n",x,y);
y=intercambio(x,y);
printf("Despues del intercambio: x=%d y=%d\n",x,y);
}
intercambio(int a,int b)
{
int aux;
aux=a;
a=b;
b=aux;
printf("Dentro de intercambio: x=%d y=%d\n",a,b);
}
Ejemplo6.c
#include <stdio.h>
void intercambio(int *a,int *b); /*declaración de la función intercambio*/
void main()
{
int x,y;
x=777;
y=10;
printf("Valor inicial: x=%d y=%d\n",x,y);
printf("Dirección de memoria: &x=%p &y=%p\n",&x,&y);
intercambio(&x,&y); /*llamada a intercambio,pasandole los punteros de x e y*/
printf("\nDespués del intercambio: x=%d y=%d\n",x,y);
printf("Dirección de memoria: &x=%p &y=%p\n",&x,&y);
}
void intercambio(int *a,int *b)
{
int aux;
aux=*a;
*a=*b;
*b=aux;
printf("\nDentro de intercambio: x=%d y=%d\n",*a,*b);
printf("Dirección de memoria: &x=%p &y=%p\n",&a,&b);
}
Ejemplo7.c
#include <stdio.h>
void visualiza (int *numero);/*declaración de la función visualiza*/
void main()
{
static int arreglo[10]={1,2,3,4,5,6,7,8,9,0};/*definimos los elementos del arreglo*/
int i;
visualiza(arreglo);
}
void visualiza(int *numero) /*función visualiza*/
{
int i;
for(i=0;i<10;i++)
{
printf("%d ",numero[i]);/*imprime los elementos del arreglo*/
}
}
Prog1.c
/*programa que introduce una cadena de caracteres en un arreglo*/
#include <stdio.h>
void main()
{
char cadena[21];/*se reserva uno más para guardar el carácter /0*/
int num_letras,i;
printf("Este programa guarda una cadena de caracteres en un arreglo\n");
printf("e imprime la cadena completa\n");
printf("Por favor, introduce el número de letras de la cadena(MAX.20)\n");
scanf("%d",&num_letras);
printf("Introduce la cadena letra por letra(separadas por INTRO):\n");
for (i=0;i<num_letras;i++)
{
scanf("%s",&cadena[i]);
}
printf ("La cadena introducida es:\n");
for (i=0;i<num_letras;i++)
{
printf("%c ",cadena[i]); /*i varia desde 0 hasta num_letras*/
}
printf ("\n");
}
Prog2.c
/*Programa que divide dos enteros y devuelve el resto de su división entera*/
void main()
{
int dividendo,divisor,resto; /*declaración de variables*/
printf("Este programa pide dos números enteros\n");
printf("y devuelve el resto de su división entera\n");
printf("Introduce el dividendo\n");
scanf("%d",÷ndo); /*entrada de datos*/
printf("Introduce el divisor\n");
scanf("%d",&divisor); /*entrada de datos*/
resto=dividendo%divisor; /*operación interna*/
printf("El resto es: %d",resto); /*salida de datos*/
}
Prog3.c
/*programa que imprime un "0" o un "1" según la respuesta introducida
por el usuario sea cierta o no*/
void main()
{
int x;
printf(" Este programa escribe un 1 si aciertas la siguiente pregunta, y si no,\n");
printf("escribe un 0. La pregunta es:\n");
printf(" ¿Qué día de agosto es la fiesta de San Eusebio?\n");
scanf("%d",&x);
printf("La respuesta es: %d\n",x==17);/*¡pero no lo mires!*/
/*si el usuario introduce el número 17, la relación será
correcta, lo que equivale a que x==17 tome el valor 1.
si se introduce cualquier otro número no se cumplirá
la relación, por que x==17 tomará el valor 0 */
}
Prog4.c
/*programa que introduce una cadena de caracteres en un arreglo*/
#include <stdio.h>
void main()
{
char cadena[20];
printf("Este programa guarda una cadena de caracteres en un arreglo\n");
printf("e imprime la cadena completa\n");
printf("Introduce la cadena(MAX.20):\n");
gets(cadena);
printf ("La cadena introducida es:\n");
puts(cadena);
printf ("\n");
}
Prog5.c
/*programa que coge 3 números, los suma y muestra la operacion en pantalla*/
#include <stdio.h>
void main()
{
int num1,num2,num3,resultado;
printf("Introduce el primer numero\n");
scanf("%d",&num1);
printf("Introduce el segundo numero\n");
scanf("%d",&num2);
printf("Introduce el tercer número\n");
scanf("%d",&num3);
resultado=num1+num2+num3;
printf("%d + %d + %d = %d \n",num1,num2,num3,resultado);
}/*en este caso printf tiene 4 argumentos*/
Prog6.c
/*programa que coge 2 números y pide al usuario el tipo de operación
que desea hacer con ellos*/
#include <stdio.h>
void main()
{
int num1,num2;
float resultado;
int opcion;
printf("Este programa pide 2 números y permite hacer 5 operaciones con ellos\n");
printf("Introduce el primer numero\n");
scanf("%d",&num1);
printf("Introduce el segundo numero\n");
scanf("%d",&num2);
printf("\n");
printf("+--------------------+\n");
printf("¦ 1.-Sumar ¦\n");
printf("¦ 2.-Restar ¦\n");
printf("¦ 3.-Multiplicar ¦\n");
printf("¦ 4.-Dividir ¦\n");
printf("¦ 5.-Resto entero ¦\n");
printf("+--------------------+\n");
printf("Elige una opción: ");
scanf("%d",&opcion);
switch(opcion)
{
case 1:
resultado=num1+num2;
printf("%d + %d = %.0f \n",num1,num2,resultado);
break;/*la instrucción %.0f sirve para que no saque cifras decimales
ya que resultado está declarado como float*/
case 2:
resultado=num1-num2;
printf("%d - %d = %.0f \n",num1,num2,resultado);
break;
case 3:
resultado=num1*num2;
printf("%d * %d = %.0f \n",num1,num2,resultado);
break;
case 4:
resultado=(float)num1/num2;/*division float*/
printf("%d / %d = %.2f \n",num1,num2,resultado);
break;/*la expresión *%.2f permite visualizar el resultado
con 2 decimales*/
case 5:
resultado=num1%num2;
printf("El resto de %d / %d es %.0f \n",num1,num2,resultado);
break;
default:
printf("La opción introducida no es válida.\n");
break;
}
}
Prog7.c
/*programa que introduce una cadena de caracteres en un arreglo*/
#include <stdio.h>
void main()
{
char cadena[21];/*se reserva uno más para guardar el carácter /0*/
int i,num_letras=0;
printf("Este programa guarda una cadena de caracteres en un arreglo\n");
printf("e imprime la cadena completa\n");
printf("Introduce la cadena letra por letra(separadas por INTRO):\n");
printf ("Para finalizar introduce '0'.\n");
do
{
scanf("%s",&cadena[num_letras]);
num_letras++;
}while(cadena[num_letras-1]!='0');/*el -1 es para que lea el registro anterior*/
printf ("La cadena introducida es:\n");
for (i=0;i<num_letras-1;i++) /*num_letras -1 es para que no lea el 0*/
{
printf("%c ",cadena[i]); /*i varia desde 0 hasta num_letras*/
}
printf ("\n");
}
Prog8.c
/*programa que descompone un número en factores primos*/
#include <stdio.h>
void main()
{
static int vector[7]={2,3,5,7,11,13,17};
static int contador[7]={0,0,0,0,0,0,0};
unsigned int i,aux,numero,resto;
printf("Este programa descompone en factores primos el número que usted introduzca.\n");
printf("\tPor favor, introduzca un número entero positivo: ");
scanf("%u",&numero);
printf("\n");
aux=numero;
printf("\tEl proceso de descomposición es el siguiente:\n\n");
for (i=0;i<7;i++)
{
while (numero%vector[i]==0)
{
resto=numero/vector[i];
printf("\t\t%u | %u\n",numero,vector[i]);
numero=resto;
contador[i]+=1;
}
}
if (numero>17)
{
printf("\t\t%u | %u\n",numero,numero);/*para incluir el caso de que
numero sea primo*/
}
printf("\n");
printf("El número %u descompuesto en factores primos es el producto de:\n\n",aux);
for (i=0;i<7;i++)
{
if (contador[i]!=0)/*para que no escriba si el exponente es 0*/
{
printf(" %u elevado a %u\n",vector[i],contador[i]);
}
}
if (numero>17)
{
printf("\t\t%u elevado a 1\n",numero);/*para incluir numero=primo*/
}
}
Prog9.c
#include <stdio.h>
#include <math.h>
main ()
{
int A[3][3];
int B[3][3];/*factores*/
int i,j,k;
int C[3][3];/*resultado*/
printf ("Este programa multiplica matrices tres por tres.\n");
for (i=0;i<3;i++)
{
for (j=0;j<3;j++)
{
printf("introduce elemento (%d,%d)\n",i,j);
scanf("%d",& A[i][j]);
}
}
for (i=0;i<3;i++)
{
for (j=0;j<3;j++)
{
printf("%d ",A[i][j]);
}
printf("\n");
}
for (i=0;i<3;i++)
{
for (j=0;j<3;j++)
{
printf("introduce elemento de la 2¦ matriz (%d,%d)\n",i,j);
scanf("%d",& B[i][j]);
}
printf ("\n");
}
printf ("\n");
for (i=0;i<3;i++)
{
for (j=0;j<3;j++)
{
printf("%d ",B[i][j]);
}
printf("\n");
}
for(i=0;i<3;i++)
{
for (j=0;j<3;j++)
{
C[i][j]=0;
for (k=0;k<3;k++)
{
C[i][j]=C[i][j]+(A[i][k]*B[k][j]);
}
}
}
printf("el producto de las dos es \n");
for (i=0;i<3;i++)
{
for (j=0;j<3;j++)
{
printf("%d ",C[i][j]);
}
printf("\n");
}
}
Porg10.c
/*programa que suma matrices del mismo tamaño m*n*/
#include <stdio.h>
void main()
{
int **A, **B, **C; /*punteros a matrices operandos y resultado*/
int num_fil, num_col;
int i,j;
printf ("introducir numero de filas de las matrices\n");
scanf ("%d", &num_fil);
printf ("introducir numero de columnas de las matrices\n");
scanf ("%d", &num_col);
/*Asignación dinámica de memoria*/
A=(int**) malloc(num_fil*sizeof(int*));
if (A==NULL)
{
printf ("Error en la reserva de memoria.\n");
exit (1);
} /*salir del programa si hay errores en la asignación*/
for (i=0;i<num_fil;i++)
{
A[i]=(int*) malloc(num_col*sizeof(int));
if (A[i]==NULL)
{
printf ("Error en la reserva de memoria.\n");
exit (1);
}
}
B=(int**) malloc(num_fil*sizeof(int*));
if (B==NULL)
{
printf ("Error en la reserva de memoria.\n");
exit (1);
}
for (i=0;i<num_fil;i++)
{
B[i]=(int*) malloc(num_col*sizeof(int));
if (B[i]==NULL)
{
printf ("Error en la reserva de memoria.\n");
exit (1);
}
}
for (i=0;i<num_fil;i++) /*pide los elementos de la matriz A*/
{
for (j=0;j<num_col;j++)
{
printf("introduce elemento (%d,%d)\n",i,j);
scanf("%d",& A[i][j]);
}
}
printf("\n");
for (i=0;i<num_fil;i++) /*imprime los elementos de la matriz A*/
{
for (j=0;j<num_col;j++)
{
printf("%d ",A[i][j]);
}
printf("\n");
}
for (i=0;i<num_fil;i++) /*pide elementos de matriz B*/
{
for (j=0;j<num_col;j++)
{
printf("introduce elemento de la 2ª matriz (%d,%d)\n",i,j);
scanf("%d",& B[i][j]);
}
}
printf ("\n");
for (i=0;i<num_fil;i++) /*imprime matriz B*/
{
for (j=0;j<num_col;j++)
{
printf("%d ",B[i][j]);
}
printf("\n");
}
printf("\n\n");
for (i=0;i<num_fil;i++) /*suma las matrices*/
{
for (j=0;j<num_col;j++)
{
C[i][j]=A[i][j] + B[i][j];
}
}
printf("El resultado es:\n");
for (i=0;i<num_fil;i++) /*imprime el resultado*/
{
for (j=0;j<num_col;j++)
{
printf("%d ",C[i][j]);
}
printf("\n");
}
}
Prog11.c
#include <stdio.h>
introduce(int numero);
void intercambio(int *a,int *b); /*declaración de la función intercambio*/
void main()
{
int x,y;
x=introduce(x);
y=introduce(y);
printf("Valor inicial: x=%d y=%d\n",x,y);
printf("Dirección de memoria: &x=%p &y=%p\n",&x,&y);
intercambio(&x,&y); /*llamada a intercambio,pasandole los punteros de x e y*/
printf("\nDespués del intercambio: x=%d y=%d\n",x,y);
printf("Dirección de memoria: &x=%p &y=%p\n",&x,&y);
}
introduce(int numero)
{
printf("Introduce un número entero: ");
scanf("%d",&numero);
return(numero);
}
void intercambio(int *a,int *b)
{
int aux;
aux=*a;
*a=*b;
*b=aux;
printf("\nDentro de intercambio: a=%d b=%d\n",*a,*b);
printf("Dirección de memoria (variables locales): &a=%p &b=%p\n",&a,&b);
}
Prog12.c
/*Programa que calcula números primos*/
#include <stdio.h>
void main()
{
unsigned int i=2,j,numero,aux,contador=0;
printf("Este programa calcula todos los números primos hasta el que usted introduzca.\n");
printf("introduzca un número:\n");
scanf("%u",&numero);
for(i=2;i<=numero;i++)
{
for(j=i/2;j>1;j--) /*comprueba si el número es divisible por otro menor o igual que la mitad*/
{ /*para no hacer divisiones innecesarias*/
if(i%j!=0)
{
aux=i;
}
else
{
aux=0;
break; /*si el programa llega a este punto es porque i es divisible por algún número*/
} /*entonces se rompe el bucle y se pasa a la siguiente iteración*/
}
if(aux!=0) /*si no se ha pasado por aux=0, es porque el número es primo, y lo imprimimos*/
{
printf("%u ",i);
contador=contador+1;
}
}
printf("\nHasta el número %u hay %u numeros primos.\n",numero,contador);
}
Prog13.c
/*Programa que calcula números primos y factoriza*/
#include <stdio.h>
void main()
{
int opcion,centinela_goto=0;
int *primos;
int *contador2;
int num_primos;
unsigned int i=2,j,resto,numero,aux=1,contador=0;/*necesitamos que al principio aux sea distinto de cero, para que se cumpla que 2 y 3 son primos*/
printf("Este programa calcula todos los números primos hasta el que usted introduzca.\n");
printf("introduzca un n£mero:\n");
scanf("%u",&numero);
printf("Además, si lo desea, lo descompone en factores primos.\n");
printf("Para ello, primero se almacenan los n£meros primos en un arreglo.\n");
printf("¨Desea guardar en un arreglo los n£meros primos hasta el %u\n",numero);
printf("y hallar su descomposici¢n factorial?\n");
printf(" 1.-Descomponer %u en factores primos.\n",numero);
printf(" 2.-Solamente visualizar los n£meros primos hasta el %u.\n",numero);
printf("\nTeclee '1' o '2': ");
scanf("%d",&opcion);
principio:
for(i=2;i<=numero;i++)
{
for(j=i/2;j>1;j--) /*comprueba si el n£mero es divisible por otro menor o igual que la mitad*/
{ /*para no hacer divisiones innecesarias*/
if(i%j!=0)
{
aux=i;
}
else
{
aux=0;
break; /*si el programa llega a este punto es porque i es divisible por alg£n n£mero*/
} /*entonces se rompe el bucle y se pasa a la siguiente iteraci¢n*/
}
if(aux!=0) /*si no se ha pasado por aux=0, es porque el n£mero es primo, y lo imprimimos*/
{
if (opcion!=1)
{
printf("%u ",i);
}
if (centinela_goto==1)
{
primos[contador]=i;
printf("%u ",primos[contador]);
}
contador=contador+1;
}
}
if (centinela_goto==1)
{
printf("\nLos n£meros han sido guardados en un arreglo.");
goto factores;
}
printf("\nHasta el n£mero %u hay %u numeros primos.\n",numero,contador);
if (opcion==2)
{
exit(2);
}
if (opcion==1 && centinela_goto!=1)
{
primos=(int*)malloc(contador*sizeof(int));
if (primos==NULL)
{
printf("Error en la asignaci¢n de memoria\n");
exit(2);
}
contador=0;
centinela_goto=1;
aux=1;
goto principio;
}
factores:
num_primos=contador;/*vamos a llamar al n£mero de primos num_primos*/
contador2=(int*)malloc((num_primos)*sizeof(int));
if (contador2==NULL)
{
printf("Error en la asignaci¢n de memoria\n");
exit(3);
}
for (i=0;i<num_primos;i++)
{
contador2[i]=0;
}
printf("\n\tA continuaci¢n vamos a descomponer en factores primos el numero que \nusted ha introducido antes.\n");
printf("\n");
aux=numero;
printf("\tEl proceso de descomposici¢n es el siguiente:\n\n");
for (i=0;i<num_primos;i++)
{
while (numero%primos[i]==0)
{
resto=numero/primos[i];
printf("\t\t%u | %u\n",numero,primos[i]);
numero=resto;
contador2[i]+=1;
}
}
if (numero>num_primos)
{
printf("\t\t%u | %u\n",numero,numero);/*para incluir el caso de que
numero sea primo*/
}
printf("\n");
printf("El n£mero %u descompuesto en factores primos es el producto de:\n\n",aux);
for (i=0;i<num_primos;i++)
{
if (contador2[i]!=0)/*para que no escriba si el exponente es 0*/
{
printf(" %u elevado a %u\n",primos[i],contador2[i]);
}
}
if (numero>num_primos)
{
printf("\t\t%u elevado a 1\n",numero);/*para incluir numero=primo*/
}
free(primos);
free(contador2);
}
Prog14.c
#include <stdio.h>
#include <alloc.h>
Calcula_cant_primos(unsigned int,int);/*declaracion de las funciones*/
*Guarda(unsigned int,unsigned int);
void Descompone(unsigned int,unsigned int,unsigned int *pr);
/*------------------------------------------------------------------------*/
void main()
{
unsigned int *primos;
unsigned int cant_primos,num,i;
int opcion;
printf("Este programa calcula todos los n£meros primos hasta el que usted introduzca.\n");
printf("introduzca un n£mero:\n");
scanf("%u",&num);
printf("Adem s, si lo desea, lo descompone en factores primos.\n");
printf("Para ello, primero se almacenan los n£meros primos en un arreglo.\n");
printf("¨Desea guardar en un arreglo los n£meros primos hasta el %u\n",num);
printf("y hallar su descomposici¢n factorial?\n");
printf(" 1.-Descomponer %u en factores primos.\n",num);
printf(" 2.-Solamente visualizar los n£meros primos hasta el %u.\n",num);
printf(" 3.-Visualizar los n£meros primos y descomponer %u.\n",num);
printf("\nTeclee '1', '2' o '3': ");
scanf("%d",&opcion);
switch(opcion)
{
case 1:
cant_primos=Calcula_cant_primos(num,opcion);
printf("%u primos\n",cant_primos);
primos=Guarda(num,cant_primos);
Descompone(num,cant_primos,primos);
exit(1);
case 2:
cant_primos=Calcula_cant_primos(num,opcion);
printf("\nHasta el numero %u hay %u primos.\n",num,cant_primos);
exit(2);
case 3:
cant_primos=Calcula_cant_primos(num,opcion);
printf("%u primos\n",cant_primos);
primos=Guarda(num,cant_primos);
for(i=0;i<cant_primos;i++)
{
printf("%u ",primos[i]);
}
Descompone(num,cant_primos,primos);
exit(3);
default:
printf("Opci¢n incorrecta.\n");
exit(4);
}/*switch*/
}/*main*/
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
Calcula_cant_primos(unsigned int numero,int opcion)
{
unsigned int i=2,j,aux,contador=0;
for(i=2;i<=numero;i++)
{
for(j=i/2;j>1;j--) /*comprueba si el n£mero es divisible por otro menor o igual que la mitad*/
{ /*para no hacer divisiones innecesarias*/
if(i%j!=0)
{
aux=i;
}
else
{
aux=0;
break; /*si el programa llega a este punto es porque i es divisible por alg£n n£mero*/
} /*entonces se rompe el bucle y se pasa a la siguiente iteraci¢n*/
}
if(aux!=0) /*si no se ha pasado por aux=0, es porque el n£mero es primo, y lo imprimimos*/
{
if (opcion==2)
{
printf("%u ",i);
}
contador=contador+1;
}
}
return(contador);/*devuelve la cantidad total de primos*/
}
/*-------------------------------------------------------------------------*/
*Guarda(unsigned int numero,unsigned int cant_primos) /*esta funci¢n devuelve un puntero*/
{
int *p; /*variable local puntero*/
unsigned int i=2,j,aux,contador=0;
p=(int*)malloc(cant_primos*sizeof(int));/*asignaci¢n din mica de memoria*/
if (p==NULL)
{
printf("Error en asignaci¢n de memoria.");
exit(4);
}
for(i=2;i<=numero;i++)
{
for(j=i/2;j>1;j--) /*comprueba si el n£mero es divisible por otro menor o igual que la mitad*/
{ /*para no hacer divisiones innecesarias*/
if(i%j!=0) /*comprueba si el resto es nulo o no*/
{
aux=i;
}
else
{
aux=0;
break; /*si el programa llega a este punto es porque i es divisible por alg£n n£mero*/
} /*entonces se rompe el bucle y se pasa a la siguiente iteraci¢n*/
}
if(aux!=0) /*si no se ha pasado por aux=0, es porque el n£mero es primo, y lo guardamos*/
{
p[contador]=i;
contador++;
}
}
return(p);/*devoluci¢n del puntero a main*/
}
/*-------------------------------------------------------------------------*/
void Descompone(unsigned int numero,unsigned int num_primos,unsigned int *pr)
{
unsigned int resto,i,aux;
unsigned int *contador2;
contador2=(int*)calloc((num_primos),sizeof(int));/*A.D. de memoria para el contador*/
if (contador2==NULL)
{
printf("Error en la asignaci¢n de memoria\n");
exit(5);
}
printf("\nA continuaci¢n vamos a descomponer en factores primos el numero que usted \nha introducido.\n");
printf("\n");
aux=numero;
printf("\tEl proceso de descomposici¢n es el siguiente:\n\n");
for (i=0;i<num_primos;i++)
{
while (numero%pr[i]==0)
{
resto=numero/pr[i];
printf("\t\t%u | %u\n",numero,pr[i]);
numero=resto;
contador2[i]+=1;
}
}
printf("\n");
printf("El n£mero %u descompuesto en factores primos es el producto de:\n\n",aux);
for (i=0;i<num_primos;i++)
{
if (contador2[i]!=0)/*para que no escriba si el exponente es 0*/
{
printf(" %u elevado a %u\n",pr[i],contador2[i]);
}
}
free(pr);
free(contador2);
}
Prog15.c
#include <stdio.h>
#define TAM 3
void Suma(int [][],int [][]);
void Multiplica(int [][],int [][]);
void Traspone(int [][]);
void Visualiza(int [][]);
void main()
{
int opcion;
int A[TAM][TAM];
int B[TAM][TAM];
int i,j;
printf("Este programa realiza las siguientes operaciones con matrices:\n");
printf(" 1.-Suma.\n");
printf(" 2.-Multiplicaci¢n.\n");
printf(" 3.-Trasposici¢n.\n");
printf("Introduce el n£mero de tu opci¢n:");
scanf ("%d",&opcion);
switch (opcion)
{
case 1:
printf ("Introduce matriz A.\n"); /*Pide la matriz*/
for (i=0;i<TAM;i++)
{
for (j=0;j<TAM;j++)
{
printf("Introduce elemento (%d,%d)\n",i,j);
scanf("%d",& A[i][j]);
}
printf ("\n\n");
}
printf ("Introduce matriz B\n");
for (i=0;i<TAM;i++)
{
for (j=0;j<TAM;j++)
{
printf("Introduce elemento (%d,%d)\n",i,j);
scanf("%d",& B[i][j]);
}
}
printf ("La matriz A es:\n");
Visualiza(A);
printf ("La matriz B es:\n");
Visualiza(B);
Suma(A,B);
break;
case 2:
printf ("Introduce matriz A.\n");
for (i=0;i<TAM;i++)
{
for (j=0;j<TAM;j++)
{
printf("Introduce elemento (%d,%d)\n",i,j);
scanf("%d",& A[i][j]);
}
printf ("\n\n");
}
printf ("Introduce matriz B\n");
for (i=0;i<TAM;i++)
{
for (j=0;j<TAM;j++)
{
printf("Introduce elemento (%d,%d)\n",i,j);
scanf("%d",& B[i][j]);
}
}
printf ("La matriz A es:\n");
Visualiza(A);
printf ("La matriz B es:\n");
Visualiza(B);
Multiplica(A,B);
break;
case 3:
printf ("Introduce matriz A.\n");
for (i=0;i<TAM;i++)
{
for (j=0;j<TAM;j++)
{
printf("Introduce elemento (%d,%d)\n",i,j);
scanf("%d",& A[i][j]);
}
printf ("\n\n");
}
printf ("La matriz A es:\n");
Visualiza(A);
printf ("La traspuesta es:\n");
Traspone(A);
break;
default:
printf("Opci¢n incorrecta.\n");
exit(4);
}
}
void Suma(int A[TAM][TAM],int B[TAM][TAM])
{
int i,j;
int C[TAM][TAM];
for (i=0;i<TAM;i++) /*suma las matrices*/
{
for (j=0;j<TAM;j++)
{
C[i][j]=A[i][j] + B[i][j];
}
}
printf("\nEl resultado es:\n");
for (i=0;i<TAM;i++) /*imprime el resultado*/
{
for (j=0;j<TAM;j++)
{
printf("%d ",C[i][j]);
}
printf("\n");
}
}
void Multiplica (int A[TAM][TAM],int B[TAM][TAM])
{
int i,j,k;
int C[TAM][TAM];
for(i=0;i<TAM;i++)
{
for (j=0;j<TAM;j++)
{
C[i][j]=0;
for (k=0;k<TAM;k++)
{
C[i][j]=C[i][j]+(A[i][k]*B[k][j]);
}
}
}
printf("\nEl producto de las dos es \n");
for (i=0;i<3;i++)
{
for (j=0;j<3;j++)
{
printf("%d ",C[i][j]);
}
printf("\n");
}
}
void Traspone(int A[TAM][TAM])
{
int i,j;
int traspuesta[TAM][TAM];
for (i=0;i<TAM;i++)
{
for (j=0;j<TAM;j++)
{
traspuesta [i][j]=A[j][i];
printf("%d ",traspuesta [i][j]);
}
printf("\n");
}
}
void Visualiza(int matriz[TAM][TAM])
{
int i,j;
for (i=0;i<TAM;i++)
{
for (j=0;j<TAM;j++)
{
printf ("%d ", matriz [i][j]);
}
printf ("\n");
}
}
Prog16.c
/*PROGRAMA QUE OBTIENE LA CLASIFICACIÓN DE UNA PRUEBA DE NATACIÓN*/
#include <stdio.h>
/*Declaración de funciones a usar*/
float Convierte();
void Seleccion (float *,int);
void Desconv (float);
/*Variables globales*/
int min,seg,cent;
void main()
{
int i,nadadores;
float *tiempo;
/*Cabecera*/
printf ("Este programa obtiene la clasificación de una prueba de natación de 50,100 y 200metros.\n");
printf ("Introduzca el número de participantes en la prueba:");
scanf ("%d",&nadadores);
/*Reserva de memoria*/
tiempo=(float*)malloc(nadadores*sizeof(float));
printf ("\n\nIntroduce los tiempos(min seg cent):\n");/*Lee los tiempos*/
for (i=0;i<nadadores;i++)
{
printf ("Tiempo %d:",i+1);
scanf ("%d" "%d" "%d",&min,&seg,¢);
tiempo[i]=Convierte();/*Almacena los tiempos en forma de centésimas*/
}
Seleccion(tiempo,nadadores);/*Ordena los tiempos de menor a mayor*/
printf ("La clasificación es:\n");
for (i=0;i<nadadores;i++) /*Saca la clasificación*/
{
Desconv(tiempo[i]);
printf ("%dº.- %d'%02d''%02d\n",i+1,min,seg,cent);
}
}
float Convierte() /*Convierte los tiempos de min,seg,cent a cent*/
{
float cent1;
float cent2,total;
cent1=min*6000; /*minutos ---> cent*/
cent2=seg*100; /*segundos---> cent*/
total=cent1+cent2+cent;
return (total);
}
void Seleccion(float *punt,int K) /*Ordena arreglos de "floats"*/
{
int i,j,pos_min=0;
float aux;
float minimo;
for (i=0;i<K;i++) /*coge la posición inicial 1,2,...,K*/
{
minimo=punt[i];
pos_min=i;
for(j=i;j<K;j++)/*recorre el arreglo*/
{
if(minimo>punt[j])
{
minimo=punt[j];/*compara el 1 elemento con los demás y asigna a mínimo el menor*/
pos_min=j;
}
}
/*intercambio*/
aux=punt[i];
punt[i]=minimo;
punt[pos_min]=aux;
}
}
void Desconv(float marca) /*Desconvierte los tiempos de cent a min,seg,cent*/
{
int aux;
min=(int)(marca/6000); /*División entera de la marca para obtener los minutos*/
aux=(int) marca%6000; /*Resto de la división anterior*/
seg=aux/100; /*División entera del resto para obtener los segundos*/
cent=(int)aux%100; /*Resto de antes para obtener las centésimas*/
}
Prog17.c
/*PROGRAMA QUE ORDENA UNA LISTA DE PALABRAS DE LA A-Z O DE LA Z-A*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*Funciones a usar*/
void A_Z (char *[],int);
void Z_A (char *[],int);
void main()
{
int i,num_elem=0;
char *lista[20];
int opcion;
/*cabecera*/
printf ("\n\nEste programa ordena alfab ticamente una lista de palabras.\n\n");
printf ("Cuando hallas terminado tu lista de palabras escribe FIN.\n");
/*Lee la lista de palabras hasta que se introduce la palabra FIN(en may£sculas)*/
printf ("Por favor introduce las palabras (MAX.25 caracteres):\n");
do
{
lista[num_elem]=(char*)malloc(27*sizeof(char));
printf ("Palabra %d: ",num_elem+1);
scanf("%s",lista[num_elem]);
}while (strcmp(lista[num_elem++], "FIN"));
/*Menú para elegir ordenaci¢n de la A-Z o de Z-A*/
printf ("\n\nElige el orden de ordenaci¢n:\n");
printf (" 1.-De la A-Z.\n");
printf (" 2.-De la Z-A.\n");
printf ("Escribe el n£mero de tu opci¢n:");
scanf ("%d",&opcion);
switch(opcion)
{
case 1:
A_Z (lista,num_elem-1);
break;
case 2:
Z_A (lista,num_elem-1);
break;
default:
printf ("\nEsa opcion no es correcta.");
exit (1);
}
printf ("\n");
printf ("La lista ordenada es:\n"); /*Saca la lista ordenada*/
for (i=0;i<num_elem-1;i++)
{
printf ("La palabra %d es: %s.\n",i+1,lista[i]);
}
}
void A_Z (char *lista[],int tamano) /*Ordena de A-Z*/
{
char *aux;
int i,j;
for (j=0;j<tamano-1;j++)
{
for (i=j;i<tamano;i++)
{
if (strcmpi(lista[j],lista[i]) > 0) /*Ordena alfabeticamente usando la funcion del C STRCMPI*/
{ /*La condici¢n > implica ordenacion normal*/
aux=lista[j];
lista[j]=lista[i];
lista[i]=aux;
}
}
}
}
void Z_A (char *lista[],int tamano) /*Ordena de Z-A*/
{
char *aux;
int i,j;
for (j=0;j<tamano-1;j++)
{
for (i=j;i<tamano;i++)
{
if (strcmpi(lista[j],lista[i]) < 0) /*Ordena alfabeticamente usando la funcion del C STRCMPI*/
{ /*La condicon < implica ordenaci¢n inversa*/
aux=lista[j];
lista[j]=lista[i];
lista[i]=aux;
}
}
}
}
Prog18.c
/*PROGRAMA QUE ORDENA UNA LISTA DE PALABRAS COGIDAS DEL ARCHIVO PALABRAS.TXT LAS GUARDA EN PALABORD.TXT*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*Funciones a usar*/
void A_Z (char *[],int);
void Z_A (char *[],int);
void main()
{
FILE *desorden; /*punteros de ficheros*/
FILE *orden;
int i,num_elem=0;
char destino[50];
char **lista;
int opcion;
/*cabecera*/
printf ("\n\nEste programa ordena alfab ticamente una lista de palabras procedente del\narchivo PALABRAS.TXT, y la guarda en otro archivo dado por el usuario.\n");
printf ("Por favor introduce el archivo en el que deseas guardar la lista de palabras\nordenada:\n");
gets(destino);
desorden=fopen("palabras.txt","r"); /*apunta el puntero a un archivo en modo lectura*/
if(desorden==NULL)
{
printf ("Error en asignaci¢n de archivos.\n");
exit (1);
}
orden=fopen(destino,"w"); /*apunta el puntero a un archivo en mod escritura*/
if(orden==NULL)
{
printf ("Error en asignaci¢n de archivos.\n");
exit (1);
}
/*Lee la lista de palabras hasta que se encientra el fin del archivo*/
do
{
lista[num_elem]=(char*)malloc(27*sizeof(char)); /*reserva memoria seg£n aumentan los elementos*/
fscanf(desorden,"%s",lista[num_elem]);
num_elem++;
}while (!feof(desorden));
fclose(desorden); /*cierra el fichero al que apunta desorden*/
printf ("\n\nEl programa ya ha leido la lista de palabras del fichero PALABRAS.TXT.\n");
/*Men£ para elegir ordenaci¢n de la A-Z o de Z-A*/
printf ("Elige el orden de ordenaci¢n:\n");
printf (" 1.-De la A-Z.\n");
printf (" 2.-De la Z-A.\n");
printf ("Escribe el n£mero de tu opci¢n:");
scanf ("%d",&opcion);
switch(opcion)
{
case 1:
A_Z (lista,num_elem-1);
break;
case 2:
Z_A (lista,num_elem-1);
break;
default:
printf ("\nEsa opcion no es correcta.");
exit (1);
}
for (i=0;i<num_elem-1;i++) /*Escribe la lista de palabras ordenada en otro fichero*/
{
fprintf (orden,"%s\n",lista[i]);
}
fclose(orden);
printf ("La lista ordenada ha sido guardada en PALABORD.TXT.\n");
}
void A_Z (char *lista[],int tamano) /*Ordena de A-Z*/
{
char *aux;
int i,j;
for (j=0;j<tamano-1;j++)
{
for (i=j;i<tamano;i++)
{
if (strcmpi(lista[j],lista[i]) > 0) /*Ordena alfabeticamente usando la funcion del C STRCMPI*/
{ /*La condici¢n > implica ordenacion normal*/
aux=lista[j];
lista[j]=lista[i];
lista[i]=aux;
}
}
}
}
void Z_A (char *lista[],int tamano) /*Ordena de Z-A*/
{
char *aux;
int i,j;
for (j=0;j<tamano-1;j++)
{
for (i=j;i<tamano;i++)
{
if (strcmpi(lista[j],lista[i]) < 0) /*Ordena alfabeticamente usando la funcion del C STRCMPI*/
{ /*La condicon < implica ordenaci¢n inversa*/
aux=lista[j];
lista[j]=lista[i];
lista[i]=aux;
}
}
}
}
Prog19.c
#include <stdio.h>
void main()
{
FILE *destino;
int indice=0;
int anio;
destino=fopen("fechas.txt","w"); /*Asigna al puntero el lugar donde está FECHAS.TXT*/
/*Condición de seguridad*/
if (destino==NULL)
{
printf ("Error en asignación de puntero");
exit (1);
}
/*Cabecera*/
printf ("Este programa guarda los datos de años de nacimiento de una población en un\narchivo llamado FECHAS.TXT.\n");
printf ("Introduce los años de nacimiento de los individuos(para finalizar introduce -1):\n");
do
{
printf ("Individuo %d:",indice+1);
scanf ("%d",&anio);
if (anio!=-1) /*Escribe los datos en el fichero si son distintos de -1*/
{
fprintf (destino,"%d\n",anio);
indice++;
}
else /*Cuando el dato sea -1 finaliza el programa*/
{
printf ("Fin de introducción de datos.\n");
printf ("El número de habitantes es: %d individuos.\n",indice);
exit (0);
}
}while (1);
fclose(destino);
}
Prog20.c
/*PROGRAMA QUE DICE QUE TIPO DE PAIS ES Y PIRÁMIDE DE POBLACIÓN CORRESPONDIENTE
A PARTIR DE LAS FECHAS DE NACIMIENTO DE LOS POBLADORES*/
#include <stdio.h>
/*funciones a utilizar*/
void Edades (int*,int*,int,int); /*calcula la edad de los individuos*/
int Secuencial(int*,int,int); /*busca el número de individuos con una determinada edad*/
int Juventud (int*,int); /*calcula los individuos de 0 a 15 años*/
int Adulto (int*,int); /*calcula los individuos de 15 a 65 años*/
int Vejez (int*,int); /*calcula los individuos de 65 a 130 años*/
void main()
{
FILE *origen;
int actual,i;
int individuos=0;
int *anio;
int *edad;
int juventud,adulto,vejez;
float porc_juv,porc_adul,porc_vej;
/*cabecera*/
printf ("Este programa calcula el número de habitantes de la población así como el\nporcentaje de habitantes de cada estado social.\n");
printf ("El programa coge los datos de un archivo de texto llamado FECHAS.TXT.\n");
printf ("Por favor introduce el año actual:");
scanf ("%d",&actual);
origen=fopen("fechas.txt","r");
if (origen==NULL)
{
printf("Error en el archivo\n");
exit(1);
}
do /*lee los años de nacimiento de los individuos del archivo fechas.txt*/
{
anio[individuos]=(int*)malloc(sizeof(int)); /*asignación dinámica de memoria*/
fscanf (origen,"%d",&anio[individuos]);
individuos++;
}while(!feof(origen));
fclose(origen);
individuos--; /*resta un individuo para no leer el carecter EOF*/
edad=(int*)malloc(individuos*sizeof(int)); /*asignación dinámica de memoria*/
Edades(anio,edad,individuos,actual);
juventud=Juventud(edad,individuos);
adulto=Adulto(edad,individuos);
vejez=Vejez(edad,individuos);
porc_juv=(float)(juventud*100)/individuos; /* % de jóvenes*/
porc_adul=(float)(adulto*100)/individuos; /* % de adultos*/
porc_vej=(float)(vejez*100)/individuos; /* % de ancianis*/
printf ("\n\n");
printf ("El número de habitantes en el país es de %d.",individuos);
printf ("\n\n");
printf ("Porcentaje de menores de 15 años(jóvenes): %.2f%\n",porc_juv);
printf ("Porcentaje de individuos entre 15-65 años(adultos): %.2f%\n",porc_adul);
printf ("Porcentaje de individuos de más de 65 años(ancianos): %.2f%\n",porc_vej);
printf ("\n\n");
/*halla el tipo de país de que se trata (joven,intermedio o viejo*/
if (porc_juv>=40)
{
printf ("El porcentaje de individuos jóvenes es mayor del 40%, por tanto es un PAÍS JÓVEN\n.");
printf ("Su pirámide de población se corresponde con una base ancha y una cúspide estrecha.\n");
}
else if (porc_juv<40&&porc_vej<12)
{
printf ("El porcentaje de jóvenes es menor del 40% y el de ancianos menor del 12%.\nPor tanto es un PAÍS INTERMEDIO.\n");
printf ("Su pirámide se asemeja más a un rectángulo en la base y en el centro.\n");
}
else if (porc_vej>12)
{
printf ("El porcentaje de ancianos es mayor del 12%,por tanto es un PAÍS VIEJO.\n");
printf ("Su pirámide de población es invertida; más ancha en la cúspide que en la base.\n");
}
printf ("\n\n");
}
/*--------------------------------------------------------------------------*/
int Vejez (int *edad,int tam)
{
int i;
int veces=0;
int vejez=0;
for(i=66;i<=500;i++)
{
veces=Secuencial(edad,tam,i); /*busca individuos con la edad i*/
vejez+=veces;
}
return (vejez);
}
/*--------------------------------------------------------------------------*/
int Adulto (int *edad,int tam)
{
int i;
int veces=0;
int adulto=0;
for(i=15;i<=65;i++)
{
veces=Secuencial(edad,tam,i); /*busca individuos con la edad i*/
adulto+=veces;
}
return (adulto);
}
/*--------------------------------------------------------------------------*/
int Juventud (int *edad,int tam)
{
int i;
int veces=0;
int juventud=0;
for(i=0;i<15;i++)
{
veces=Secuencial(edad,tam,i); /*busca individuos con la edad i*/
juventud+=veces;
}
return (juventud);
}
/*--------------------------------------------------------------------------*/
int Secuencial(int *arreglo,int tam,int buscado)
{
int num_veces=0;
int i;
for (i=0;i<tam;i++)
{
if (arreglo[i]==buscado)
{
num_veces++;
}
}
return (num_veces);
}
/*--------------------------------------------------------------------------*/
void Edades(int *lista,int *edad,int tam,int actual)
{
int i;
for (i=0;i<tam;i++)
{
edad[i]=actual-lista[i];
}
}
Prog21.c
/*PROGRAMA QUE GUARDA ESTRUCTURAS EN ARCHIVOS DE TEXT0*/
#include <stdio.h>
/*Definición de la estructura*/
struct biblioteca
{
char titulo[50];
char autor[50];
char editorial[20];
unsigned int precio;
};
void main()
{
int i,num_libros;
FILE *destino;
char archivo[15];
struct biblioteca libro; /*declaración de la variable del tipo de la estructura biblioteca*/
printf ("Programa que escribe el registro de libros recibidos en una libreria en un archivo especificado por el usuario.\n");
printf ("Por favor introduce el nombre del archivo donde se guardarán los datos:\n");
gets(archivo);
printf ("Por favor introduce el número de libros recibidos:");
scanf ("%d",&num_libros);
destino=fopen(archivo,"w");
if (destino==NULL)
{
printf("Error en la asignación de puntero.\n");
exit(1);
}
for (i=0;i<num_libros;i++) /*coge los datos y los archiva en un archivo*/
{
gets(libro.autor);
printf ("Titulo(MAX.50):");
gets(libro.titulo); /*coge la cadena y la manda a la dirección de titulo dentro de la estructura libro*/
printf("Autor(MAX.50):");
gets(libro.autor);
printf("Editorial(MAX.20):");
gets(libro.editorial);
printf ("Precio:");
scanf ("%u",&libro.precio);
printf ("\n");
fprintf (destino,"Titulo: %s\n",libro.titulo); /*Escribe el contenido de la variable titulo que esta dentro de una estructura*/
fprintf (destino,"Autor: %s\n",libro.autor);
fprintf (destino,"Editorial: %s\n",libro.editorial);
fprintf (destino,"Precio: %u\n\n",libro.precio);
}
fclose(destino);
}
Prog22.c
/*PROGRAMA QUE RECOGE GUARDA EN UN ARCHIVO BINARIO LAS DATOS DE UNOS LIBROS*/
#include <stdio.h>
#include <conio.h>
/*declaración de estructuras*/
struct fecha
{
int dia;
int mes;
int anio;
};
struct prestado
{
char nombre[20];
char apellido1 [20];
char apellido2 [20];
struct fecha fecha; /*estructura dentro de estructura*/
};
struct biblioteca
{
char titulo[50];
char autor[50];
char editorial[20];
struct prestado prestado; /*estructura dentro de estructura*/
};
/*declaración de funciones*/
struct biblioteca Introducir(struct biblioteca);
struct biblioteca Inicializar(struct biblioteca);
void main()
{
FILE *destino;
struct biblioteca libro;
clrscr(); /*limpia la pantalla*/
/*cabecera*/
printf ("Este programa guarda los datos de los libros de una biblioteca en un archivo\nbinario llamado BIBLIOTE.BIN.\n");
printf ("Las indicaciones entre corchetes corresponden al número máximo de caracteres\naceptables (incluidos espacios en blanco).\n\n");
printf ("Introduce los datos de los libros (para terminar escribe FIN en el titulo):\n");
printf ("\n");
destino=fopen("bibliote.bin","w");
if(destino==NULL)
{
printf("Error en archivo.\n");
exit(1);
}
while (1) /*bucle infinito*/
{
printf ("Titulo(Máx[50]):");
gets(libro.titulo);
if (strcmp(libro.titulo,"FIN")==0) /*condición de ruptura del bucle*/
{
printf ("Proceso de introducción de datos finalizado.\n");
break;
}
libro=Introducir(libro); /*función para coger los datos*/
clrscr();
fwrite (&libro,sizeof(struct biblioteca),1,destino); /*escribe datos en un archivo en forma binaria*/
printf ("\n");
gets (libro.titulo);
}
fclose (destino);
}
/*---------------------------------------------------------------------------*/
struct biblioteca Introducir (struct biblioteca libro)
{
char prestado;
printf ("Autor(Máx[50]):");
gets(libro.autor);
printf ("Editorial(Máx[20]):");
gets(libro.editorial);
printf ("¿Este libro esta prestado?[s/n]:");
scanf ("%c",&prestado);
if (prestado=='s')
{
printf ("\n\nIntroduce los datos de la persona y la fecha;\n");
gets(libro.prestado.nombre);
printf ("Nombre(Máx[20]:");
gets(libro.prestado.nombre);
printf ("1ºApellido(Máx[20]):");
gets(libro.prestado.apellido1);
printf ("2ºApellido(Máx[20]):");
gets(libro.prestado.apellido2);
printf ("Fecha(dd mm aa):");
scanf ("%d %d %d",&libro.prestado.fecha.dia,&libro.prestado.fecha.mes,&libro.prestado.fecha.anio);
printf ("\n\n");
}
else
{
libro=Inicializar(libro); /*función para dar valores por defecto a los préstamos*/
}
return(libro);
}
/*---------------------------------------------------------------------------*/
struct biblioteca Inicializar(struct biblioteca libro)
{
int i;
libro.prestado.fecha.dia = 0;
libro.prestado.fecha.mes = 0;
libro.prestado.fecha.anio = 0;
for (i=0;i<20;i++) /*bucle que llena el nombre del préstamo con espacios en blanco*/
{
libro.prestado.nombre[i]=' ';
}
for (i=0;i<20;i++)
{
libro.prestado.apellido1[i]=' ';
}
for (i=0;i<20;i++)
{
libro.prestado.apellido2[i]=' ';
}
return(libro);
}
Prog23.c
/*PROGRAMA PARA VER LOS DATOS DE LOS LIBROS GUARDADOS EN BIBLIOTE.BIN*/
#include <stdio.h>
#include <conio.h>
/*declaración de estructuras*/
struct fecha
{
int dia;
int mes;
int anio;
};
struct prestado
{
char nombre [20];
char apellido1 [20];
char apellido2 [20];
struct fecha fecha;
};
struct biblioteca
{
char titulo[50];
char autor[50];
char editorial[20];
struct prestado prestado;
};
/*declaración de funciones*/
void Ver(struct biblioteca,int);
void main()
{
FILE *origen;
char continuar;
int registro;
struct biblioteca libro;
clrscr(); /*limpia la pantalla*/
/*cabecera*/
printf ("Este programa muestra uno por uno cada uno de los libros contenidos en el\narchivo BIBLIOTE.BIN.\n");
printf ("Introduzca el registro desde el que desea comenzar: ");
scanf ("%d",®istro);
origen=fopen("bibliote.bin","r");
if(origen==NULL)
{
printf("Error en el archivo.\n");
exit(1);
}
while (!feof(origen))
{
fseek (origen,(registro-1)*sizeof(struct biblioteca),SEEK_SET);/*busca la posición del registro a ver*/
fread (&libro,sizeof(struct biblioteca),1,origen);
Ver(libro,registro); /*función que muestra los datos*/
registro++;
printf ("\n=============================================================================\n");
printf ("¿Desea continuar?[s/n]:");
scanf ("\n%c",&continuar);
if (continuar=='n') /*condición para finalizar*/
{
printf ("Proceso terminado por el usuario.\n");
exit (0);
}
clrscr();
}
printf ("\nTodos los registros han sido visualizados.\n");
fclose(origen);
}
/*-----------------------------------------------------------------------------------------*/
void Ver(struct biblioteca libro,int registro)
{
printf ("\n\nREGISTRO: %d\n\n",registro);
printf ("Título: %s\n",libro.titulo);
printf ("Autor: %s\n",libro.autor);
printf ("Editorial: %s\n",libro.editorial);
printf ("-----------------------------------------------------------------------------\n");
printf ("El libro ha sido prestado a:\n");
printf ("Nombre: %s\n",libro.prestado.nombre);
printf ("1ºApellido: %s\t2ºApellido: %s\n",libro.prestado.apellido1,libro.prestado.apellido2);
printf ("\n");
printf ("Fecha de préstamo: %d/%d/%d\n",libro.prestado.fecha.dia,libro.prestado.fecha.mes,libro.prestado.fecha.anio);
}
Prog24.c
/*PROGRAMA QUE AÑADE DATOS A UN ARCHIVO BINARIO (BIBILIOTE.BIN) LAS DATOS DE UNOS LIBROS*/
#include <stdio.h>
#include <conio.h>
/*declaración de estructuras*/
struct fecha
{
int dia;
int mes;
int anio;
};
struct prestado
{
char nombre[20];
char apellido1 [20];
char apellido2 [20];
struct fecha fecha; /*estructura dentro de estructura*/
};
struct biblioteca
{
char titulo[50];
char autor[50];
char editorial[20];
struct prestado prestado; /*estructura dentro de estructura*/
};
/*declaración de funciones*/
struct biblioteca Introducir(struct biblioteca);
struct biblioteca Inicializar(struct biblioteca);
void main()
{
FILE *destino;
struct biblioteca libro;
clrscr(); /*limpia la pantalla*/
/*cabecera*/
printf ("Este programa añade datos libros de una biblioteca al archivo\nbinario BIBLIOTE.BIN.\n");
printf ("Las indicaciones entre corchetes corresponden al número máximo de caracteres\naceptables (incluidos espacios en blanco).\n\n");
printf ("Introduce los datos de los libros (para terminar escribe FIN en el titulo):\n");
printf ("\n");
destino=fopen("bibliote.bin","a");
if(destino==NULL)
{
printf("Error en archivo.\n");
exit(1);
}
while (1) /*bucle infinito*/
{
printf ("Titulo(Máx[50]):");
gets(libro.titulo);
if (strcmp(libro.titulo,"FIN")==0) /*condición de ruptura del bucle*/
{
printf ("Proceso de introducción de datos finalizado.\n");
break;
}
libro=Introducir(libro); /*función para coger los datos*/
clrscr();
fwrite (&libro,sizeof(struct biblioteca),1,destino); /*escribe datos en un archivo en forma binaria*/
printf ("\n");
gets (libro.titulo);
}
fclose (destino);
}
/*---------------------------------------------------------------------------*/
struct biblioteca Introducir (struct biblioteca libro)
{
char prestado;
printf ("Autor(Máx[50]):");
gets(libro.autor);
printf ("Editorial(Máx[20]):");
gets(libro.editorial);
printf ("¿Este libro esta prestado?[s/n]:");
scanf ("%c",&prestado);
if (prestado=='s')
{
printf ("\n\nIntroduce los datos de la persona y la fecha;\n");
gets(libro.prestado.nombre);
printf ("Nombre(Máx[20]:");
gets(libro.prestado.nombre);
printf ("1ºApellido(Máx[20]):");
gets(libro.prestado.apellido1);
printf ("2ºApellido(Máx[20]):");
gets(libro.prestado.apellido2);
printf ("Fecha(dd mm aa):");
scanf ("%d %d %d",&libro.prestado.fecha.dia,&libro.prestado.fecha.mes,&libro.prestado.fecha.anio);
printf ("\n\n");
}
else
{
libro=Inicializar(libro); /*función para dar valores por defecto a los préstamos*/
}
return(libro);
}
/*---------------------------------------------------------------------------*/
struct biblioteca Inicializar(struct biblioteca libro)
{
int i;
libro.prestado.fecha.dia = 0;
libro.prestado.fecha.mes = 0;
libro.prestado.fecha.anio = 0;
for (i=0;i<20;i++) /*bucle que llena el nombre del préstamo con espacios en blanco*/
{
libro.prestado.nombre[i]=' ';
}
for (i=0;i<20;i++)
{
libro.prestado.apellido1[i]=' ';
}
for (i=0;i<20;i++)
{
libro.prestado.apellido2[i]=' ';
}
return(libro);
}
Prog25.c
/*PROGRAMA PARA CAMBIAR LOS DATOS DEL ARCHIVO BIBLIOTE.BIN*/
#include <stdio.h>
#include <conio.h>
/*declaración de estructuras*/
struct fecha
{
int dia;
int mes;
int anio;
};
struct prestado
{
char nombre[20];
char apellido1 [20];
char apellido2 [20];
struct fecha fecha;
};
struct biblioteca
{
char titulo[50];
char autor[50];
char editorial[20];
struct prestado prestado;
};
/*declaración de funciones*/
struct biblioteca Introducir(struct biblioteca);
struct biblioteca Inicializar(struct biblioteca);
void main()
{
FILE *destino;
struct biblioteca libro;
int registro;
clrscr();/*limpia la pantalla*/
/*cabecera*/
printf ("Este programa permite cambiar los datos de un libros del archivo binario llamadoBIBLIOTE.BIN.\n");
printf ("Las indicaciones entre corchetes corresponden al número máximo de caracteres\naceptables (incluidos espacios en blanco.\n\n");
printf ("Introduce los datos de los libros (para terminar escribe FIN en el titulo):\n");
printf ("\n");
destino=fopen("bibliote.bin","r+");
if (destino==NULL)
{
printf ("Error en archivo.\n");
exit(1);
}
printf ("Introduce el número de registro del libro a modificar:");
scanf ("%d",®istro);
fseek(destino,(registro-1)*sizeof(struct biblioteca),SEEK_SET);/*busca la posición del registro a modificar*/
fread (&libro,sizeof(struct biblioteca),1,destino);/*lee los datos de ese registro*/
printf ("Introduce los nuevos datos:\n");
printf ("Titulo(Máx[50]):");
gets(libro.titulo);
gets(libro.titulo);
libro=Introducir(libro); /*función para introducir los datos*/
fseek(destino,(registro-1)*sizeof(struct biblioteca),SEEK_SET); /*vuelve a situar el puntero en el lugar del registro a sustituir*/
fwrite (&libro,sizeof(struct biblioteca),1,destino); /*escribe los nuevos datos*/
printf ("\n");
printf ("Los datos han sido modificados.\n");
fclose (destino);
}
/*--------------------------------------------------------------------------*/
struct biblioteca Introducir (struct biblioteca libro)
{
char prestado;
printf ("Autor(Máx[50]):");
gets(libro.autor);
printf ("Editorial(Máx[20]):");
gets(libro.editorial);
printf ("¿Este libro esta prestado?[s/n]:");
scanf ("%c",&prestado);
if (prestado=='s')
{
printf ("\n\nIntroduce los datos de la persona y la fecha;\n");
gets(libro.prestado.nombre);
printf ("Nombre(Máx[20]:");
gets(libro.prestado.nombre);
printf ("1ºApellido(Máx[20]):");
gets(libro.prestado.apellido1);
printf ("2ºApellido(Máx[20]):");
gets(libro.prestado.apellido2);
printf ("Fecha(dd mm aa):");
scanf ("%d %d %d",&libro.prestado.fecha.dia,&libro.prestado.fecha.mes,&libro.prestado.fecha.anio);
printf ("\n\n");
}
else
{
libro=Inicializar(libro); /*función que asigna unos datos iniciales a prestado*/
}
return(libro);
}
/*--------------------------------------------------------------------------*/
struct biblioteca Inicializar(struct biblioteca libro)
{
int i;
libro.prestado.fecha.dia = 0;
libro.prestado.fecha.mes = 0;
libro.prestado.fecha.anio = 0;
for (i=0;i<10;i++)
{
libro.prestado.nombre[i]=' ';
}
for (i=0;i<10;i++) /*bucle para rellenar el espacio de apellido1 con espacios en blanco*/
{
libro.prestado.apellido1[i]=' ';
}
for (i=0;i<10;i++)
{
libro.prestado.apellido2[i]=' ';
}
return(libro);
}
Binaria.c
#include <stdio.h>
#include <stdlib.h>
#include <buscar.h>
void main()
{
int i,tam,buscado;
int *lista;
int *posicion;
printf ("Este programa busca dentro de un arreglo ordenado las posiciones en las que\naparece un elemento.\n");
printf ("Introduce el número de elementos:\n");
scanf ("%d", &tam);
lista=(int*)malloc(tam*sizeof(int));
if (lista==NULL)
{
printf ("Error en asignaci¢n de memoria a lista");
exit (1);
}
posicion=(int*)calloc(tam,sizeof(int)); /*Asigna memoria a posición y lo inicializa a cero*/
if (posicion ==NULL)
{
printf ("Error en asignación de memoria a posicion");
exit (1);
}
printf ("Introduce los elementos ordenados:\n");
for (i=0;i<tam;i++)
{
scanf ("%d", &lista[i]);
}
printf ("Introduce el elemento buscado:\n");
scanf ("%d",&buscado);
BusqBinaria(lista,tam,buscado,posicion); /*Llamada a la función*/
printf ("Las posiciones son:\n");
for (i=0;i<tam;i++)
{
while (posicion[i]!=0&&i<tam) /*Cuando encuentra el valor cero es que no hay más información útil*/
{
if (posicion[i]==-1) /*si hay un -1 es porque el elemento se encuentra en la posico 0 del arreglo*/
{
printf ("%d\n",0);
i++;
}
else /*si no es -1 imprime su valor*/
{
printf ("%d\n",posicion[i]);
i++;
}
}
}
}
Velocida.c
#include <stdio.h>
#include <stdlib.h>
#include <ordena.h>
#include <time.h>
void Ordenado();
void Aleatorio();
void Inverso(); /*declaración de las funciones*/
void Metodo(int*,int); /*funcion para elegir las opciones*/
float principio,fin; /*declaración de variables globales para medir el tiempo*/
void main()
{
char opcion;
printf ("Este programa ordena arreglos que se encuentran en diferentes estados de\nordenación mediante diferentes métodos y muestra además el tiempo empleado.\n");
printf ("Si el número de elementos a ordenar es mayor de 300, estos no se\nvisualizaran. Sólamente se saldrá en pantalla el tiempo empleado en ordenarlo.\n");
printf ("Elige el estado de orden del arreglo.\n");
printf (" 1.- Ordenado.\n");
printf (" 2.- Aleatorio.\n");
printf (" 3.- Generado inversamente\n");
printf (" 4.- Abandonar.\n\n");
printf ("Escribe el número de tu elección:");
scanf ("%c", &opcion);
switch (opcion)
{
case '1':
Ordenado();
break;
case '2':
Aleatorio();
break;
case '3':
Inverso();
break;
case '4':
printf ("Programa abortado por el usuario.\n");
break;
default:
printf ("Opción incorrecta\n");
}
}
void Ordenado()
{
int *ordenado;
int i;
int tam;
printf("Introduce el número (máximo 10000) de elementos a generar:");
scanf ("%d",&tam);
ordenado=(int*)malloc(tam*sizeof(int));
if (aleat==NULL) /*comprobación de asignación de punteros*/
{
printf("Error en la asignación de memoria.\n);
exit(1);
}
for (i=0;i<tam;i++)
{
ordenado[i]=i;
}
if (tam<=300) /*sólo los imprime si son menos de 300*/
{
printf ("Los números son:\n");
for (i=0;i<tam;i++)
{
printf ("%d ",ordenado[i]);
}
}
printf ("\n");
printf ("\n");
Metodo(ordenado,tam);
printf ("\n");
if (tam<=300)
{
printf ("Los números ordenados son:\n");
for(i=0;i<tam;i++)
{
printf ("%d ",ordenado[i]);
}
}
printf ("\n\n");
printf ("El tiempo empleado ha sido:%f\n", (fin-principio)/CLK_TCK); /*trasforma los clicks de reloj en segundos*/
free (ordenado);
}
void Aleatorio()
{
int *aleat;
int i;
int tam;
printf("Introduce el número (máximo 10000) de elementos a generar:");
scanf ("%d",&tam);
aleat=(int*)malloc(tam*sizeof(int)); /*reserva dinámica de memoria*/
if (aleat==NULL)
{
printf("Error en la asignación de memoria.\n);
exit(1);
}
randomize();/*macro que inicializa el generador de números aleatorios*/
for (i=0;i<tam;i++)
{
aleat[i]=rand() % tam; /*genera un arreglo de números aleatorios*/
}
if (tam<=300)
{
printf ("Los números son:\n");
for (i=0;i<tam;i++)
{
printf ("%d ",aleat[i]);
}
}
printf ("\n");
printf ("\n");
Metodo(aleat,tam);
printf ("\n");
if (tam<=300)
{
printf ("Los números ordenados son:\n");
for(i=0;i<tam;i++)
{
printf ("%d ",aleat[i]);
}
}
printf ("\n\n");
printf ("El tiempo empleado ha sido:%f\n",(fin-principio)/CLK_TCK);
free (aleat);
}
void Inverso()
{
int *inverso;
int i,j;
int tam;
printf("Introduce el número (menor o igual a 10000)de elementos a generar:");
scanf ("%d",&tam);
inverso=(int*)malloc(tam*sizeof(int));
j=tam-1; /*variable auxiliar para generar los numeros ordenados inversamente*/
for (i=0;i<tam;i++)
{
inverso[i]=j;
j--;
}
if (tam<=300)
{
printf ("Los números son:\n");
for (i=0;i<tam;i++)
{
printf ("%d ",inverso[i]);
}
}
printf ("\n");
printf ("\n");
Metodo(inverso,tam);
if (tam<=300)
{
printf ("Los números ordenados son:\n");
for(i=0;i<tam;i++)
{
printf ("%d ",inverso[i]);
}
}
printf ("\n\n");
printf ("El tiempo empleado ha sido:%f\n",(fin-principio)/CLK_TCK);
free (inverso);
}
void Metodo(int *puntero,int N)
{
int elige;
printf ("Este menu sirve para elegir el método de ordenación que prefieras:\n");
printf (" 1.- Selección directa.\n");
printf (" 2.- Burbuja.\n");
printf (" 3.- Inserción.\n");
printf (" 4.- Inserción binaria.\n");
printf (" 5.- Shell.\n");
printf (" 6.- Quicksort.\n");
printf (" 7.- Quicksort mejorado.\n\n");
printf ("Introduce el número de tu opción:");
scanf ("%d", &elige);
switch (elige)
{
case 1:
principio=clock();/*inicio del temporizador en clicks de reloj*/
Seleccion(puntero,N);/*llamada a la función*/
fin=clock();/*fin del temporizador en clicks de reloj*/
break;
case 2:
principio=clock();
Burbuja(puntero,N);
fin=clock();
break;
case 3:
principio=clock();
Insercion(puntero,N);
fin=clock();
break;
case 4:
principio=clock();
InsercionBinaria(puntero,N);
fin=clock();
break;
case 5:
principio=clock();
Shell(puntero,N);
fin=clock();
break;
case 6:
principio=clock();
Quicksort(puntero,N);
fin=clock();
break;
case 7:
principio=clock();
Quicksortmejor(puntero,N);
fin=clock();
break;
default:
printf ("Opcion incorrecta");
break;
}
}
Poblacio.c
/*Programa que calcula el % de individuos de una determinada edad*/
#include <stdio.h>
#include <ordena.h>
#include <buscar.h>
void main()
{
/*Declaracion de variables*/
int *anio;
int *posicion;
int n,i;
int individuos;
int anio_buscado;
int porcentaje;
/*Cabecera*/
printf ("Este programa calcula el porcentaje de individuos de una determinada edad dentrode una población.\n");
printf ("Introduce el número de individuos en la población:\n");
scanf ("%d",&individuos);
/*Reserva de memoria*/
anio=(int*)malloc(individuos*sizeof(int));
posicion=(int*)calloc(individuos*sizeof(int));/*Arreglo usado por la función de búsqueda*/
printf ("Introduce los años de nacimiento:\n");/*Lee los años de nacimiento*/
for (i=0;i<individuos;i++)
{
printf ("Individuo %d:",i+1);
scanf ("%d",&anio[i]);
}
Shell(anio,individuos);/*Ordena el arreglo*/
printf ("\n\n");
printf ("Introduce el año de nacimiento de los individuos a evaluar:");/*Pide el año a buscar*/
scanf ("%d",&anio_buscado);
BusqBinaria(anio,individuos,anio_buscado,posicion);/*Busca las posiciones donde está ese año*/
n=0;
while (posicion[n]!=0) /*Cuenta el número de indiduos nacidos en ese año*/
{
n++;
}
porcentaje=(n*100)/individuos;/*Calcula el porcentaje de individuos con esa edad*/
printf ("\n\n");
printf ("El porcentaje de individuos nacidos en %d es: %d %\n",anio_buscado,porcentaje);
}
Bibliografía
-
“Programación en C” Byron Gotfried (McGraw Hill)
-
“Programación en lenguaje C” Herbert Schildt (Mc Graw Hill)
-
“Programación en Microsoft Cpara IBM PC y compatibles” Robert Lafore - Grupo Waite (Anaya)
Nota: Los datos expuestos en la tabla pueden comprobarse experimentalmente en el proyecto Speed.prj que se adjunta en el disquete.
Página: 26
Descargar
Enviado por: | Guillermo Martinez |
Idioma: | castellano |
País: | España |