Noticias:

C.M. Burns se compra un dinosaurio.

Menú Principal

Curso de iniciación en programación C

Iniciado por Thylzos, 23 de Junio de 2008, 09:26

M.Rajoy y 1 Visitante están viendo este tema.

Thylzos

Soluciones capítulo 5º

Citar1- Realiza un programa que genere dos punteros y tres variables int, luego pida dos números al usuario, almecene cada uno en una variable int y le disigne a cada puntero una de esas variables, depués que se sumen desde los punteros y se almacenen en el int restante para luego printearla.

#include <stdio.h>

int mian (void){

int *puntero1, *puntero2;
int numero1, numero2, resultado;

printf ("Ponga el primer número: ");
scanf ("%d", numero1);

printf ("Ponga el segundo: ");
scanf ("%d", numero 2);

puntero1=&numero1;
puntero2=&numero2;

resultado=*puntero1+*puntero2;

printf ("Esta es su suma: %d", resultado);
}


Citar2- V o F

a) Con *puntero estamos trabajando con la dirección donde está el puntero. Falso. Estamos hablando del contenido de dicha dirección.

b) Con *puntero estamos trabajando con la dirección de la variable que almacena el puntero. Falso. Estamos hablando del contenido de dicha dirección.

c) Con &puntero nos encontramos trabajando con el valor de la variable a la que apunta el puntero. Falso. Estamos hablando de la dirección en que está el puntero.

d) Con puntero estamos trabajando con la dirección de memoria que almacena el puntero. Verdadero

El reto:

El método que yo había pensado es demasiado complicado, así que eché mano de un pequeño programa que codeó un conocido (conocido que corrige todos los errores del curso y cuyo nick es Oni), utiliza controles de flujo, que todavía no vimos, pero aún así, helo aquí:

#include <stdio.h>

int main(){
int *p,v;
int mod=1<<25; /*Desplazamiento a la izquierda 25 lugares (un número muy grande como resultado, sino me equivoco 2^24), con esta variable nos moveremos por todo el programa*/

printf("%p\t%p\t%d\n",p,&v,mod); /*printeamos el lugar de memoria donde están el puntero y la variable, además del valor del número mod*/
do{ /*iniciamos el control do-while (hacer-mientras)*/
if/* Si: */( (mod<=0 && p<&v) /* mod es menor o igual que cero y p menor que la dirección de memoria de v*/ || (mod>=0 && p>&v) /* o mod mayor o igual que 0 y p mayor que la dirección de memoria de v*/){ /*entonces hacemos: */
mod>>=1; /* Desplazamiento derecha una vez a mod una vez*/
mod*=-1; /*Invertimos al signo de mod*/
}
p+=mod; /* p es igual a la dirección de p más mod*/
printf("%p\t%p\t%d\n",p,&v,mod); /*printeamos otra vez lo mismo*/
}while(p!=&v); /*repetimos la operación hasta que p=&v, es decir, lo que queremos*/

return 0;
}


Explicación más certera:
Pongo un valor positivo muy grande en mod.
Si mod es positivo y el puntero señala a una posicion mas alta de donde esta la variable lo divido entre dos e invierto su signo
Si mod es negativo y el puntero señala a una posicion mas baja de donde esta la variable lo divido entre dos e invierto su signo.
Sumo al valor de la posicion donde señala el puntero el valor actual de mod.
Esto lo repito hasta que ambos son iguales. Puede no funcionar en ciertas circunstancias.
Intente no liarlo mucho, espero que se haya entendido.

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

7- Controles de flujo

Siento desilusionar a muchos, pero los programas que hicimos hasta ahora eran muy malos. Principalmente constaban de tres defectos:

- Si teníamos que repetir código, nos no quedaba otra que cortar y pegar tantas veces como queramos repetir ese grupo de instrucciones, y algunas veces nos encontramos con que ni siquiera sabemos cuántas veces son.

- El programa no se adaptaba a posibles situaciones en las que el usuario quiere alguna variante de la función de éste, es decir, siguía un ritmo e instrucciones prefijados y no se apartaba de ahí.

- Si había algún tipo de problema o de excepción en la contemplación del código, no había forma de solucionarla durante la ejecución.

Para todo, existen los controles de flujo.

7.1- Definiciones

- Control de flujo: Sentencia que nos ofrece una gran cantidad de posibilidades sobre un bloque de código, puede ir desde no ejecutarlo si no se cumple una condición, hasta repetirlo tantas veces como haga falta.

- Sentencia condicional: Sentencia que ejecuta o no un bloque de código según se cumpla o no una condición.

- Sentencia repetitiva: Sentencia que permite repetir un bloque de código una cierta cantidad de veces o hasta que se cumpla una condición.

- Sentencias anidadas: control o controles de flujo que se encuentran dentro de otro control de flujo.

7.2- Sentencias condicionales

7.2.1- Sentencia if.

Cuando queremos que se ejecute un bloque de código si se cumple una condición solamente, la solución es la sentencia if ("si" en inglés). Su sintaxis es la siguiente:

if (condición)
   bloque de código


Donde condición es un expresión lógica que debe ir entre paréntesis y que en caso de que sea  verdadera, se ejecuta el bloque de código. Este bloque de código debe ir entre { y } si tiene más de una instrucción, pero no es obligatorio en caso de que sea una.

Un pequeño ejemplo:

#include <stdio.h>

int main (void){
int numero;

printf ("Escriba un número: ");
scanf ("%d", &numero);

if (numero == 2)
   printf("Has puesto el dos");
}


7.2.2- Sentencia if-else

Cuando lo que queremos es que se ejecute un bloque de código cuando pasa algo, pero otro si pasa lo contrario, la solución radica en la sentencia if-else (si-si no en inglés), cuya sintaxis veremos a continuación:

if (condición)
   bloque de código 1
else
   bloque de código 2


Entonces, si se cumple la condición, se ejecuta el bloque de código uno, sino el dos, así de simple. Una vez más (y como será de ahora en adelante con todos los bloques de código), estos debe ir entre { y } si tienen más de un instrucción, pero no es obligatorio si tienen más de una.

Ejemplo:

#include <stdio.h>

int main (void){
int numero;

printf ("Escriba un número: ");
scanf ("%d", &numero);

if (numero == 2)
   printf("Has puesto el dos");
else {
   printf ("No has puesto el dos);
   printf ("/n ¿Cuál habrás puesto?");
}
}


7.2.3- Expresión condicional

No es exactamente una sentencia, es más un operador, pero viene para el caso. Cuando lo que queremos poner en la sentencia if-else tiene una sola línea podemos simplificar el código usando este operador, aquí la sintaxis:

expresión 1 ? expresión 2 : expresión 3

Para no tener que explicar (estoy vago), pongo su equivalente en if-else:

if (expresión 1)
   expresión 2
else
   expresión 3


Y su correspondiente ejemplo:

#include <stdio.h>

int main (void){
int numero;

printf ("Escriba un número: ");
scanf ("%d", &numero);

(numero == 2) ? printf ("El número es dos") : printf ("El número no es dos");
}


Como ven, y ya deberían saber, la condición va siempre entre paréntesis.

7.2.4- Sentencia switch

Bien, pero ¿qué pasa si tenemos muchas alternativas?, ¿tenemos que poner un montón de sentencias if-else seguidas para cubrirlas?, no, hombre, no, para eso está switch. Y esta es su sintaxis:

swith (expresión) {
   case valor_1: bloque de código 1;
                      break;
   case valor_2: bloque de código 2;
                      break;
   case valor_n: bloque de código n;
                      break;
   default:         bloque de código por defecto;


Entonces, según sea el valor de la expresión hacemos un case u otro, sin tener ningún máximo de case's posibles. Si no se da ninguna de esos case's posibles, entonces se ejecuta el bloque de código por defecto. La instrucción break la veremos más adelante, por ahora sabed que la deben poner ahí. Por cierto, recuerden que un bloque de código debe de ir entre corchetes si tiene más de una instrucción y no es obligatorio si tiene una.

Ejemplo:

#include <stdio.h>

int main (void){
int numero;

printf ("Escriba un número: ");
scanf ("%d", &numero);

switch (numero) {
   case 1: printf ("El número es uno");
              break;
   case 2: printf ("El número es dos");
              break;
   case 3: printf ("El número es tres");
              break;
   default: printf("El número no es ni uno, ni dos, ni tres");
}


7.3- Sentencias repetitivas

7.3.1- Sentencia while

Cuando queremos que un código se repita MIENTRAS (while en inglés) se cumpla una condición, una de nuestras posibilidades será while. Para entender cómo funciona, la sintaxis siempre es algo útil:

while (condición)
   bloque de código


Entonces, lo primero que hace es comprobar la condición, si se cumple, se ejecuta el bloque de código, si no no. Luego de ejecutar el bloque de código, vuelve a verificar el bloque de código, si se vuelve a cumplir, vuelve a ejecutar el bloque. Y así hasta que se deje de cumplir la condición. Pero entonces, ¿se puede dar la situación de que se pase la sentencia while sin ejecutar nunca el bloque de código?, claro, si la primera vez que verificamos la condición, ésta da que es falsa, no se ejecuta nunca el bloque.

Veamos un ejemplo:

#include <stdio.h>

int main (void) {
int numero = 0;

while (numero <= 6) {
   printf ("%d", numero);
   numero ++;
}
}


Como verán, este simple código lo único que hace es printear todos los números entre 0 y 6, contando los extremos.

7.3.2- Sentencia do-while

Su pongo que sabrán que do- while significa hacer-mientras en inglés. Pues lo que esta sentencia hace es HACER algo MIENTRAS se cumpla una condición. Acá está su sintaxis:

do
   bloque de código;
while (condición)


Pero, ¿esto no hace lo mismo que antes?, no amigo, no. Tiene una sola diferencia fundamental con la sentencia anterior (por lo demás es igual) y es que siempre se ejecuta por lo menos una vez el bloque de código, puesto que la condición se comprueba después hacerlo. Es decir, que cuando llegamos al bloque, éste se ejecuta y luego se comprueba la condición. Si es verdadera, vuelve arriba y retorna a hacer correr el bloque y si no sigue de largo.

Ejemplo:

#include <stdio.h>

int main (void) {
int numero = 0;

do {
   printf ("%d", numero);
   numero ++;
}
while (6<=0)
}


7.3.3- Sentencia for

En este caso si que hay más diferencia entre sentencias. La que se presenta a continuación también repite un bloque de código, pero lo hace de manera más distinta, aquí su sintaxis:

for (inicialización; condición; incremento)
   bloque de código


En este caso tenemos una inicialización que suele ser una asignación a una variable, una condición que como en while es la que dirá cuándo se termina la sentencia for (en el momento en que ésta sea falsa) y un incremento, que será la operación que le haremos a nuestra variable inicializada cada vez que terminemos una ejecusión del bloque de código. Como no creo que haya quedado muy claro lo explico de otra forma:

1- Se inicializa la variable.
2- Se evalúa si la condición es verdadera, sino lo es se termina sin ejecutar el bloque de código (como en while), es decir se salta al paso 6.
3- Se ejecuta el bloque de código.
4- Se hace el incremento.
5- Se vuelve al paso dos.
6- Fin se la sentencia.

Creo que ahí está más claro, pero por si acaso, acá hay un ejempo:

#include <stdio.h>

int main (void) {
int numero;

for (numero = 0; numero <=6; numero++)
   printf ("Este es el número %d", numero");
}


Lo que hace el código es declarar una variable y luego ir a una sentencia for. Una vez en ella, se inicializa la variable con el valor 0, se comprueba la condición y al dar verdadera se ejecuta el bloque de código. Cuando termina, suma uno a la variable y vuelve a ejecutar lo anterior. Y así hasta que se deje de cumplir la condición.

7.4- Sentencias anidadas

Cualquiera de las sentencias que vimos se puede combinar con otras sentencias, a esto se le llama anidar sentencias.

Un ejemplo de sentencias while e if es el siguiente programa que muestra en pantalla los números pares de entre los diez primeros números.

#include <stdio.h>

int main (void) {
int numero=1;

while (numero <= 10) {
   if (numero%2==0)
      printf ("Número %d", numero);
   }
}
}


7.5- Otras consideraciones

Además debemos tener en cuenta:

7.5.1- Bucles infinitos

Todo programa debe tener, en principio, un comienzo y un fin. Cuando escribamos alguna control de flujo debemos de asegurarnos que ésta tenga un fin y que siempre haya una forma, al menos, de salir del programa sin presionarlo. Esta es una de las reglas fundamentales que tenemos que tener en cuenta a la hora de desarrollar software eficiente.

7.5.2- Sentencias break, continue y goto.

Además de lo visto, contamos con los siguientes instrumentos a la hora de manejar controles de flujo:

- break: Sale incondicionalmente de un bucle for, while, do-while o de una sección case de una sentencia switch. Retoma por la instrucción siguiente al final del bucle.

- continue: Salta del interior de un bucle hasta el principio de éste para proseguir con la ejecución, dejando las líneas restantes sin ejecutar, similar a break.

- goto: Recomiendo no usarla, pero es útil saber que nos envía a una etiqueta, esta se debe escribir como un conjunto de símbolos seguidos de dos puntos, así se usaría:

etiqueta:
...
if (expresión)
   goto etiqueta;


7.6- Ejercicios

1- Hacer un programa que muestre las diez primeras tablas de multiplicar.

2- Realizar un programa que nos pida una contraseña y nos diga si ésta es verdadera o falsa.

3- Realizar un programa que pida un número al usuario y le diga si es positivo o negativo y si es par o impar.

El reto

No sé si esto es legal o no, pero como creo que sí (díganme en caso contrario), lo pongo. Busquen información sobre qué son las bombas lógicas y realiza una muy simple con lo que sabemos hasta ahora.

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

Soluciones del 7º capítulo

Citar1- Has un programa que muestre las 10 primeras tablas de multiplicar.

#include <stdio.h>

int main (void) {
int numero, tabla;

for (tabla=1; tabla<=10; tabla++) {
   printf ("Tabla del %d\n", tabla);
   for (numero=1; numero<=10; numero++)
      printf ("%d*%d=%d\n", tabla, numero, tabla*numero);
}
}


Citar2-Has un programa que pdia una contraseña y diga si es correcta o no.

#include <stdio.h>
#define CONTRASENIA 123

int main (void) {
int contrasenia;

printf ("Introduzca la contraseña\n");
scanf ("%d", &contrasenia);

if (contrasenia==CONTRASENIA)
   printf ("Contraseña correcta");
else
   printf ("Contraseña incorrecta");
}


Citar3- Has un programa que pida un número al usuario y diga si es par o impar

#include <stdio.h>

int main (void) {
int numero;

printf ("Ingrese un número");
scanf ("%d", numero);

if (numero%2==0)
   printf("El número es par");
else
   printf ("El número es impar");
}


El reto

Por fin llegamos, ¿no?. Bien, las bombas lógicas son programas que se esconden como procesos y esperan a que se cumpla un evento determinado (sea el día tal, la hora cual o se pulse una combinación de teclas, por ejemplo) para realizar un efecto determinado. No nos engañemos, suelen estar hechas con malas intenciones.

Voy a aprovechar esto para explicar una función que puede ser útil en este tipo de programas:

- Función int system (const char *cadena[ ]) (no recuerdo si era así, lo hago de memoria) de la biblioteca stdlib: Básicamente lo que hace es: si el puntero que le mandamos es nulo, comprueba que exista un intérprete de comandos en el sistema, en caso contrario (que contenga una cadena) pasa el contenido al intérprete como un comando.

Comencemos a hacerlo. En nuestro caso, para hacer el algoritmo que espera al evento, lo único que podemos hacer es esperar a que el usuario coloque una combinación de números, más no sabemos. Esta sería una alternativa:

do {
scanf ("%d", numeros);
}
while (numeros != NUMEROS)


Donde NUMEROS es una constante en la que se almacena esa combinación.

Para el efecto no sabemos mucho tampoco. Pero con esa nueva función se pueden hacer cosas interesantes. Por ejemplo:

for (int contador =0; contador<500; contador++)
   system ("start www.google.es");


Con sólo dos líneas le abrimos al descuidado usuario 500 veces su navegador por defecto, teniendo evidentes consecuencias. Este sería un posible código completo:

#include <stdio.h>
#include <stdlib.h>
#defina NUMEROS 123

int main (void) {
int numeros;

do {
scanf ("%d", numeros);
}
while (numeros != NUMEROS)

for (int contador =0; contador<500; contador++)
   system ("start www.google.es");
}


Por supuesto, hay que estar muy mal de la cabeza para ingresar "123" si te aparece una pantalla de cmd esperando una entrada. Pero si aprendemos un poco más de programación y nos estudiamos las API's de Windows, podremos hacer cosas muy interesantes. Sin embargo, no es necesario, precisamente, con saber las funciones de la biblioteca times, podemos esperar a determinado día para que se cumpla la condición. O, si queremos hacer efectos un poco más visuales, pero sin aprender las API, siempre se puede recurrir al scripting, por ejemplo:

#include <stdio.h>
#include <stdlib.h>
#define NUMEROS 123

int main (void) {
int numeros;

system ("echo WScript.echo \"Has pulsado 123\">blogica.vbs");

do {
scanf ("%d", numeros);
}
while (numeros != NUMEROS);

system ("blogica.vbs");
}


Esto (en teoría, no lo he probado) nos tiene que mostrar un mensaje que diga "Has pulsado 123", para lo cual nos valemos de un pequeño código en Visual Basic Script, sin embargo sería un tanto más eficiente si usásemos funciones de ficheros (para que el usuario no vea el "echo WScript.echo "Has pulsado 123">blogica.vbs") y más aún si lo hiciésemos todo en VBS, pero como no sabemos usar ficheros (todavía) y esto no es un curso de VBS, lo dejamos aquí.

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

Capítulo 8: Arrays y cadenas

Bueno, por fin nos encontramos en el capítulo donde dejaremos de manejar simples caracteres sueltos, pera meternos de lleno en las cadenas. Para eso, debemos entender y manejar otro concepto igual de útil, los arrays.

8.1- Definiciones

- Array: Se conoce como una colleción indexada de variables del mismo tipo (que llamaremos elementos del array), a las que nos referimos con el nombre de array y su índice. Pueden ser unidimensionales o multidimensionales, pero nos centreremos en las unidimensionales y en las bidimensionales.

- Cadena de caracteres: Son un conjunto de caracteres que en C se manipulan como si fuesen un array de tipo char (al contrario que en otros leguajes que tienen un tipo propio).

8.2- Arrays unidimensionales

Como habrán podido adivinar algunas personas de coeficiente itelectual elevado, estos arryas tienen una sola dimensión. Consisten, básicamente en una fila de x variables del mismo tipo, cuya x declaramos a la hora de declarar el array. Para hacerlo basta con poner primero el tipo de las variables a almacenar, luego el identificador del array y entre [] la cantidad de elementos, es decir, así:

tipo indentificador [x];

Para acceder a los elementos que declaramos (y poder darles valores, operar con ellos o lo que queramos), debemos escribir el identificador del array en cuestión y (otra vez entre corchetes) el puesto que ocupa en la "fila". Tengan en cuenta que se empieza a contar desde 0, es decir, si queremos acceder al elemento cuarto, debemos poner 3 entre corchetes, y lo mismo pasa a la hora de declararlos. Además, para incializar un array con los valores que queremos, debemos hacerlo así:

tipo identificador [x] = {valor de elemento 0, valor de elemento 1, valor de elemento 2...}

Es decir, poner entre {} la lista de valores con el orden que queremos que tengan en el array.

Veamos un par de ejemplos sencillos:

int main (void) {
int numeros [5] = {1, 2, 3, 4, 5};
int resultado [4];
int contador;

for (contador =0; cotador<4; contador++)
   resultado [contador] = numeros [cotador] + numeros [contador +1];
}


int main (void) {
int sucesion_fibonacci [10];

sucesion_fibonacci [0] = sucesion_fibonacci [1] = 1;

for (int contador = 2; contador <10; contador ++)
   sucesion_fibonacci [contador] = sucesion_fibonacci[contador - 1] + sucesion_fibonacci[contador-2];
}


Como pueden ver, son muy útiles los arrays, para no tener que declarar cada variable que necesitamos con un identificador distinto, imagínense que precisamos declarar 50 números enteros, es una bestialidad si no usamos arrays.

8.3- Arrays bidimensionales

En este caso, con encontramos con un array que tiene x filas e y columnas, es decir, tenemos una cantidad de elementos, ordenados en filas y columnas, igual a x*y. Para declararlos, debemos hacerlo así:

tipo identificador [numero de filas] [numero de columnas];

Para tratar con cada elemento, es necesario poner el identificador seguido del número de la fila en que está el elemento y el número de la columna, como en los unidimensionales,se empieza a contar desde 0. Por ejemplo:

int numero [2] [2];
numeros [0] [1]= 1;
numeros [1] [0]=2;
numeros [0] [0]=numeros [0] [1] - 1;
numeros [1] [1]= numeros[1] [0] + 1;


Después de esto nos podemos imaginar que hay una tabla así:

0 / 1
2 / 3

Como antes, podemos inicializar un array bidimensional en el momento de declararlo, haciéndolo de izquierda a derecha para las filas y de arriba a abajo para las columnas:

int numeros [3] [5] =
   {
      1, 2, 3,
      4, 5, 6,
      7, 8, 9,
      10, 11, 12,
      13, 14, 15,
   }


Así, nos podemos imaginar la siguiente tabla:

1 / 2 / 3
4 / 5 / 6
7 / 8 / 9
10 / 11 / 12
13 / 14 / 15

Un ejemplo:

#include <stdio.h>

int main (void) {
int numeros [5] [5];

for (int fila = 0; fila<5; fila ++) {
   for (int columna =0; columna <5; columna ++) {
      printf ("Introduzca un numero");
      scanf ("%d", numeros [fila] [columna];
      printf ("Este es su doble: %d", numeros [fila] [columna] * 2);
}
}
}


8.4- Declaración e inicialización de cadenas

Como ya anticipé, las cadenas no son más que arrays unidimencionales de caracteres. Y se declaran igual. La diferencia está en la inicialización, no podemos hacer esto:

char cadena[4]; /*Después explico por qué no puse [3]*/
cadena = "hola";


Para hacer un equivalente a eso, que es erroneo, debemos usar la función strcpy de la biblioteca string, cuya sintaxis es esta:

strcpy (destino, origen);

En destino se indica la cadena que queremos inicializar, y en origen el valor que le queremos dar, que puede ser una variable ya declarada, así podemos escribir en siguiente código:

#include <string.h>

int main (void) {
char cadena [5];

strcpy (cadena, "hola");
}


Ahora, ¿por qué puse 4 en vez de tres?, ¿no se empezaba a contar desde cero con los arrays?. Sí, se empieza a contar desde cero, pero con las cadenas debemos agregar siempre implicitamente el caracter '/0' que indica el final de ésta y siempre está presente. Es muy importante saber definir bien qué cantidad de caracteres usaremos para cada cadena, porque si decimos una cantidad y después le asignamos una superior estaremos sobreescribiendo partes de la memoria que no deberíamos tocar, y esto no es más que el principio de un bug de Buffer Overflow (desbordamiento de buffer) y que consiste en aprovechar esto para meter instrucciones con fines periorativos donde no deberían ir.

8.5- Entrada/salida con cadenas

8.5.1- Salida de cadenas: printf

Para imprimir cadenas en pantalla nos valeremos de la función printf y usaremos el formto "cadena" que se pone en la función con %s, es decir, así:

printf ("%s", identificador de la cadena);

Con esto imprimiremos en pantalla todo lo que halla entre el principio de la cadena y el carater '/0', nada más, por más que halla datos después de este, estos NO SE IMPRIMEN. Un ejemplito:

#include <stdio.h>

int main (void) {
char cadena [5];
strcpy (cadena, "hola");
printf ("%s, ¿cómo estás?", cadenas);
}


8.5.2.1- Entrada de datos con scanf

Una forma de ingresar cadenas desde el usuario es con la entrada formateada y usando el mismo estilo que con printf, es decir así:

scanf ("%s", &cadena);

Tened en cuenta que en este caso se aplica lo mismo que en los anteriores, después de copiar toda la cadena en la variable, la función agrega un '/0', así que nos tenemos que acordar de tenerlo en cuenta. Un ejemplo:

#include <stdio.h>

int main (void) {
char cadena[101];

printf ("Escriba una cadena de no más de 100 caracteres");
scanf ("%s", &cadena);
}


Sin embargo, esto presenta un inconveniente: la función scanf sólo lee hasta el primer espacio, es decir, si en el ejemplo anterior escribimos "Esto es un ejemplo", cadena sólo almacenará "Esto".

8.5.2.2- Entrada de datos con gets

La función gets nos soluciona el problema que tenomos con scanf, ya que nos permite almacenar carracteres. Esta es su sintaxis:

gets (identificador de la cadena);

Y para finalizar el mismo ejemplo anterior, pero ahora pudiendo almacenar espacios:

#include <stdio.h>

int main (void) {
char cadena[101];

printf ("Escriba una cadena de no más de 100 caracteres");
gets (cadena);
}


8.6- funciones con cadenas

8.6.1- Copiar cadenas: strcpy

Sí, otra vez strcpy: En realidad la función de esta función (valga la redundancia) es copiar la cadena origen en la destino. Un ejemplo para que quede más claro:

#include <stdio.h>
#inlcude <string.h>

int main (void) {
char cadena [51];
char copia [51];

printf ("Ingrese una cadena de máximo 50 caracteres: \n");
gets (cadena);
strcpy (copia, cadena);
printf ("\nEsta es la cadena que has puesto: %s", copia);
}


8.6.2- Concatenar cadenas: strcat

Al concatenar, fusionamos dos cadenas. Así, si concatenamos la cadena "Ho" con la cadena "la", el resultado es "Hola". La función que usaremos es strcat, de la biblioteca string. Su sintaxis es la siguiente:

strcat (destino, origen);

Lo que hace es, a partir del caracte '/0' de destino escribir la cadena origen, agrandándola. Un ejemplo:

#include <string.h>

int main (void) {
char cadena1 [5];
char cadena2 [4];

strcpy (cadena1, "Ho");
strcpy (cadena2, "la");
strcat (cadena1, cadena2);
}


8.6.3-  Tamaño de cadenas: strlen

Para saber el tamño de una cadena usamos la función strlen, que nos devuelve el número de carecateres que tiene sin contar con '/0'. Aquí su sintaxis:

variable numérica = strlen (cadena);

En variable numérica se almacena el número decaracteres de cadena, que puede ser tanto una variable ya declarada e inicializada, como un texto puesto por nosotros. Así que estos dos ejemplos darían el mismo resultado:

#include <string.h>

int main (void) {
int tamanio;
char cadena;

strcpy (cadena, "hola");
tamanio = strlen (cadena);
}


#include <string.h>

int main (void) {
int tamanio;

tamanio =strlen ("hola");
}


8.6.4- Comparar cadenas: strcmp

La función strcmp nos sirve para saber si una función es igual, mayor o menor que otra. Cuando digo mayor o menor no me refiero al número de caracteres, sino que al orden alfabético, por ejemplo, 'A' es menor que 'Z' porque la primera es 65 en el código ASCII y la segunda 90 y 65 es menor que 90. La sintaxis de esta función es:

strcmp (cadena1, cadena2);

Cadena1 y cadena 2 son las cadenas que se van a comparar. La función comienza comparando la primera letra de cada cadena y sigue así hasta que se encuentre con una diferencia. Lo hace, como ya vimos, en base al código ASCII, por lo que se distingue entre mayúsculas y minúsculas ('A' es menor que 'a', porque 65 es menor que 97).

Según el resultado de la comparación, la función devuelve: cero si son iguales, un valor menor que cero si cadena1 es menor que cadena2 y uno mayor que cero si se da lo contrario (cadena1 mayor que cadena2). Por lo general esta función se usa para las sentancias condicionales. Un ejemplo:

#include <stdio.h>
#include <string.h>

int main (void) {
char palabra1 [21];
char palabra2 [21];

printf ("Escriba la primera palabra (no más de 20 caracteres): ");
gets (palabra1);
printf ("Escriba la segunda palabra (no más de 20 caracteres): ");
gets (palabra2);

if (strlen (palabra1, palabra2) == 0)
   printf ("Las palabras son iguales");
else if (strlen (palabra1, palabra2) >0)
      printf ("La primera palabra es mayor");
   else
      printf ("La segunda palabra es mayor");
}


8.7- Ejercicios.

1- Has un programa que pida al usuario una frase y un número y que después diga cuántas veces sale ese número en la frase.

2- Has un programa que pida al usuario dos palabras y después las muestre alfabéticamente.

3- Has un programa que pida al usuario una cadena y después la muestre invertida.

El Reto

Has un programa que organice las tres notas de la cantidad de alumnos que indique el usuario y diga su media, la media mejor y la media peor. Además para poder usar el programa hay que poner primero una contraseña alfenumérica que debe ser correcta.

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

Soluciones del 8º capítulo

Citar1- Has un programa que pida al usuario una frase y un número y que después diga cuántas veces sale ese número en la frase.

Aquí me confundí, en vez de número quería decir carácter, así que lo corrijo como quería que sea, no como lo puse:

#include <stdio.h>
#include <string.h>

int main (void) {

char frase [50];
char caracter;
int count;
int veces;

printf ("Escriba una frase, máximo 50 caracteres: ");
gets (frase);

printf ("Escriba el caráter: ");
scanf ("%c", &caracter);

for (count=0; count<strlen(frase); count++) {
   if (frase [i]==caracter)
      veces ++;
}

printf ("%c aparece %d en la frase", caracter, veces);
}


Citar2- Has un programa que pida al usuario dos palabras y después las muestre alfabéticamente.

#include <stdio.h>
#include <string.h>

int main (void) {
char palabra1 [20];
char palabra2 [20];

printf ("Escriba la primera palabra");
gets (palabra1);
printf ("Escriba la segunda");
gets (palabra2);

if (strcmp (palabra1, palabra2) < 0)
   printf ("\n%s\n%s", palabra1, palabra2);
else
   printf ("\n%s\n%s", palabra2, palabra1);
}


Citar3- Has un programa que pida al usuario una cadena y después la muestre invertida.

#include <string.h>
#include <stdio.h>

int main (void) {
char frase [50];
int i;

printf ("Escriba una frase");
gets (frase);

printf ("\nFrase invertida: ");
for (i=strlen (frase)-1; i >=0; i--)
   printf ("%c", frase [i]);
}


El Reto

Lo más difícil de este programa es que es muy largo, por lo demás está bastante simple si nos valemos de los arrays bidimensionales.

#include <stdio.h>
#include <string.h
#define CONTRASENIA "ConTr4S3Ni4"

int main (void) {
char contrasenia [25];
printf ("Introduzca la contraseña: ");
gets (contrasenia);

if (strcmp (CONTRASENIA, contrasenia) != 0) { /*Si queremos que se pueda poner la contrasenia más de una vez sin que se cierre el programa, podemos usar while, cuestión de gusto*/
   printf ("Contraseña incorrecta");
   return (1);
}

int numero;
int alumnos;
int notas;
int suma;
int media_mayor;
int media_menor; /*También podemos declararlos todos juntos*/

printf ("Introduzca el número de alumnos");
scanf ("%d", &numero);

int tabla [4] [numero];

for (alumno =0; alumno <numero; alumno++) {
   for (nota=1; nota<=3; nota++) {
      printf ("Escriba la %d nota: ", nota);
      scanf ("%d", &tabla [nota-1] [alumno]);
}
}

for (alumno =0; alumno <numero; alumno ++) {
   for (nota =0; nota <=2; nota ++) {
      suma = suma + tabla [nota] [alumno];
}
   tabla [3] [alumno] = suma /3;
   printf ("\nLa media del alumno %d es: %d", alumno, tabla [3] [alumno]);
}

media_mayor =0;
media_menor =0;

for (alumno =0; alumno <numero; alumno++) {
   if (tabla [3] [alumno] > media_mayor)
      media_mayor= alumno;
   if (tabla [3] [alumno] < media_menor)
      media_menor= alumno;
}

printf ("La mejor media es del alumno %d, con: %d", media_mayor, tabla [3] [media_mayor]);
printf ("\nY la peor del %d con: %d", media_menor, tabla [3] [media_menor]);
}


P.D.: Estarán orgullosos, ¿no?, es el primer programa de más de 50 líneas y que además sirve para algo :P.

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

#55
Estructuras (by Master Ageof)

Una estructura, en C y C++, es un tipo definido por el usuario que puede contener variables. ¿Qué significa esto. Pues que, igual que utilizamos los tipos int, float, double, y todos los demás, podemos definir nuestros propios tipos, y una forma de hacerlo es con estructuras.

Declaración

La declaración de una estructura comienza por la palabra reservada "struct" seguida (no necesariamente) del nombre de la estructura. A continuación, entre llaves, definimos las variables que va a contener. Por último, la declaración termina en punto y coma, o bien declarando alguna variable del tipo definido
Por ejemplo, si queremos una estructura para representar los números complejos, hacemos:
struct complejo
{
double real;
double imaginario;
};


O bien:
struct complejo
{
double real;
double imaginario;
} c1, c2;


Lo que hemos creado es un tipo que engloba dos variables "double". La definición de una estructura puede hacerse en cualquier parte del código, fuera de main, dentro, dentro de otra estrucutra, en otra función...
Las variables que están englobadas por la estructura, se llamarán a partir de ahora "atributos".

Crear una variable del tipo definido es tan sencillo como crearla del tipo 'int' o 'float'. Basta con poner "struct"+ nombre de la estructura, seguido de los nombres de las variables creadas. Por ejemplo, para el tipo complejo:
struct complejo c1, c2, c3;

También podemos crear punteros a estructuras, o vectores de estructuras:
struct complejo vc[30], *pc;

Otras declaraciones de estructuras pueden ser:

struct fecha
{
int dia;
int mes;
int anyo;
};


struct persona
{
struct fecha nacimiento;
char* nombre;
int edad;
} habitantes[1000];


NOTA: Cuando usemos una estructura dentro de otra, como "persona" utiliza la estructura "fecha", hay que asegurarse de haber definido "fecha" antes que "persona" para evitar errores.

La declaración anterior es equivalente a:

struct persona
{
struct fecha
{
int dia;
int mes;
int anyo;
} nacimiento;
char* nombre;
int edad;
} habitantes[1000];



Acceso a atributos

Una vez hemos definido una variable del tipo definido, podemos acceder a los atributos que la forman del siguiente modo.
Si c1 es una variable definida como complejo (la que definimos arriba), y queremos acceder a su parte real, hacemos:
c1.real;

c1.real es la expresión por la que accedemos al atributo "real" de la variable de tipo complejo "c1".
Por ejemplo, si queremos instanciar la parte real de c1 a 5 y la parte imaginaria de c1 a 9, hacemos:
c1.real = 5;
c1.imaginario = 9;


Para leer el valor de un atributo tampoco hay mayor complicación:
c2.imaginario = c1.imaginario-5;

Hay que entender que "c1.real" puede ser visto como el nombre de una variable, y puede ser usado del mismo modo que una variable normal y corriente.

Recordemos que c1.real no es el mismo dato que c2.real. Las variables "c1" y "c2" están separadas en memoria, y cada una tiene sus propios atributos "real" e "imaginario", que son del tipo double.

Operaciones con estructuras

Si bien los atributos de una estructura son variables normales como las que estamos acostumbrados a trabajar, la variable que los engloba, que es del tipo definido por el usuario como una estructura, no puede ser usada del mismo modo.
Por ejemplo, dos variables c1 y c2 del tipo struct conjunto (definido arriba) no se pueden sumar. Podemos sumar sus atributos, pero no las variables c1 y c2 en sí mismas.

c1.real += c2.real; //Correcto
c1.imaginario += c2.imaginario; //Correcto
c1 += c2; //Incorrecto


Una de las pocas operaciones que podemos hacer con las variables c1 y c2, que podemos hacer con cualquier variable de un tipo estructura, es la asignación:
c1 = c2; //Correcto
Al hacer esto, los atributos de c1 toman los valores de los atributos de c2.

Vectores de estructuras

Si tenemos declarado un vector de un tipo definido por el usuario como estructura, como:
struct complejo vc[30];
Lo que tenemos es una lista de treinta estructuras "complejo", cada una con sus propios atributos "real" y "imaginario".
Si queremos acceder al cuarto elemento del array de complejos vc, hacemos:
vc[3]; //Recordemos que este es el cuarto, después de vc[0], vc[1] y vc[2].
Esto lo que nos da es una estructura "complejo" individual. Podemos tratarla igual que hemos tratado c1 y c2:
vc[3] = c1; //Asignamos a vc[3] los valores de c1.
vc[3].real = 5;


Podemos usar cualquier expresión:
vc[3].imaginario = vc[7].real * vc[1].real + vc[2].imaginario*vc[5].imaginario;

O podemos acceder en un bucle:
for(int i = 0; i < 30; i++)
{
vc[i].real = i;
vc[30-i].imaginario = i+1;
}


No hay límites a la imaginación.


Punteros a estructuras

Como todo puntero, un puntero a una estructura no es más que una variable que guarda, o "señala" la posición de una estructura.
Ya hemos visto su declaración:
struct complejo *pc;

Y ahora veremos su uso.
Para asignar un objeto "complejo" a este puntero, no hace falta ninguna consideración especial, basta con hacer lo mismo que hacíamos con otros tipos de datos.
pc = &c1;

Si queremos acceder a la variable de tipo "complejo" a que apunta "pc", tampoco hay ninguna excepción, y actuamos como siempre que queremos acceder al dato apuntado por un puntero:
*pc;
Esto es, la estructura a la que apunta pc.
Por ejemplo, véase la diferencia entre hacer:
1) pc = &c2;
y hacer
2) *pc = c2;
En el primer caso hacemos que "pc" apunte a "c2". En el segundo caso, lo que hacemos es asignar al complejo apuntado por "pc" (sea cual sea) los valores de los atributos de c2.

Para acceder a los atributos de una variable de tipo estructura apuntada por "pc", sabemos que *pc nos devuelve la estructura apuntada por el puntero, y que para acceder a un atributo se utiliza el punto. Por lo que para acceder a un atributo de la variable apuntada por "pc", hacemos:
(*pc).real;

NOTA: Los paréntesis se usan para indicar que primero se accede a la variable apuntada, y luego a su atributo. En caso de faltar los paréntesis daría error porque el procesador creería que tiene que acceder al valor apuntado por el atributo "real" de la variable "pc", y esto es imposible porque "real" no es un puntero ni "pc" es una variable, sino un puntero.

Pese a que esa es una forma de hacerlo, en la práctica se usa otra mucho más sencilla: El operador flecha (->). La expresión de arriba "(*pc).real", es equivalente a la siguiente expresión:
pc->real;
El operador flecha nos permite acceder de forma sencilla a un atributo de una variable apuntada por un puntero. Sólo puede ser usada por un puntero, ya que en una variable normal no tendría sentido (daría error al no poder acceder a la variable apuntada, porque las variables normales no apuntan a ninguna variable).


Estructuras y funciones

Una función en C/C++ acepta unas variables como parámetros, y devuelve otra o ninguna. Las variables que acepta tienen que tener unos tipos definidos. Pueden ser enteros, reales, punteros a matrices de enteros... o pueden ser definidos por el usuario, como estructuras.

No hay ninguna excepción a tener en cuenta para tratar estructuras en las funciones, así que mejor pondremos un ejemplo explicativo:

//Función que devuelve el módulo del complejo
double modulo(struct complejo c); //Como argumento, le pasaremos "c", del tipo "struct complejo". Devolverá el módulo de ese complejo, que es la raíz cuadrada de la suma de los cuadrados de "real" e "imaginario".

void main(void)
{
complejo c;
c.real = 3;
c.imaginario = 4;
printf("%lf",modulo(c)); //Como queda claro, lo que pasamos por parámetro es la variable c, que es la que ha sido definida como complejo
}

double modulo(struct complejo c)
{
return sqrt(c.real * c.real + c.imaginario*c.imaginario);
}


Este programa ha de devolver por pantalla el número "5".


Otro ejemplo más estrambótico, sin una razón determinada más que mostrar cómo se pasan y devuelven estructuras a funciones:

struct complejo* fc(struct complejo v[], int l);
//Esta función acepta un array de complejos y un entero que indica su longitud; y devuelvolverá un //puntero al complejo de mayor módulo del array.

void main(void)
{}

struct complejo* fc(struct complejo v[], int l)
{
//Declaramos un puntero a complejos:
struct complejo *p;

//Hacemos que este puntero apunte al primer elemento del array:
p = &v[0];

//Recorremos el array
for(int i = 0; i < l; i++)
{
//Comparamos el módulo de cada elemento con el módulo del complejo apuntado por "p":
if(modulo(v[i]) > modulo(*p))
{
//Si uno es mayor, hacemos que "p" apunte a ese elemento:
p = &v[i];
}
}

//Devolvemos el puntero:
return p;
}

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

10- Funciones

Bueno, ya sabemos cómo hacer programas medianamente inteligentes que resuelvan problemas de forma dinámica y analizasen condiciones para hacerlo. Es decir, tenemos conocimientos bien avanzados de cómo movernos con este magnífico lenguaje. Pero hay un inconveniente, tenemos que repetir mucho código. Es cierto, tenemos sentencias como for o while que nos ayudan a evitar esto, pero no es suficiente. Hay veces en que nuestras sentencias condicionales no nos ayudarán a ahorrar código, para esas ocasiones tenemos las funciones.

10.1- Definiciones

- Función: Son subprogramas que pondremos en nuestros códigos para realizar tareas usales en él y que tengamos que repetir mucho en el algoritmo. Por ejemplo, si hacemos un programa que requiera que el usuario se loguee para seguir, haremos una función que pida el usuario y la contraseña, para después comprobar si son correctos. Así, cada vez que queramos que se loguee, convocamos la función.

- Pasar argumentos a una función: Acción en la que damos a una función en el momento de llamarla un dato para que trabaje con él.

- Retornar un dato de una función: Acción en la que una función nos da un dato que se estaba manejando en ella, para que lo utilicemos en la rutina que la llamó.

Estos conceptos se entenderán mejor mientras avancemos.

10.2- Estructura

La sintaxis de una función, a nivel muy muy simple, podría ser así:

identificador () {
}


Identificador es el nombre que le damos a la subrutina para convocarla cuando haga falta. Los dos paréntesis sin nada entre ellos indican que no habrá argumentos que pasar. Y entre las dos llaves pondremos el código que ejecutará la función, como supongo que habrán adivinado. Veamos un ejemplo más complejo:

void saludo () {
printf ("Hola!");
}


Vale, ya sabemos cómo hacer una función. Ahora aprendamos cómo utilizarla. Como con las variables, lo primero es declararla, para eso se pone su cabecera en el código antes de la función main, en el caso anterior, tendríamos que poner:

void saludo ();

Ahora, para llamarla dentro del código y que ejecute sus sentencias, tenemos que hacer lo mismo, escribir su cabecera. Al final del programa, se debe escribir su cuerpo. Veamos un ejemplo simple y completo:

#include <stdio.h>

/*Función saludo*/
void saludo ();

/* Rutina principal */
int main (void) {

/*Llamamos a la función*/
saludo ();

printf ("Chau!");
}

/*Función*/
void saludo () {
printf ("Hola!");
}


Espero que haya quedado claro. Lo que pasará es que en primer lugar se mostrará el "Hola!" de la función saludo, y luego se seguirá con el contenido de main. Entonces, el orden de ejecución es, primero las sentencias que haya antes de llamar a la función (en este caso ninguna), luego el contenido de la función y por último las sentencias de después de la función. Veamos un ejemplo más complejo, porque temo que no haya quedado claro:

#include <stdio.h>

void multiplicar ();

int main (void) {

printf ("Hola, estas son las tablas de multiplicar");

multiplicar ();

printf ("Chau!");
}

void multiplicar () {
int n, i;

for (n=1; n<=10; n++) {
      for (i=1; i<=10; i++) {
            printf ("%d * %d = %d", n, i, n*i);
       }
}
}


Creo que sabrás adivinar qué pasará.

10.3- Paso de parámetros y retorno de datos.

Llegamos al ki de la cuestión, el verdadero jugo de las funciones. A ver si consigo explicarlo de forma decente.

10.3.1- Paso de parámetros

Antes quiero que quede claro el concepto de parámetro. Son datos que durante la ejecución pasamos a una función a través de variables, punteros, estructuras... tenemos para elegir. Una ves le pasamos estos datos, la función tendrá una copia de ellos (esto es algo que debemos recordar) para trabajar.

Bien, como vimos antes los dos paréntesis sin nada indicaban que no se pasarán parámetros. Pero, ¿y si queremos hacerlo?. Simple, ponemos entre los paréntesis la declaración de las variables que pasaremos. Por ejemplo:

void multiplicar (int tabla);

A esta función le pasaremos un dato de tipo int y dentro de ella, ese dato se llamará tabla. Veamos otro ejemplo:

void multiplicar (int tabla, char opcion);

Ahora pasaremos dos datos, un int que tendrá de nombre en la función tabla y un char que se llamará opcion. Para entenderlo mejor todavía, un ejemplo completo:

#include <stdio.h>

void multiplicar ( int tabla);

int main (void) {
int dato;

printf ("Introduzca el número del cual quiere saber la multiplicación:");
scanf  ("%d", &dato);

multiplicar (dato);
}

void multiplicar (int tabla) {
int n;

for (n=1; n<=10; n++)
      printf ("%d + %d = %d", tabla, n, tabla*n);
}


Como ven, lo primero que hacemos es pedir un dato que almacenamos en una variable. Esa variable se la mostramos a la función tabla, que hace una copia de la misma en la variable tabla, para poder trabajar con ella. Pero eso no es todo, podríamos haber echo esto:

#include <stdio.h>

void multiplicar ( int tabla);

int main (void) {
int dato;

multiplicar (2);
}

void multiplicar (int tabla) {
int n;

for (n=1; n<=10; n++)
      printf ("%d + %d = %d", tabla, n, tabla*n);
}


Que el programa hubiese funcionado lo mismo.

10.3.1.1- Pasar punteros a una función.

Bien, probemos una cosa:

#include <stdio.h>

void suma (int dato);

int main (void) {
int n=2;

suma (n);

printf ("%d", n);
}

void suma (int dato) {
dato= dato+1;
}


Si compilamos el programa y lo ejecutamos, notaremos que cuando se muestra el contenido de dato, éste no ha variado, sigue siendo dos. ¿Pero cómo?, ¿no le habíamos sumado uno con la función suma?. No, no lo hicimos. No sé si te acuerdas (deberías, porque te avisé), pero lo que pasamos a una función es una COPIA de una variable y esa copia de la variable sólo dura lo que dura la función, como veremos más adelante, es una variable local. Es decir, no estamos trabajando con la variable que pasamos por parámetro, sino que con una copia de la misma. Y los cambios que le hagamos a esa variable copia no afectarán a la original.

Ahora, si queremos que al pasar una variable se trabaje con ella y se "guarden" los datos en la variable que pasamos, tenemos nuestros amados punteros:

void suma (int *dato);

¿Cuál es la diferencia?, ahora la veremos:

#include <stdio>

void suma (int *dato);

int main (void) {
int n=2;

suma (&n);

printf ("%d", n);
}

void suma (int *dato) {
*dato ++;
}


Compilamos, ejecutamos y... ¡voila!, n ha cambiado. ¿Por qué?, por que en este caso, lo que pasamos a la función suma, es la dirección donde está almacenada la variable n, así que el puntero dato apunta a n. Con lo que, cuando hacemos:

*dato ++;

Estamos trabajando con el contenido de la dirección de memoria a la que apunta dato, es decir, con n. Podemos jugar de distintas formas con esto:

#include <stdio>

void suma (int *dato);

int main (void) {
int n=2, *p;

p=&n;

suma (p);

printf ("%d", n);
}

void suma (int *dato) {
*dato ++;
}


Y tendremos el mismo resultado. Cuestión de imaginación, vamos.

10.3.1.2- Pasar estructuras a una función

Se me ocurrió que quizá no entendieron mucho la explicación de Master al respecto (no porque sea mala, sino porque no lo habíamos visto), así que la copio y pero otra vez:

Una función en C/C++ acepta unas variables como parámetros, y devuelve otra o ninguna. Las variables que acepta tienen que tener unos tipos definidos. Pueden ser enteros, reales, punteros a matrices de enteros... o pueden ser definidos por el usuario, como estructuras.

No hay ninguna excepción a tener en cuenta para tratar estructuras en las funciones, así que mejor pondremos un ejemplo explicativo:

//Función que devuelve el módulo del complejo
double modulo(struct complejo c); //Como argumento, le pasaremos "c", del tipo "struct complejo". Devolverá el módulo de ese complejo, que es la raíz cuadrada de la suma de los cuadrados de "real" e "imaginario".

void main(void)
{
complejo c;
c.real = 3;
c.imaginario = 4;
printf("%lf",modulo(c)); //Como queda claro, lo que pasamos por parámetro es la variable c, que es la que ha sido definida como complejo
}

double modulo(struct complejo c)
{
return sqrt(c.real * c.real + c.imaginario*c.imaginario);
}


Este programa ha de devolver por pantalla el número "5".


Otro ejemplo más estrambótico, sin una razón determinada más que mostrar cómo se pasan y devuelven estructuras a funciones:

struct complejo* fc(struct complejo v[], int l);
//Esta función acepta un array de complejos y un entero que indica su longitud; y devuelvolverá un //puntero al complejo de mayor módulo del array.

void main(void)
{}

struct complejo* fc(struct complejo v[], int l)
{
//Declaramos un puntero a complejos:
struct complejo *p;

//Hacemos que este puntero apunte al primer elemento del array:
p = &v[0];

//Recorremos el array
for(int i = 0; i < l; i++)
{
//Comparamos el módulo de cada elemento con el módulo del complejo apuntado por "p":
if(modulo(v[i]) > modulo(*p))
{
//Si uno es mayor, hacemos que "p" apunte a ese elemento:
p = &v[i];
}
}

//Devolvemos el puntero:
return p;
}


10.3.1.3- Argumentos en main

Si eres un usuario de sistemas derivados de Unix (y si eres de Windows quizá también), sabrás que cuando llamamos a un programa por la consola, podemos pasarle unos parámetros para que trabaje. Con C, eso se puede hacer, para eso debemos agregar a la función main:

int main (int argc, char *argv[]);

Donde argc es la cantidad de parámetros (más el propio programa, es decir, el número de parámetros más uno) que se pasaron al llamar a la función y argv es un array con esos parámetros, en el que argv[0] es el nombre del programa. Para entenderlo mejor, hagamos un programita:

#include <stdio>

int main (int argc, char *argv[]) {
int n = argc -1; /*Restamos el nombre del programa*/

for (n<argc; n--) {
      printf ("%s ", *argv[n]);
}
}


Así, si compilamos este programa llamándolo "echo1" (por ejemplo :P), y tecleamos en la consola:

echo1 hola soy Agustín[/echo]

Nos mostrará "hola soy Agustín".

10.3.2- Retornar datos.

Si queremos que un dato que obtenemos por una función sea usado por la que la llama, podemos retornar esos datos. Es lo mismo que pasar argumentos, pero al revés, en vez de darle nosotros el dato a la función, ésta nos lo da a nosotros. Para hacerlo, al final de la subrutina, debemos poner:

[code]return expresion;


Expresión es eso, una expresión cuyo resultado es el dato que pasamos, por ejemplo, puede ser:

return 2; /*devuelvo el dato 2*/
return variable; /*Devuelvo el contenido de una variable*/
return variable*variable; /*Devuelvo el cuadrado de la variable*/


Y así podría seguir. Además, cuando declaramos la función, debemos especificar qué dato retornará antes de su identificador, por ejemplo:

int funcion ();

Ahora ya comprendes mejor la cabecera de main, ¿no?

Pero se estarán preguntando, ¿y dónde se guarda ese dato que retornamos?, buena pregunta. Se guarda en una variable a la que antes (en el momento de llamar la función), dijimos que compliría con ese cometido, por ejemplo:

variable=suma();

Con eso, decimos que la variable variable almacenará el valor que retorne suma. Veamos un ejemplo:

#include <stdio.h>

int suma (int n1, int n2);

int main (void) {
int numero1, numero2, resultado;

printf ("Introduzca el primer dato:");
scanf ("%d", &numero1);

printf ("Introduzca el segundo número:");
scanf ("%d", &numero2);

resultado=suma (numero1, numero2);
}

int suma (int n1, int n2) {
int res=n1 + n2;

return res;
}


También podríamos haber hecho:

#include <stdio.h>

int suma (int n1, int n2);

int main (void) {
int numero1, numero2, resultado;

printf ("Introduzca el primer dato:");
scanf ("%d", &numero1);

printf ("Introduzca el segundo número:");
scanf ("%d", &numero2);

resultado=suma (numero1, numero2);
}

int suma (int n1, int n2) {
return n1+n2;
}


Advierto que en muchos casos es más cómodo usar punteros que retornar datos (a mí me gustan más), pero eso es cuestión de gustos.

10.4- Variables globales y variables locales.

Como ya vimos, hay variables que sólo sirven en la función en la que se declaran (main o una definida por nosotros).  A estas variables se las llama locales. Usemos como ejemplo el código anterior, es decir:

#include <stdio.h>

int suma (int n1, int n2);

int main (void) {
int numero1, numero2, resultado;

printf ("Introduzca el primer dato:");
scanf ("%d", &numero1);

printf ("Introduzca el segundo número:");
scanf ("%d", &numero2);

resultado=suma (numero1, numero2);
printf ("%d", resultado);
}

int suma (int n1, int n2) {
int res=n1 + n2;

return res;
}


Aquí vemos varias variables locales. De las función main tenemos numero1, numero2 y resultado. De la función suma, n1, n2, res. Estas variables sólo están en sus respectivas funciones, no podemos usar la variable resultado en la función suma. Sin embargo, hay un método para generar variables que se puedan usar en todo el código, con independencia de qué función sea. Para eso hay que declararla fuera de toda función, es decir, en la parte en que declaramos las funciones. Por ejemplo:

int global;

void suma (int *n2);

int main (void) {
int n=2;
global=1;

suma (&n);
printf ("%d, %d", n, global);
}

void suma (int *n2) {
*n=*n+global;
global=3;
}


Si compilamos y ejecutamos, notaremos que los datos que se printean en pantalla son en primer lugar 3 y en segundo 3. Esto significa que los cambios que hicimos a global se hicieron siempre, con independencia de en qué función fue y sin necesitar pasarla por parámetro. Este recurso les puede atraer mucho, pero incluso prefiero que retornen datos y si usan punteros, tres veces mejor.

10.5- Recursividad

Estoy seguro que más de uno se preguntará si una función se puede llamar a sí misma. Y yo le digo, sí se puede y a esto se lo llama recursividad. Pero para usar esto debemos acordarnos que todo programa debe tener una salida, al menos una. Si ponemos una función que se llame a sí misma sin más, tendremos que nuestro programa no terminará nunca, ya que la función x estará siempre llamándose a sí misma.

Por esto, para usar la recursividad debemos contemplar los casos en que la función se llame y los casos en la que no. Es decir, usar alguna sentencia condicional, para asegurarnos de que habrá un fin. Veamos un ejemplo de una función que calcula un factorial:

int factorial (int numero) {
int resultado;

if (numero == 1)
   resultado=1;
else
   resultado=numero*factorial (numero -1);

return resultado}


10.6- Ejercicios.

1. Crea un programa que pida un usuario y una contraseña al usuario, compruebe si son ciertos y si no, se vuelva a pedir. Y así.

2. Realiza un programa que cuando se llame se le pasen dos palabras por parámetro, luego que se comparen y diga cuál es mayor o sin son iguales. Además, que pregunte si se quiere hacer otra comparación y en caso de ser la respuesta afirmativa, pedir otras dos y comparar.

El reto

Has un programa llamado calc que reciba en el momento de ser llamado un parámetro que indique si se quiere sumar, restar, dividir o multiplicar. Que los otros datos pasados a la función principal sean los datos con los que se realizarán esas operaciones. Luego, que el programa haga el calculo, muestre el resultado y pregunte si se quiere hacer otra operación.[/code]

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

Soluciones del 10º capítulo.

Citar1. Crea un programa que pida un usuario y una contraseña al usuario, compruebe si son ciertos y si no, se vuelva a pedir. Y así.

Este era bien fácil, qué poca imaginación que tengo...

#include <stdio.h>
#include <string.h>
#define CONTRASENIA "AlFaNuMeRiCa"
#define USUARIO "Cualquiera"

int comprobar (usuario, contrasenia);

int main (void) {
char Usuario[1024], Contrasenia[1024];

do {
printf ("Introduzca el usuario:");
scanf ("%s", &Usuario);

printf ("Introduzca la contraseña:");
scanf ("%s", &Contrasenia);

} while (comprobar (Usuario, Contrasenia)==1)
return 0;
}

int comprobar (usuario, contrasenia) {
if ((strcmp (usuario, USUARIO) || strcmp (contrasenia, CONTRASENIA)) == 0)
   return 0;
else
   return 1;
}


Citar2. Realiza un programa que cuando se llame se le pasen dos palabras por parámetro, luego que se comparen y diga cuál es mayor o sin son iguales. Además, que pregunte si se quiere hacer otra comparación y en caso de ser la respuesta afirmativa, pedir otras dos y comparar.

#include <stdio.h>
#include <string.h>

void comparar (char cadena[ ], char cadena1[ ]);

int main (int argc, char *argv[ ]) {
char cadenas[1024];
char cadenas1[1024];

if (argc==2 && comparar (argv[1], argv[2]) == 1) {
   do {
         printf ("Pon la primera cadena:");
         scanf ("%s", cadenas[ ]);

         printf ("Pon la segunda: ");
         scanf ("%s", cadenas1[ ]);
    } while (comparar (cadenas[ ], cadenas1[ ])==1)
}
return 0;
}

int comparar (char cadena[1024], cadena1[1024]) {
int opcion;

if (strcmp (cadena, cadena1) != 0) {
   if (strcmp (cadena, cadena1) > 0)
      printf ("%s es mayor que %s", cadena, cadena1);
   else
      printf ("%s es mayor que %s", cadena1, cadena); }
else
   printf ("Son iguales");

printf ("¿Quieres seguir?\n 1- Sí\n 2- No");
scanf ("%d", &opcion);

if (opcion==1)
   return 1;
else
   return 2;
}


CitarEl reto

Has un programa llamado calc que reciba en el momento de ser llamado un parámetro que indique si se quiere sumar, restar, dividir o multiplicar. Que los otros datos pasados a la función principal sean los datos con los que se realizarán esas operaciones. Luego, que el programa haga el calculo, muestre el resultado y pregunte si se quiere hacer otra operación.

#include <stdio.h>
#include <string.h>

void sumar (int opciones[ ], int cantidad);
void restar (int opciones[ ], int cantidad);
void dividir (int opciones[ ], int cantidad);
void multiplicar (int opciones[ ], int cantidad);

int main (int argc, char *argv[ ]) {
int numeros [argc - 2];
int opcion;

if (argc <= 2) {
   printf ("Modo de uso: %s [accion] [operandos]", argv [0]);
   return 1;
}

for (int i = 0; i < argc - 2; i ++)
   numeros [i] = atoi (argv [argc - 2 - i]; /* atoi (cadena) convierte un string a int*/

if (strcmp (argv [1], "s") != 0) {
   if (strcmp (argv [1], "r") != 0) {
      if (strcmp (argv [1], "m") != 0)
         multiplicar (numeros, argc - 2);
      else
         dividir (numeros, argc - 2); }
   else
      restar (numeros, argc - 2); }
else
   sumar (numeros, argc - 2);

printf ("¿Desea continuar?\n1- S\n2-N\n");
scanf ("%d", &opcion);

while (opcion == 1) {
   int cantidad;
   printf ("¿Cuántos operandos habrá?\n");
   scanf ("%d", &cantidad);
   
   for (i = 0; i<cantidad; i++) {
      printf ("Introduzca el operando %d\n", cantidad + 1);
      scanf ("%d", numeros [i]); }
   
   if (strcmp (argv [1], "s") != 0) {
      if (strcmp (argv [1], "r") != 0) {
         if (strcmp (argv [1], "m") != 0)
            multiplicar (numeros, argc - 2);
         else
            dividir (numeros, argc - 2); }
      else
         restar (numeros, argc - 2); }
   else
      sumar (numeros, argc - 2);
   
   printf ("¿Desea continuar?\n1-S\n2-N\n", &opcion);
   scanf ("%d", &opcion);
}
return 0;
}

void sumar (int opciones [ ], int cantidad) {
int total = 0;

for (int i = 0; i < cantidad; i++)
   total += opciones [i];

printf ("Resultado: %d\n", total);
}

void restar (int opciones [ ], int cantidad) {
int total = 0;

for (int i = 0; i < cantidad; i++)
   total -= opciones [i];

printf ("Resultado: %d\n", total);
}

void multiplicar (int opciones [ ], int cantidad) {
int total = 0;

for (int i = 0; i < cantidad; i++)
   total *= opciones [i];

printf ("Resultado: %d\n", total);
}

void dividir (int opciones [ ], int cantidad) {
int total = 0;

for (int i = 0; i < cantidad; i++)
   total /= opciones [i];

printf ("Resultado: %d\n", total);
}


El último me salió un poco largo, pero se puede hacer con menos líneas, por ejemplo poniendo un do...while en la función main y ahorrándome repetir todos los ifs, pero me daba un poco de pereza editarlo :P.

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

#58
10- Ficheros

Hasta ahora, los datos que conseguíamos con nuestros programas se perdían en el momento en que finalizaba la ejecución, de tal forma de que si en otro momento los necesitábamos el usuario debía ingresar todo otra vez y nosotros tratarlo todo otra vez. Pero a partir de ahora veremos un método para que esto no vuelva a suceder, los ficheros. Podremos realizar distintas funciones con ellos, como crearlos, abrirlos, añadirles datos y cerrarlos. De esta forma, si necesitamos guardar datos de configuración, por ejemplo, no hace falta que el usuario los ingrese cada vez que inicia el programa. Todas las funciones de manejo de ficheros que veremos están incorporadas en la biblioteca stdio.h.

10.1- Definiciones

- Fichero: Conjunto de datos que se almacenan en un soporte como el disco duro, un disquete, un CD-ROM o un DVD y se les da un nombre para identificarlos. Los datos de los ficheros no se pierden ni al finalizar el programa ni al apagar el ordenador.

- Dirección del fichero: Sitio donde se encuentra un fichero dentro del disco duro.

10.2- Apertura y cierre de ficheros

10.2.1- Apertura

Cada vez que un programa nuestro abra un fichero obtiene la dirección de éste y es con ella con la que trabaja a lo largo de la ejecución. Para poder realizar esta acción, usaremos la función fopen, cuya sintaxis es la siguiente:

fopen (nombre, modo);

En nombre tendremos que poner el nombre del fichero (valga la redundancia) o su ruta. ¿Cuándo poner el nombre y cuándo poner la ruta?, si el fichero se encuentra en la carpeta donde estamos ejecutando el programa, con poner el nombre sobra. Si por el contrario, se encuentra en otra dirección, tendremos que poner todo. El otro parámetro especifica en una cadena de caracteres cómo queremos que se abra o cree dicho fichero. Los posibles valores de este parámetro son:

      Modo texto
            w       crea un fichero de escritura. Si ya existe lo crea de nuevo. (También puede ser wt)
            w+       crea un fichero de lectura y escritura. Si ya existe lo crea de nuevo. (También puede ser w+t)
            a       abre o crea un fichero para añadir datos al final del mismo. (También puede ser at)
            a+       abre o crea un fichero para leer y añadir datos al final del mismo. (También puede ser a+t)
            r       abre un fichero de lectura. (También puede ser rt)
            r+       abre un fichero de lectura y escritura. (También puede ser r+t)


      Modo binario
            wb       crea un fichero de escritura. Si ya existe lo crea de nuevo.
            w+b       crea un fichero de lectura y escritura. Si ya existe lo crea de nuevo.
            ab       abre o crea un fichero para añadir datos al final del mismo.
            a+b       abre o crea un fichero para leer y añadir datos al final del mismo.
            rb       abre un fichero de lectura.
            r+b       abre un fichero de lectura y escritura.


Como pueden ver, un archivo puede abrirse de dos formas, modo texto y modo binario. No hay diferencia real entre uno y otro, salvo que en un fichero en modo texto se supone que hay fines de línea y en un momento dado hay funciones de ficheros que pueden buscarlos. Si se abre en modo binario, la información puede ser de cualquier tipo y las funciones de ficheros no buscarán fines de línea. Digo "tradicionalmente" porque en la práctica no hay ninguna diferencia entre abrirlo de una manera o de otra. Por convención, si el fichero es de texto legible se abre en modo texto. Si el fichero no es de texto legible, se abre en modo binario.

Esta función, como ya hemos mencionado, devuelve la dirección del fichero. Si se da el caso de que intente abrir un fichero y este no exista, devuelve el valor NULL. Supongo que habrán deducido cuando hablé de direcciones que tenemos que almacenar el valor devuelto en un puntero, en concreto de tipo FILE. Veamos, como de costumbre, un ejemplo ilustrativo:

#include <stdio.h>

int main (void) {
FILE *fichero;
fopen ("ejemplo.txt", "r");
return 0; /*Que si no Oni se mosquea :P*/
}


Aquí abrimos en el puntero *fichero el archivo ejemplo.txt en modo lectura.

10.2.2- Cierre

Cuando abrimos un fichero, antes de terminar el programa debemos cerrarlos. Por lo general, los compiladores nos avisan si nos dejamos alguno abierto, pero siempre es buena práctica hacerlo uno mismo, de lo contrario podríamos modificar por error su contenido y ocasionar resultados inesperados. Para eso, tenemos la función fclose:

flcose (puntero);

Donde "puntero" es el puntero que contiene la dirección del fichero. Así que vamos a cerrar el fichero anterior:

#include <stdio.h>

int main (void) {
FILE *fichero;
fopen ("ejemplo.txt", "r");
fclose (fichero);
return 0; /*Que si no Oni se mosquea :P*/
}


Pero, ¿y si no existe el fichero que se intenta abrir?, no tiene mucho sentido cerrar algo que no está abierto, así que:

#include <stdio.h>

int main (void) {
FILE *fichero;
if (fichero=fopen ("ejemplo.txt", "r") == NULL)
   return 1;
else {
   fclose (fichero);
   return 0;
}
}


10.3- Lectura y escritura

10.3.1- Escritura

Bien, bien, ya sabemos abrir ficheros, estamos a un paso de escribirlos. Podemos, por ejemplo, escribir caracteres de uno en uno con fputc:

fputc (caracter, puntero);

Donde, caracter es el carácter que queremos agregar y puntero el puntero de tipo FILE que contiene la dirección al fichero. También, podemos escribir cadenas enteras, con fputs:

fputs (cadena, puntero);

Es exactamente igual que el anterior, pero ahora cadena es una cadena, no un puntero (sé que hablo sobre lo obvio, pero seguro que alguien no se enteró). Lo "malo" de estas dos funciones es que sólo trabajan con ficheros de texto, pero aún así merecen un ejemplo:

#include <stdio.h>

int main (void) {
FILE *fichero;

if (fichero = fopen ("ejemplo.txt", "w") == NULL)
   return 1;
else {
   fputc ('a', fichero); /*Acuérdense, los caracteres entre ' y ' y las cadenas entre " y "*/
   fputs ("lo de antes era una a", fichero);
   fclose (fichero);
   return 0;
}
}


Pero, ¿para qué toto eso si tenemos una función más "interesante"?, es fwrite, que maneja ficheros de texto y binarios y todo tipo de datos:

fwrite (direccion_a_datos, tamanio_de_datos, veces, puntero);

Direccion_a_datos, como su propio nombre indica, es la dirección donde están los datos que queremos escribir en el fichero objetivo. tamanio_de_datos es el tamaño de esos datos (podemos obtenerlo con el operados sizeof ()), veces la cantidad de elementos de tamaño tamanio_de_datos que vamos a escribir (no la cantidad de veces que lo queremos escribir) y puntero el puntero de tipo FILE que contiene la dirección del fichero. Ejemplo:

#include <stdio.h>

int main (void) {
FILE *fichero;
char letra = 'a';

if (fichero = fopen ("ejemplo.txt", "w") == NULL)
   return 1;
else {
   fwrite (&letra, sizeof (letra), 1, fichero);
   fclose (fichero);
   return 0;
}
}


Lo mismo, pero con una cadena, sería usando la función así:

fwrite (&cadena, sizeof (cadena), 1, fichero);

Incluso, si el fichero es binario, podemos escribir una estructura:

#include <stdio.h>

struct t_ficha {
   char nombre [30];
   int edad; }

int main (void) {
FILE *fichero;
struct t_ficha persona1;
int n;

if (fichero = fopen ("persona.dat", "wb") == NULL)
   return 1;
else {
   for (n=1; n<=3; n++) {
      printf ("\nNombre:");
      gets (persona1.nombre);
      printf (\nEdad:");
      scanf ("%d", &persona1.edad);
      fwrite (&persona1, sizeof (persona1), 1, fichero); }
    fclose (fichero);
    return 0; }
}


10.3.1.1- Escritura formateada

Pues sí, es posible escribir con formato en fichero. Lo único que cambia es el tipo de salida que tiene, en vez de imprimir el texto en pantalla, lo hace a un fichero. Para eso tenemos la función fprintf:

fprintf (fichero, cadena_con_formato, datos...);

Lo único que cambia con nuestro querido printf es que al principio debemos especificar el puntero de tipo FILE (me parece que esto lo voy a copiar y pegar ¡, escribir siempre lo mismo es aburrido) que contiene la dirección donde está ubicado el fichero. Para que vean que no tiene complicación, un ejemplo:

#include <stdio.h>

int main (void) {
FILE *fichero;
char letra = 'a';

if (fichero = fopen ("ejemplo.txt", "w") == NULL)
   return 1;
else {
   fprintf (fichero, "El valor ASCII de a es: %d", letra);
   fclose (fichero);
   return 0;
}
}


10.3.2- Lectura

Bueno, como se hace con la enseñanza, primero aprendemos a escribir, luego a leer (Dios, me voy superando, a ver cuál será la siguiente piedra que suelte).

Como en la pantalla, cuando abrimos un fichero tenemos un puntero que nos indica en qué parte de éste estamos. Al abrirlo, lo tenemos ubicado en el principio. Cada vez que leemos algo, luego de leer hacemos que el puntero avance. De tal forma, que podemos leer un carácter (con lo que el puntero se mueve una posición) y luego leer directamente el siguiente llamando otra vez a la misma función (y el puntero se volverá a mover).

Eso es lo único más o menos complicado con respecto a la lectura, después es más de lo mismo. Por ejemplo, tenemos fgetc (el hermanito de fputc):

fgetc (fichero);

Con él leemos el siguiente carácter a la ubicación del puntero en el fichero al que apunte fichero. También tenemos fgets:

fgets (cadena, tamanio, fichero);

Ésta lee la cadena de caracteres siguiente a la ubicación del puntero hasta que se encuentre con un salto de línea. Y por supuesto, posiciona al puntero en el lugar donde se quedó. Además, debemos especificar en qué cadena guardar la lectura (la variable cadena) y el tamaño de ésta (un simple sizeof() tiene que servir). Así que leeremos hasta la cantidad máxima de caracters contenida en tamanio. Decimos máxima porque puede detenerse si hay un salto de línea, como ya comenté. Además devuelve la cadena leída o el valor NULL en caso de error o de llegar al final del fichero.

Resumamos todo con un bonito ejemplo:

#include <stdio.h>

int main (void) {
FILE *fichero;
char letra;
char cadena [256];
char *resultado;

if (fichero = fopen ("ejemplo.txt", "r") == NULL)
   return 1;
else {
   letra = fgetc (fichero);
   resultado = fgets (cadena, sizeof (cadena), fichero);
   while (resultado != NULL) { /*Es decir, hasta el final*/
      printf ("%s", cadena);
      resultado = fgets (cadena, sizeof (cadena), fichero); }
   fclose (fichero);
   return 0;
}
}


Una función de interés y que queda muy bien combinada con fgetc, es feof, la cual retorna 0 si no se esta en el final y un valor distinto si sí se está. Así, con estas dos podríamos uno por uno todos los caracteres de un fichero, ayudados por un while bien bonito.

feof (fichero);

Pero no nos desviemos del tema. La siguiente función a analizar es fread:

fread (direccion_dato, tamanio, veces, puntero);

Donde direccion_dato es la dirección de la variable donde queremos almacenar el dato leído, tamanio es el tamaño en bytes que queremos leer y veces cuantos elementos de tamaño tamanio vamos a leer. No creo que no sepan qué es la variable puntero, pero por si acaso, es el puntero de tipo FILE que contiene la dirección donde está almacenado el fichero.

Para poner a prueba lo que dije, leamos el fichero que antes escribimos con fwrite:

#include <stdio.h>

struct t_ficha {
   char nombre [30];
   int edad;
}

int main (void) {
FILE *fichero;
struct t_ficha persona1;

if (fichero = fopen ("persona.dat", "r") == NULL)
   return 1;
else {
   fread (&persona1, sizeof (persona1), 1, fichero);
   while (feof == 0) {
      printf ("\nNombre: %s", persona.nombre);
      printf ("\nEdad: %d", persona.edad);
      fread (&persona1, sizeof (persona1), 1, fichero);
   }
   fclose (fichero);
   return 0;
}
}


10.3.2.1- Lectura formateada.

Si podemos valernos de una escritura formateada, por regla de tres también de una lectura. Y como seguro que se están imaginando, se hace a través de la función fscanf:

fscanf (fichero, datos, direccion_de_variable_almacenadora);

Funciona de igual manera que la ya conocida scanf, sólo que agregando al comienzo el fichero del que se leerá. De esta forma, podemos, por ejemplo, si sabemos que un fichero contiene 5 números enteros separados por espacios, hacer lo siguiente:

#include <stdio.h>

int main (void) {
int numeros [5];
FILE *fichero;

if (fichero = fopen ("numeros.txt", "r") == NULL)
   return 1;
else {
   fscanf (fichero, "%d %d %d %d %d", numeros [0], numeros [1], numeros [2], numeros [3], numeros [4]);
   fclose (fichero);
   return 0;
}
}


10.4- Otras funciones útiles (cortesía de Hirin)

fflush(puntero_fichero);

Esta sirve para forzar la salida de la información que esta en el buffer stream de salida del fichero ( devuelve 0 si se a ejecutado con éxito ) es útil sobretodo cuando trabajas con mucha informaciones de golpe para asegurarte que se a ejecutado todo antes de hacer otra acción.

El buffer stream es el paso entre la orden que da el programa ejecutado y el resultado final en el fichero ( en el programa tu no trabajas directamente sobre el fichero sino que todo pasa por el buffer )
FILE *f;
......
fflush(f);


fseek(f, desplazamiento, origen);

Sirve para posicionar el puntero donde tu quieras lo cual nos da mucha maniobrabilidad dentro el fichero.

Donde 'f' es el puntero a fichero, 'desplazamiento' son las posiciones que se desplazara el puntero dentro el fichero y 'origen' es la posición desde donde se desplazara.
'origen' puede ser un numero o bien uno de estos tres comandos:
SEEK_SET es el inicio del fichero y equivale al numero '0'
SEEK_CUR es la posición actual del puntero
SEEK_END es el final del fichero
FILE *f;
......
fseek(f,0,SEEK_SET);   // aquí nos posicionamos al inicio del fichero


ftell(puntero_fichero);

Esta función retorna la posición actual del puntero ('-1' si hay algún error )

FILE *f;
int posición;
......
posición=ftell(f);     // nos dice donde esta el puntero en este momento


rename(nombre_actual, nombre_nuevo);

Sirve para cambiar el nombre del fichero ( de forma permanente, no solo durante la ejecución del programa )

remove(nombre_fitxer);

Elimina el fichero. Sirve sobretodo cuando en la ejecución de un programa usas temporalmente un fichero y al terminar de usarlo no lo quieres tener guardado en el PC

FILE *pas;
pas=fopen("fitxer_pas.txt","w+t");
......
rename("fitxer_pas.txt", "temporal.txt");   // aquí cambiamos el nombre del fichero
......
remove("temporal.txt");   // aquí borramos el fichero ( lo podemos comprobar yendo a la carpeta donde este guardado )


10.5- Ejercicios

1.- Has un programa al que se le pase como argumento un fichero, que lo habra e imprima en pantalla todo su contenido.

2.- Has un programa que escriba en un fichero tantas frases como quiera el usario.

3.- Has un programa al que se le pase un fichero que contendrá en la primera línea números separados por espacios y en la segunda la operación a hacer con esos números (suma, resta...), que la haga e imprima el resultado.

El Reto

Has un programa que pida el ingreso de un nombre de usuario y una contraseña al usuario. Para comprobar si están bien, las comparará con los datos de un fichero que tendrá. Por defecto, ese fichero sólo tiene el nombre de usuario "Thylzos" y la contraseña "Thylzos123", pero si logramos loguearnos bien, podremos agregar más usuarios al fichero y luego loguearnos en el programa con ellos. Pero estos que agreguemos, no podrán crear más usuarios, sólo el que viene por defecto (Thylzos). Joder, qué mal me explico. Si alguien no entendió la pedazo de liada que acabo de meter, que lo diga.

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Thylzos

Soluciones del 11º capítulo.

Citar1.- Has un programa al que se le pase como argumento un fichero, que lo habra e imprima en pantalla todo su contenido.

Cada vez denoto menos imaginación...

#include <stdio.h>

int main (void) {
FILE *fichero;

printf ("Introduzca el nombre del fichero: ");

if ((fichero = fopen (gets (), "rt")) == NULL) {
   printf ("\nEl fichero no existe");
   return 1; }

while (feof (fichero) == 0)
   printf ("%c", fgetc (fichero));

fclose (fichero);

return 0;
}


Citar2.- Has un programa que escriba en un fichero tantas frases como quiera el usario.

#include <stdio.h>

int main (void) {
FILE *fichero;

printf ("¿En qué fichero desea guardar el texto?");

fichero = fopen (gets (), "wt");

do {
   printf ("Introduzca una frase: ");
   fprintf (fichero, "%s", gets ());
   printf ("¿Desea continuar?(S/N)");
} while (getc () == 'S')

fclose (fichero);

return 0;
}


Citar3.- Has un programa al que se le pase un fichero que contendrá en la primera línea números separados por espacios y en la segunda la operación a hacer con esos números (suma, resta...), que la haga e imprima el resultado.

#include <stdio.h>

void sumar (int opciones[1024], int cantidad);
void restar (int opciones[1024], int cantidad);
void dividir (int opciones[1024], int cantidad);
void multiplicar (int opciones[1024], int cantidad);

int main (int argc, char **argv) {
FILE *fichero;
char opcion;
int numeros [1024], i;

if (argc != 2) {
   printf ("Modo de uso: %s [fichero]");
   return 1; }

if ((fichero = fopen (argv [1], "rt")) == NULL) {
   printf ("El fichero no existe.");
   return 1; }

for (i = 0; fgetc (fichero) == '\n'; i ++)
   fscanf (fichero, "%d", numeros [1]);

opcion = fgetc (fichero);

if (opcion != 's') {
   if (opcion != 'r') {
      if (opcion != 'd')
         multiplicar (numeros, i);
      else
         dividir (numeros, i); }
   else
      restar (numeros, i); }
else
   sumar (numeros, i);

fclose (fichero);

return 0;
}

void sumar (int opciones [ ], int cantidad) {
int total = 0;

for (int i = 0; i < cantidad; i++)
   total += opciones [i];

printf ("Resultado: %d\n", total);
}

void restar (int opciones [ ], int cantidad) {
int total = 0;

for (int i = 0; i < cantidad; i++)
   total -= opciones [i];

printf ("Resultado: %d\n", total);
}

void multiplicar (int opciones [ ], int cantidad) {
int total = 0;

for (int i = 0; i < cantidad; i++)
   total *= opciones [i];

printf ("Resultado: %d\n", total);
}

void dividir (int opciones [ ], int cantidad) {
int total = 0;

for (int i = 0; i < cantidad; i++)
   total /= opciones [i];

printf ("Resultado: %d\n", total);
}


CitarEl Reto

Has un programa que pida el ingreso de un nombre de usuario y una contraseña al usuario. Para comprobar si están bien, las comparará con los datos de un fichero que tendrá. Por defecto, ese fichero sólo tiene el nombre de usuario "Thylzos" y la contraseña "Thylzos123", pero si logramos loguearnos bien, podremos agregar más usuarios al fichero y luego loguearnos en el programa con ellos. Pero estos que agreguemos, no podrán crear más usuarios, sólo el que viene por defecto (Thylzos). Joder, qué mal me explico. Si alguien no entendió la pedazo de liada que acabo de meter, que lo diga.

#include <stdio.h>
#include <stdlib.h>

struct user {
   char usuario [1024];
   char contrasenia [1024]; }

int main (void) {
FILE *fichero;
struct user user1, fichero;
int opcion, permisos, ingreso

if ((fopen ("contraseñas", "w+t")) == NULL) {
   printf ("Error al abrir el fichero");
   return 1; }

printf ("Introduzca el usuario: ");
gets (user1.usuario);

printf ("\nIntroduzca la contraseña: ");
gets (user1.contrasenia);

if ((strcmp (user1.usuario, "Thylzos")) || (strcmp (user1.contrasenia, "Thylzos123")))
   permisos = ingreso = 1;

while ((ingreso != 1) || (feof (fichero) == 0)) {
   fread (&fichero, sizeof (fichero), 1, fichero);
   if ((strcmp (fichero.usuario, user1.usuario) == 0) || (strcmp (fichero.contrasenia, user1.contrasenia) == 0)
      ingreso = 1; }

if (ingreso != 1) {
   printf ("Usuario o contraseña inválidos);
   return 1; }

do {
   printf ("\n¿Qué desea hacer?\n1.- Crear otro usuario\n2.- Salir");
   scanf ("%d", opcion);
   if ((opcion != 1) || (opcion != 2)
      printf ("Opcion incorrecta");
   if (opcion == 1) {
      if (permisos == 1){
         printf ("\nIngrese el usuario: ");
         strcpy (user1.usuario, gets ());
         printf ("\nIngrese la contraseña: ");
         strcpy (user1.contrasenia, gets ());
         fwrite (&user1, sizeof (user1), 1, fichero); }
      else
         printf ("No tiene los permisos necesarios"); }
} while (opcion != 2)

flcose (fichero);

return 0;
}


Qué bestia soy, me acabo de dar cuenta que llevo todo el curso escribiendo "has" en vez de "haz"... esto no me lo perdono...

Gracias freyi *.*


Cita de: Gambit en 26 de Enero de 2010, 10:25
Follar cansa. Comprad una xbox 360, nunca le duele la cabeza, no discute, no hay que entenderla, la puedes compartir con tus amigos...

Últimos mensajes

Curso de iniciación en programación C de PerroSanxe
[Hoy a las 15:14]


Adivina la película de M.Rajoy
[Hoy a las 15:04]


Holaaaaa de PerroSanxe
[Ayer a las 14:00]


Mis dos centavos de M.Rajoy
[Ayer a las 13:58]


Mininoticias de M.Rajoy
[Ayer a las 13:53]