1.1 ARREGLOS – ARRAYS
Un array o vector es una colección de variables del mismo tipo que se referencia por un nombre común u que están situadas en posiciones contiguas de memoria. El concepto de vector en lenguaje c es análogo al de matemáticas, es decir, un conjunto de componentes o elementos del mismo tipo agrupados bajo un nombre.
Existen arrays unidimensionales, bidimensionales y multidimensionales.
1.1.1 Arrays unidimensionales:
La declaración de estos es:
Tipo variable_array[tamaño];
Donde tipo es cualquier tipo válido en C, variable_array es el nombre del array y tamaño el número de elementos del array, quedando así:
Int vector[10];
La estructura asociada a esta variable es la que podemos observar en esta tabla:
Array de diez elementos
Índice | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Componentes | Vacío | Vacio | Vacio | Vacio | Vacio | Vacio | Vacio | Vacio | Vacio | vacio |
Como se muestra en ella, los arrays en c empiezan con el 0 como índice del primer elemento; luego para “recorrer” el array del ejemplo tendríamos que ir desde vector [0] a vector [9]. Hemos de hacer énfasis en que los índices se numeran desde el 0, puesto que en la mayoría de lenguajes de alto nivel (pascal, Basic, Fortran…) esta numeración empieza en el 1. En la memoria de nuestro ordenador, el compilador reservaría una dirección para cada una de las componentes, siendo todas ellas consecutivas. La dirección del elemento de índice 0 sería la menos y la del 9 la mayor.
Otros vectores que se usas mucho en C, y muchas veces de forma transparente para el programador son los arrays de caracteres, también llamados cadena de caracteres. Aunque se puede trabajar con ellos como con otros vectores de otros tipos, su frecuencia ha hecho introducir algunas normas y operadores para escribir más cómodamente código con cadenas de caracteres.
La primera que explicaremos es la que atiende a los operadores simple y doble comilla (‘y “”). Mientras el primero indica un único carácter, el segundo implica la construcción de todo un vector o cadena de caracteres que además termina en un valor ASCII especial: el 0x0. Así en el siguiente código, se inicializa la variable c solamente con el ASCII de la letra ‘A’, pero la variable cadena2 se inicia con las letras del texto entre doble comillas más el ASCII 0x0.
Char c=’A’;
Char cadena2 [14]=”UNA CADENA”;
Cadena2 [12]=’Z’;
Cadena de longitud 14
Índice | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
Componente | ‘U’ | ‘N’ | ‘A’ | , | ‘C’ | ‘A’ | ‘D’ | ‘E’ | ‘N’ | ‘A’ | 0X0 | ? | ‘Z’ | ? |
Obsérvese que tras la última letra de la cadena se introduce el valor 0x0, pero detrás de él, el operador doble comilla no valorará las últimas componentes (la 11, 12 y 13) de forma que su valor es desconocido. La asignación posterior del elemento 12 introduce la letra 'Z' en tal elemento. Con lo anterior, el ASCII 0x0 sirve para indicar el fin de una cadena de caracteres, y muchas funciones estándar de las librerías de C consideran que una cadena o vector de caracteres empieza en el índice 0 y termina en aquél que contiene tal valor 0x0. Así, aunque la variable cadena2 puede contener hasta 14 letras, la mayoría de las funciones estándar considerarán que su texto acaba en el elemento de índice 9 (la última 'A'). Aunque aún no hemos explicado el significado que toma el nombre de un vector o una cadena cuando se usa sin referenciar a ninguna componente (o sea, sin corchetes []), pruebe a imprimir el valor de esta cadena usando, por ejemplo, printf():
Printf (cadena2);
En pantalla se imprimirá:
UNA CADENA
y la letra Z no aparecerá, es decir, la función printf ha ido imprimiendo caracteres desde el cadena2[0] hasta encontrarse con el valor 0x0. De forma análoga se han construido librerías estándar de tratamiento de cadenas que copian, comparan, etc. cadenas de caracteres. Sin embargo si el valor de fin de cadena (ASCII 0x0) se eliminara de un vector de caracteres, entonces la cadena no terminaría "nunca". En tal caso las anteriores funciones que trabajan con cadenas pueden dar resultados imprevisibles, ya que empezarían desde el primer carácter y no pararían hasta encontrar un ASCII 0x0 (lo cual puede que signifique una ejecución con miles y miles de bytes).
1.1.2 Arrays bidimensionales:
También conocidos como matrices, en general lo que se estudia en esta sección se puede extrapolar a arrays incluso n-dimensionales o multidimensionales, aunque en la práctica su uso no es muy común.
La declaración de una matriz o array bidimensional es:
Tipo variable_matriz [N] [M];
Donde N y M son el número de filas y de columnas respectivamente (la dimensión de la matriz). Se ha escrito la dimensión con letras mayúsculas, ya que deben ser constantes, y al igual que con vectores se suelen definir con constantes, por ejemplo:
#define N 4 //número de filas de las matrices a declarar
#define M 5 //número de columnas de las matrices a declarar
main ()
{ double matriz1[N][M], matriz2[N][M];
int matriz_entera [N][M];
…}
#define M 5 //número de columnas de las matrices a declarar
main ()
{ double matriz1[N][M], matriz2[N][M];
int matriz_entera [N][M];
…}
Al igual que con vectores, las matrices se numeran empezando por el índice 0, con lo cual el elemento superior izquierdo es el [0][0] y el inferior derecho es el [N-1][M-1]. En la siguiente tabla se muestra cual sería la forma y los elementos de una matriz a[4][5], de tamaño 4×5. Véase que el primer elemento del array bidimensional es el a[0][0], el siguiente sería el a[0][1], y así, hasta llegar al elemento a[3][4].
Matriz bidimensional
Elemento en | Índice de columna | |||||
0 | 1 | 2 | 3 | 4 | ||
Índice de fila | 0 | a[0][0] | a[0][1] | a[0][2] | a[0][3] | a[0][4] |
1 | a[1][0] | a[1][1] | a[1][2] | a[1][3] | a[0][4] | |
2 | a[2][0] | a[2][1] | a[2][2] | a[2][3] | a[0][4] | |
3 | a[3][0] | a[3][1] | a[3][2] | a[3][3] | a[0][4] |
Aunque se puede trabajar con elementos por separado de una matriz, lo habitual es hacer operaciones matriciales con todos los elementos en conjunto. Al igual que no existen operadores vectoriales en lenguaje C, no existen tampoco operadores matriciales, de manera que tales operaciones hay que realizarlas con bucles. El anidamiento de bucles permite el recorrido completo de un array bidimensional, y tal recorrido puede hacerse por filas o por columnas, a continuación un ejemplo que permite recorrer por filas completamente matrices… primero se valoran las matrices b y c con ciertos números y luego se realiza la operación matricial suma:
int a[4][5], b[4][5], c[4][5], i, j;
//se valoran las matrices origen A y B con ciertos números.
for (i = 0; j < 5; j++)
{ b[i][j] = i;
c[i][j] = (i * j) + j;
}
// a continuación se realiza la suma matricial
for (i = 0; i < 4; i++)
for (j = 0; j < 5; j++)
{ a[i][j] = b[i][j] + c[i][j];
//se valoran las matrices origen A y B con ciertos números.
for (i = 0; j < 5; j++)
{ b[i][j] = i;
c[i][j] = (i * j) + j;
}
// a continuación se realiza la suma matricial
for (i = 0; i < 4; i++)
for (j = 0; j < 5; j++)
{ a[i][j] = b[i][j] + c[i][j];
}
Además del recorrido simple por filas o por columnas, pueden existir otros bucles anidados que mezclen recorridos por filas con recorridos con columnas. Esto ocurre, por ejemplo, en el producto matricial (en matemáticas A=B×C), ya que en tal operación es necesario que una fila de B se multiplique por una columna de C.
Autor: Departamento de arquitectura y tegnología de computadores de la Universidad de Sevilla
Hay que tener en cuenta que, por un lado, la declaración de matrices implica una reserva o consumo de memoria que puede ser importante, y por otro, que el uso posterior de las mismas en bucles anidados puede suponer un tiempo de ejecución considerable. Por ejemplo si es pretende trabajar con una matriz donde se almacena un elemento tipo double por cada píxel de la pantalla de alta resolución, esto supone declarar una matriz de dimensión [1024][768], es decir, un consumo de 8×1024×768=6,291,456 bytes, o sea, más de 6 MB. Posteriormente cada vez que se ejecute un bucle anidado para recorrer tal matriz, ¡se ejecutarán 1024×768=786,432 iteraciones! Por último y como explicamos antes, se puede extrapolar todo lo anterior a arrays n-dimensionales o tensores. Así, el siguiente ejemplo declara un tensor de dimensión 4×5×6 e inicializa todos sus elementos a cero usando tres bucles anidados:
Doubletensor [4][5][6];
int i, j, k;
for (i = 0; I < 4; i++)
for(j = 0; j < 5; j++)
int i, j, k;
for (i = 0; I < 4; i++)
for(j = 0; j < 5; j++)
For (k = 0; k < 6; k++)
tensor[i][j][k] = 0
tensor[i][j][k] = 0
Un arreglo es una colección ordenada de variables del mismo tipo. Las variables que pertenecen a un arreglo se conocen por el nombre de elementos. El término ordenado significa que en la colección hay un primer elemento, un segundo elemento, un tercer elemento, y así sucesivamente. Además, los elementos pueden a su vez organizarse en subgrupos llamadas dimensiones. El subgrupo más pequeño posible se conoce como un arreglo de una dimensión. Un arreglo de dos dimensiones se subdivide en arreglos de una dimensión.
Un arreglo de tres dimensiones se subdivide en arreglos de dos dimensiones los cuales a su vez se dividen en arreglos de una dimensión. Un arreglo de cuatro dimensiones se subdivide en arreglos de tres dimensiones los cuales a su vez se dividen en arreglos de dos dimensiones los cuales a su vez se dividen en arreglos de una dimensión. La misma idea se aplica en arreglos de más dimensiones.
En resumen, un arreglo:
• No es una variable; es un grupo de variables conocidas como elementos
• Cada elemento ocupa una posición dentro del grupo
• Todos los elementos son del mismo tipo
• El nombre del arreglo indica donde se localiza el grupo en la memoria de la computadora
• Los arreglos se clasifican de acuerdo a las dimensiones que tengan
• Las dimensiones no tienen relación con el plano Cartesiano; nada que ver con Matemática.
• Las dimensiones indican cómo están organizados los elementos dentro del grupo.
• Los arreglos de dos dimensiones pueden visualizarse como tablas.
• Los valores que se guarden en el arreglo se almacenan en los elementos ya que los elementos son las variables.
Para crear arreglos en C++, hay que indicar:
1. el tipo de los elementos (ejemplo, int, char, double, bool o un tipo definido por el programador)
2. el nombre del arreglo
3. la cantidad de dimensiones y sus tamaños; cada dimensión comienza con el signo [ seguido por el tamaño de la dimensión y termina con el signo ]
Para crear un arreglo de una dimensión, el formato es el siguiente:
<tipo de los elementos><nombre del arreglo> [ <tamaño primera dimensión> ]
Para determinar la cantidad de elementos en un arreglo, hay que multiplicar el tamaño de cada una de las dimensiones indicados en la declaración. El tamaño debe ser un literal de tipo entero o el nombre de una constante de tipo entero. Para nombrar un elemento en un arreglo hay que indicar el nombre del arreglo, seguido de tantas parejas de corchetes [ ] como dimensiones declaradas. Dentro de cada pareja de corchetes tiene que haber un índice. La combinación de los índices indica la posición del elemento dentro del grupo. El valor mínimo para un índice es 0. El valor máximo es uno menos que el tamaño de la dimensión correspondiente en la declaración del arreglo.
1.1.3 EJERCICIOS PROPUESTOS ARRAYS
1.1 Crear un arreglo de 10 elementos usando ciclos repetitivos for.1.2 Ordenar n números a través de un arreglo
1.3 Promediar n números y colocarlos en un arreglo del mismo tamaño n.
1.4 Crear un arreglo unidimensional de tamaño 4.
1.5 Crear un arreglo unidimensional a preferencia y ordenarlo de forma ascendente.
1.6 Crear un arreglo unidimensional a preferencia y ordenarlo de forma descendente.
1.7 Crear un arreglo bidimensional a preferencia.
1.8 Crear dos matrices y sumarlas.
1.9 crear una cadena de caracteres que reciba una frase y cuente sus vocales.
Fuente: http://bc.inter.edu/facultad/hbruckman/documents/Manual.de.Referencia.de.C++.-.Parte.III.-.Arreglos.pdf
Autor: José Galindo (Dpto. de lenguajes y ciencias de computación de la Universidad de Málaga)
Autor: José Galindo (Dpto. de lenguajes y ciencias de computación de la Universidad de Málaga)
1.2 FUNCIONES EN C
En c no hay una palabra reservada para iniciar la definición de una función. El aspecto de una definición de función en C es este:
Tipo_de_retorno identificador(parámetros)
{ cuerpo_de_la_funcion
}
{ cuerpo_de_la_funcion
}
El cuerpo de la función puede contener declaraciones de variables locales (típicamente en sus primeras líneas). En el siguiente ejemplo se observa la definición de una función la cual calcula el logaritmo en base b(para b entero) de un numero x. Se ha definido de un modo menos compacto de lo que podemos hacer para ilustrar los diferentes elementos que puedes encontrar en una función:
Float logaritmo(float x, int b)
{ float logbase, resultado;
logbase = log10(b);
resultado = log10(x)/logbase;
return resultado;
}
{ float logbase, resultado;
logbase = log10(b);
resultado = log10(x)/logbase;
return resultado;
}
Las funciones con parámetros siempre tienen entre paréntesis una lista de declaraciones separadas por comas, cada declaración de parámetro indica tanto el tipo del mismo como su identificador.
1.2.1 Funciones sin parámetros:
Se puede definir una función sin parámetros dejando la palabra void como contenido de la lista de parámetros. Esta función definida por nosotros, por ejemplo, utiliza la función rand de stdlib.h para devolver un número aleatorio entre 1 y 6:
Int dado(void)
{ return rand()%6+1;
}
{ return rand()%6+1;
}
// regresar el cuadrado de un número
double cuadrado(double n)
{ return n*n;
}
1.2.2 Parámetros:
Hay dos tipos parámetros por valor y por referencia.
Por valor: Podemos ver en la función cuadrado de arriba que nos muestra el paso de parámetros por valor, en ese sentido la función cuadrado() recibe una copia del parámetro n. En la misma función se puede observar que se realiza un cálculo ( n*n ), sin embargo el parámetro original no sufrirá cambio alguno, esto seguirá siendo cierto aun cuando dentro de la función hubiera una instrucción parecida a n = n * n; o n*=n;.
Por referencia: Para mostrar un ejemplo del paso de parámetros por referencia, vamos a retomar el caso de la función cuadrado, salvo que en esta ocasión cambiaremos ligeramente la sintaxis para definir la misma. Así:
// Regresar el cuadrado de un número
double cuadrado2(double &n)
{ n *= n;
return n;
}
Se puede observar en ambos ejemplos que la primera de estas dos funciones no cambia el valor del parámetro original, mientras que la segunda si lo hace. Se puede llamar a una función de diferentes formas:
cout << cuadrado(25);
cout << cuadrado(X);
R = cuadrado(X); // guardar en R el cuadrado de X
1.2.3 EJERCICIOS PROPUESTOS FUNCIONES
1.- Dado el vector T de tamaño n. Si el tamaño es par invertir los elementos de la mitad de los elementos
Ejemplo: v=[1][2][3][4][5][6] v(invertido)=[3][2][1][6][5][4]
Ejemplo: v=[1][2][3][4][5][6] v(invertido)=[3][2][1][6][5][4]
2.- Con la formula general ax+bx+c hallar las raices cuadradas.3.- Hallar los coeficientes y verificar si la ecuación es cuadrática mediante el uso de funciones.4.- Crear un menú de opciones en el que se den las opciones tabla de multiplicar, númeroinverso, palíndromo y salir mediante el llamado de funciones.
1.3.1 CONCEPTO
La recursividad es una técnica de programación que se utiliza para realizar una llamada a una función desde ella misma, de allí su nombre. El ejemplo más utilizado por su fácil comprensión es el cálculo de números factoriales. El factorial de 0 es, por definición, 1. Los factoriales de números mayores se calculan mediante la multiplicación de 1 * 2 *..., incrementando el número de 1 en 1 hasta llegar al número para el que se está calculando el factorial. Un algoritmo recursivo es un algoritmo que expresa la solución de un problema en términos de una llamada a sí mismo. La llamada a si mismo se conoce como llamada recursiva o recurrente.(JOSE DELPHIN)
Es una alternativa diferente para implementar estructuras de repetición (ciclos). Los módulos se hacen llamadas recursivas. Se puede usar en toda situación en la cual la solución pueda ser expresada como una secuencia de movimientos, pasos o transformaciones gobernadas por un conjunto de reglas no ambiguas.
¿Por qué escribir programas recursivos?
· Son más cercanos a la descripción matemática.
· Generalmente más fáciles de analizar
· Se adaptan mejor a las estructuras de datos recursivas.
· Los algoritmos recursivos ofrecen soluciones estructuradas, modulares y elegantemente simples.
¿Cuándo usar recursividad?
· Para simplificar el código.
· Cuando la estructura de datos es recursiva ejemplo: árboles.
¿Cuándo no usar recursividad?
· Cuando los métodos usen arreglos largos.
· Cuando el método cambia de manera impredecible de campos.
· Cuando las iteraciones sean la mejor opción.
(M.A. Criado).
1.3.2 EJERCICIOS PROPUESTOS
1.- Hallar la serie Fibonacci ingresando un número por teclado usando recursividad
2.- ingrese un número por teclado y lo devuelva en binario
3.- Ingrese un número y halle la suma de sus dígitos
4.- Ingrese un numero por teclado y determinar si el número es par o impar
1.4 ESTRUCTURA
Una estructura es un tipo de dato compuesto que permite almacenar un conjunto de datos de diferente tipo. Los datos que contiene una estructura pueden ser de tipo simple (caracteres, números enteros o de coma flotante etc.) o a su vez de tipo compuesto (vectores, estructuras, listas, etc.).
A cada uno de los datos o elementos almacenados dentro de una estructura se les denomina miembros de esa estructura y éstos pertenecerán a un tipo de dato determinado.
En esta declaración aclararemos que:
· struct: es una palabra reservada de C que indica que los elementos que vienen agrupados a continuación entre llaves componen una estructura.
· nombre_estructura: identifica el tipo de dato que se describe y del cual se podrán declarar variables. Se especifica entre corchetes para indicar su opcionalidad.
· miembro1, miembro2,...: Son los elementos que componen la estructura de datos, precedidos por el tipo_dato al cual pertenecen.
Recordemos que una estructura define un tipo de dato, no una variable, lo que significa que no existe reserva de memoria cuando el compilador está analizando la estructura. Posteriormente habrá que declarar variables del tipo definido por la estructura para poder almacenar y manipular datos. (JOSE DELPHIN)
Primero veamos como seria ahora cada una de nuestras fichas:
struct f { /* Estos son los datos que guardamos: */
char nombre[30]; /* Nombre, hasta 30 letras */
char direccion[50]; /* Direccion, hasta 50 */
int edad; /* Edad, un numero < 255 */
struct f* siguiente ; /* Y dirección de la siguiente */
};
1.4.2 EJERCICIOS PROPUESTOS
1.- Realice un programa en C que lea el arreglo deporte y que devuelva los datos(nombre,pais,deporte del atleta que ha ganado el mayor número de medallas).
2.- Realice un programa en C que lea la información de varios clientes que guarde en un vector de cliente y luego recorre el vector para detectar los clientes que tienen un sueldo superior a $1000 y escriba por pantallas sus nombres.
3.- Estructuras escribe una declaracion de un tipo de estructuras que se llame alumno con la siguiente información:
identificador del alumno : un entero
sexo: un caracter ('V' para varon 'H' para hembra)
notas: un vector de 10 reales que contienen notas del alumno de 10 asignatura
pasa: un booleano (cierto o falso) que nos dice si el alumno puede pasar o no de curso
Escribe la declaracion de una variable llamada alumno de tipo talumno y el codigo necesario para poner el campo pasa de esa variable a true si el alumno ha aprobado mas de 6 asignatura o false en caso contrario.
4.- Escribe la declaracion en c de un vector de 50 elementos de tipo talumno.Escribedespues el codigo necesario para subir, a todas las chicas, 0,5 puntos en la calificacion de todas las asignatura
1.5.1 Conceptos:
· Un puntero es una variable como cualquier otra.
· Una variable puntero es un dato que contiene una dirección de memoria.
· Un puntero apunta a un espacio de memoria.
· Un puntero es una variable que únicamente puede contener direcciones de memoria, es decir: podrá almacenar solamente la dirección de memoria de otra variable o de alguna celda de memoria de la máquina.
Un puntero es una variable como cualquier otra por lo tanto hay que declararla. Una variable puntero contiene una dirección de memoria que apunta a otra posición dentro de la máquina, en esa posición se almacenan los datos a los que apunta el puntero. Los punteros se usan para manipular otras variables, para el envío de parámetros por referencia o dirección de memoria o para la asignación dinámica de memoria.
Existe una dirección especial que se representa por medio de la constante NULL y se emplea cuando queremos indicar que un puntero no apunta a ninguna dirección:
int *ptr=NULL;
1.5.2 Declaración de punteros:
Al igual que cualquier variable, las variables tipo punteros han de ser declaradas antes de utilizarlas.
La declaración de una variable tipo puntero debe indicar al compilador el tipo de dato al que apunta el puntero; para ello se hace preceder a su nombre con un asterisco (*), mediante el siguiente formato:
<tipo> *<identificador>
Ejemplos de variables punteros:
int * ptr; //Puntero de tipo entero
char * ptr; //Puntero de tipo char
1.5.3 Inicialización de punteros:
Al igual que otras variables, C no inicializa los punteros cuando se declaran y es preciso inicializarlos antes de su uso. La inicialización de un puntero proporciona a ese puntero la dirección del dato correspondiente. Después de la inicialización, se puede utilizar el puntero para referenciar los datos direccionados:
Dirección (Operador &)
&<id> devuelve la dirección de memoria (es un número hexadecimal FFF01) donde comienza la variable <id>. El operador & se utiliza para asignar valores a datos de tipo puntero:
int i;
int *ptr;
...
ptr = &i;
Indirección (Operador *)
*<id> devuelve el contenido del objeto referenciado por el puntero <id>. El operador * se usa para acceder a los objetos a los que apunta un puntero:
char c;
char *ptr;
…
ptr = &c;
*ptr = ‘A’; // Equivale a escribir: c = ‘A’
C requiere que las variables puntero direccionen realmente variables del mismo tipo de dato que está ligado a los punteros en sus declaraciones.
Operaciones no validas con punteros:
· No se pueden sumar dos punteros.
· No se pueden multiplicar dos punteros.
· No se pueden dividir dos punteros.
1.6 ARCHIVO (FILE)
Para declara un puntero tipo FILE; se escribe el prototipo llamado FILE y con un argumento del mismo tipo:
FILE* pf;
Apertura de archivo:
Para procesar un archivo en C (y en todos los lenguajes de programación), la primera operación que hay que realizar es abrir el archivo.
La apertura del archivo supone conectar el archivo externo con el programa, e indicar cómo va a ser tratado el archivo: binario, de caracteres, etc.
El programa accede a los archivos a través de un puntero a la estructura FILE, la función de apertura devuelve dicho puntero.
La función para abrir un archivo es fopen ( ) y el formato de llamada es:
fopen (nombre-archivo, modo);
FILE* pf;
pf = fopen (nombre-archivo, modo);
La función puede detectar un error al abrir el archivo, por ejemplo que el archivo no exista y se quiera leer, entonces devuelve NULL.
Ejemplo:
#include <stdio.h>
#include <stdlib.h>
FILE *ff;
char* arch = "C: \AMBIENTE\JARDINES.DAT";
if ((ff = fopen(nm, "w")) == NULL )
{
puts("Error al abrir el archivo para escribir." );
exit(-1) ;
}
1.6.1 Modos de apertura de un archivo:
Al abrir el archivo f open ( ) se espera como segundo argumento el modo de tratar el archivo. Fundamentalmente se establece si el archivo es de lectura, escritura o añadido; y si es de texto o binario. Los modos básicos se expresan en esta tabla:
Modo Significado
“r” Abre para lectura.
“W” Abre para crear nuevo archivo (si ya existe sobrescribe).
“a” Abre para añadir al final.
“r+” Abre archivo ya existente para modificar (leer/escribir).
“w+” Crea un archivo para escribir y leer (si ya existe sobrescribe).
"a+" Abre el archivo para modificar (escribir /leer) al final. Si no existe es como w+.
1.6.2 NULL y EOF
Las funciones de biblioteca que devuelven un puntero (strcpy ( ), f open ( ). .); especifican que si no pueden realizar la operación (generalmente si hay un error) devuelven NULL. Esta es una macro definida en varios archivos de cabecera, entre los que se encuentran: stdio.h y stdlib.h. Las funciones de librería de E/S de archivos, generalmente empiezan por f de file, tienen especificado que son de tipo entero de tal forma que si la operación falla devuelven EOF, también devuelven EOF para indicar que se ha leído el fin de archivo. Esta macro está definida en stdio.h.
El siguiente segmento de ccídigo lee del flujo estándar de entrada hasta fin de archivo:
int c;
while((c=getchar())!=EOF)
{
}
1.6.3 Cierre de archivos:
La función fclose (puntero-f ile) cierra el archivo asociado al puntero-f ile, devuelve EOF si ha habido un error al cerrar. El prototipo de la función se encuentra en stdio.h y es:
intfclose (FILE* pf);
Ejemplo:
<stdio.h>
FILE *pfl, * pf2;
pfl = /*Abrir dos archivos de texto, después se cierra cada uno de ellos.*/
#include fopen('C:\DATOS.DAT", "a+")
pf2 = fopen("C:\TEMPS.RET", "b+";)
fclose(pf1);
fclose(pf2);
1.6.4 Creación de un archivo secuencial:
Una vez abierto un archivo para escribir datos hay que grabar los datos en el archivo. La biblioteca C proporciona diversas funciones para escribir datos en el archivo a través del puntero a FILE asociado.
Las funciones de entrada y de salida de archivos tienen mucho parecido con las funciones utilizadas para entrada y salida para los flujos stdin (teclado) y stdout (pantalla):
printf( ), scanf ( ), getchar ( ), putchar ( ), gets ( ) y puts ( ).
Todas tienen una versión para archivos que empieza por la letra f, así se tiene: fprintf ( ), fscanf ( ), fputs ( ), fgets ( ).
La mayoría de las funciones específicas de archivos empiezan por “f”.
1.6.5 Funciones putc ( ) y fputc ()
Ambas funciones son idénticas, putc ( ) está definida como macro. Escriben un carácter en el archivo asociado con el puntero a FILE. Devuelven el carácter escrito, o bien EOF si no puede ser escrito. El formato de llamada:
putc (c, puntero-archivo);
fputc(c, puntero-archivo);
Siendo c el carácter a escribir.
1.6.6 Función malloc ();
La forma más habitual de C para obtener bloques de memoria es mediante la llamada a la función malloc ( ). La función asigna un bloque de memoria que es el número de bytes pasados como argumento, malloc ( ) devuelve un puntero, que es la dirección del bloque asignado de memoria. El puntero se utiliza para referenciar el bloque de memoria y devuelve un puntero del tipo void*.
La forma de llamar a la función malloc ( ) es:
puntero = malloc (tamaño en bytes);
Generalmente se hará una conversión al tipo del puntero:
tipo *puntero;
puntero = (tipo *) malloc (tamaño en bytes);
Operador unario sizeof ();
Se utiliza con mucha frecuencia en las funciones de asignación de
memoria. El operador se aplica a un tipo de dato (o una variable), el valor resultante es el número de bytes que ocupa.
int *r;
r = (int*) malloc (10* sizeof (int));
Al llamar a la función malloc ( ) puede ocurrir que no haya memoria disponible, en ese caso malloc ( ) devuelve NULL.
Sintaxis de llamada a malloc( )
tipo *puntero;
puntero = (tipo*) malloc (tamaño);
1.6.7 Función feof ( )
Diversas funciones de lectura de caracteres devuelven EOF cuando leen el carácter de fin de archivo. Con dicho valor, que es una macro definida en stdio.h, ha sido posible formar bucles para leer un archivo completo.
La función feof ( ) revisa el cometido anterior, devuelve un valor distinto de 0 (true) cuando se lee el carácter de fin de archivo, en caso contrario devuelve 0 (false).
1.6.8 Función rewind ( )
Una vez que se alcanza el fin de un archivo, nuevas llamadas a FEOF siguen devolviendo un valor distinto de cero (true). Con la función rewind ( ) se sitúa el puntero del archivo al inicio de éste.
El formato de llamada es:
rewind(punter0-archivo).
1.6.9 Función de salida fwrite ( )
La función fwrite ( ) escribe un buffer de cualquier tipo de dato en un archivo binario. El formato de llamada es:
fwrite(dirección, tamaño, num-elememtos,puntero-archivo) ;
1.6.10 Función de lectura fread ( )
Esta función lee de un archivo en bloques de bytes y los almacena en un buffer. El número de bytes de cada bloque (tamaño) se pasa como parámetro, al igual que el número n de bloques y la dirección del buffer (o variable) donde se almacena.
El formato de llamada:
fread(dirección, tamaño, num-elementos, puntero-archivo) ;
1.6.11 Función ftell ( )
La posición actual del archivo se puede obtener llamando a la función ftell ( ) y pasando un puntero al archivo como argumento. La función devuelve la posición como número de bytes (en entero largo: long int) desde el inicio del archivo (byte O).
1.6.12 Función fseek ( )
Con la función fseek ( ) se puede tratar un archivo en C como un arreglo que es una estructura de datos de acceso aleatorio.
fseek ( ) sitúa el puntero del archivo en una posición aleatoria, dependiendo del desplazamiento y el origen relativo que se pasan como argumentos:
intfseek(FILE *archivo, Long nbytes ,int origen);
Esta función sitúa el indicador de posición del fichero nbytes contados a partir del origen.
La función devuelve 0 cuando a tenido éxito. En caso contrario devuelve un valor diferente de 0.
Esta función maneja simplemente el indicador de posición del fichero, pero no realiza ninguna operación de lectura o escritura.
Por ello, después de usar fseek() debe ejecutarse una función de lectura o escritura.
Los valores posibles del parámetro origen y sus macros son:
Origen Macro
Principio del fichero SEEK_SET
Posición actual SEEK_CUR
Fin del fichero SEEK_END
1.7 ALGORITMO DE LA BURBUJA
1.7.1 CONCEPTOS
La ordenación por burbuja es uno de los métodos más fáciles de ordenación. El método (algoritmo) de ordenación es muy simple. Se compara cada elemento del array con el siguiente (por parejas), si no están en el orden correcto, se intercambian entre sí sus valores. El valor más pequeño flota hasta la parte superior del array como si fuera una burbuja en un vaso de refresco con gas.
Lista desordenada: 6 4 10 2 8
Primera pasada
6 4 4 4
4 6 6 6
10 10 2 2
2 2 10 8
8 8 8 10
Segunda pasada
4 4
6 2
2 6
8 8
10 10
Tercera pasada
4 2
2 4
6 6
8 8
10 10
Cuarta pasada
2
4
6
8
10
El algoritmo de intercambio utiliza una variable auxiliar
aux = x;
x = y;
y = aux;