CientoSeis

Comunidad CientoSeis => Tecnología => Mensaje iniciado por: Thylzos en 23 de Junio de 2008, 09:26

Título: Curso de iniciación en programación C
Publicado por: Thylzos en 23 de Junio de 2008, 09:26
Pues eso, como veo que la mayoría a mudado sus hilos de OGame aquí y que esta sección va medio muerta, pues mi modesto aporte. Iré publicando capítulos y soluciones periódicamente. He aquí el índice:

1- Introducción
+ A quién va dirigido el manual
+ Objetivos
+ Definiciones básicas
+ ¿Por qué C?
+ Historia
+ Sobre el compilador y los elementos necesarios

2- El lenguaje
+ Definiciones
+ Nuestro primer programa
+ Estructura del programa
+ Comentarios
+ Bibliotecas
+ Ejercicios

3- Variables y constantes
+ Definiciones
+ Identificadores
+ Tipos de datos
+ Declaraciones de variables y constantes
+ Inicialización
+ Ejercicios -> Soluciones

4- Operadores
+ Definiciones
+ Operadores aritméticos
+ Operadores de asignación
+ Operadores de asignación compuestos
+ Operadores de incremento y decremento
+ Operadores de bits
+ Operadores racionales
+ Operadores lógicos
+ Operador de conversión explícita (cast) [Cortesía de Hartigan]
+ Orden de ejecución
+ Ejercicios. -> Soluciones

5- Entrada/Salida estándar
+ Definiciones
+ Salida de caracteres
+ Entrada y salida formateada
+ Ejercicios -> Soluciones

6- Punteros
+ Definiciones
+ Qué hacen los punteros
+ Declaración de punteros
+ Utilidad de los punteros
+ Ejercicios -> Soluciones

7- Control de flujo
+ Definiciones
+ Sentencias condicionales
+ Sentencias repetitivas
+ Sentencias anidadas
+ Otras consideraciones
+ Ejercicios -> Soluciones

8- Arrays y cadenas
+ Definiciones
+ Arrays unidimensionales
+ Arrays bidimensionales
+ Declaración y cadenas
+ Entrada/Salida con cadenas
+ Funciones de cadenas
+ Ejemplos
+Ejercicios -> Soluciones

9- Estructuras
+ Definiciones
+ Declaraciones y acceso a campos
+ Estructuras y arrays
+ Ejemplo
+ Ejercicios

10- Funciones
+ Definiciones
+ Estructura
+ Paso de parámetros y retorno de datos
+ Variables locales y globales
+ Recursividad
+ Ejemplo
+ Ejercicios

11- Ficheros
+ Definiciones
+ Apertura y cierre de ficheros
+ Lectura y escritura
+ Ejemplo
+ Ejercicios

12- Estructuras dinámicas
+ Definiciones
+ Reserva y liberación de memoria
+ Listas
+ Pilas
+ Colas
+ Ejemplos
+ Ejercicios

13- Bibliotecas Estándar en C

14- Ampliando conocimientos I: Un chat
+ Definiciones
+ Sockets, ¿Cómo funcionan?
+ Funciones y estructuras útiles
+ Ejemplo
+ Ejercicios

15- Ampliando concimientos II: Nuestra biblioteca virtual

Ahora pongo el primer capítulo.

P.D.: Peluche, a ver si hacemos eso de los manuales que hablamos en OGame, que allí quedó en el aire, pero no estaría mal llevar a cabo la idea.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 23 de Junio de 2008, 09:28
1- Introducción

1.1- A quién va dirigido el manual
Este tutorial va dirigido a toda persona que, con o sin conocimientos previos de programación esté interesada en iniciarse en el lenguaje C desde cero. Por estos motivo, insto a los recién llegados al maravilloso mundo de la programación a no quedarse con ninguna duda y que pregunten todo lo que quieran, ya que como decía una profesora mía de latín "no es sabio acumular ignorancia".

1.2- Objetivos
La idea es que al acabar este tutorial pueda crear programas básicos sin problemas e incluso algunos complejos, utilizando en lenguaje C. Además me propongo que sirva para obligar a los lectores a pensar como programadores, ya que como bien dijo Peluche, pudiendo hacer esto sólo con leer un pequeño manual básico por Internet sobre cualquier lenguaje, se podrá programar sin problemas.

1.3- Definiciones útiles.
Antes de empezar con la materia en bruto daré a los iniciados unas pequeñas definiciones básicas:

- Lenguaje de programación: Conjuntos de palabras, símbolos y reglas utilizados para controlar las operaciones a realizar en un ordenador. Hay, principalmente, dos tipos de lenguajes de programación: de bajo y de alto nivel. Para entenderlos deben saber que la máquina tiene su propio lenguaje, muy difícil para nosotros, a fin de poder programar cómodamente se hicieron lenguajes de programación. Los de bajo nivel son los que proporcionan poca o ninguna abstracción del microprocesador de un ordenador, es decir que se "parecen" más al lenguaje de la máquina, por ejemplo ASM. Los de alto nivel  se caracterizan por parecerse más a nuestra forma de "pensar" que a la del ordenador, por ejemplo C/C++, Java 2, VB...

- Algoritmo: Un conjunto de pasos a seguir para solucionar un problema determinado, en esta caso informático.
- Programa: Secuencia de instrucciones a seguir, escritas en un lenguaje de programación o de script, que resuelven un problema.
- Instrucción: Orden dada a un ordenador para que éste realice una acción determinada.
- Compilador: Se encarga de traducir todo un programa fuente (formado por uno o varios ficheros fuente), escrito en un lenguaje de programación, a un programa objeto en código máquina. Una vez se que se ha generado el programa objeto es necesario un proceso de enlazado o linkado, el cual es realizado por un enlazador o link editor, para enlazar el programa objeto con otros archivos de biblioteca y otros módulos (ambos en código maquina) para dar lugar a un único programa ejecutable. En general a ambos procesos se les suele englobar bajo el nombre de compilación

1.4- Por qué elegir C
C no es un lenguaje orientado a objetos ni es tan simple como uno de scripting, entonces, ¿por qué lo elegimos para programar y es tan relevante en la actualidad como en la historia? Principalmente, por cuatro razones. La primera, es portable, puede ser implementado en casi cualquier sistema operativo. Segundo lugar, posee bibliotecas estándar con numerosas funciones que permiten realizar toda clase de acciones, además de las extensiones que nos proporcione nuestro compilador habitual. Tercero, tiene cualidades tanto de lenguaje de alto nivel (como las estructuras típicas de este tipo de lenguaje) y de alto nivel (como manejo de punteros, de bits y control directo de memoria y periféricos). Por último y no por ello menos importante, C es un lenguaje en el cual se basan otros de igual o más relevancia en la actualidad, como C++, Java 2 y C#, y siempre es conveniente conocer las bases antes de estudiar el resto.

1.5- Historia
Bien, C es un lenguaje creado por los Laboratorios Bell hacia alrededor de 1970 (no estoy muy seguro). Tomó este nombre porque estaba hecho tomando muchas características del lenguaje B. Principalmente estaba orientado a la implementación de sistemas operativos, principalmente de Unix. Sobre los motivos de porqué lo crearon, no creo que esté muy claro, algunos dicen que fue el resultado del deseo de los programadores de jugar con Space Travel (el padre de los virus informáticos), pero a mí me gusta más la idea de que se creó para poder reescribir el sistema operativo Unix, ya que ASM era complicado y así sería más fácil hacer modificaciones.

El hecho es que C comenzó a triunfar y a usarse en distintos plataformas. Como era muy popular el Instituto Nacional Estadounidense de Estándares estandarizó (valga la redundancia) el lenguaje en 1989 en el ANSI C o C89.
C siguió evolucionando y en 1999 fue ampliado dando como resultado el C99.

1.6- Sobre el compilador y los elementos necesarios.
Bien, finalmente llegamos al final del primer capítulo, para el próximo comenzaremos con el lenguaje en sí. Para eso debo aclarar algo, C es un lenguaje que precisa compilador, es decir, un programa que lo traduzca al lenguaje del ordenador. En principio no creo que tengamos problemas en este aspecto, ya que no veremos aplicaciones tan complicadas como para necesitar uno específico. Para los que tengan Windows pueden usar el Dev C++ que es gratis e incluso el Virtual C++ en modo consola (aunque no lo recomiendo) y los que se decanten por Linux pueden probar el gcc, una herramienta tan útil como interesante.
Título: Re: Curso de iniciación en programación C
Publicado por: Des en 23 de Junio de 2008, 16:46
Oh, me gusta es hilo para iniciarse en la programación en C, ponedlo con chincheta.
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 23 de Junio de 2008, 17:19
Thylzos, cuando termines y lo corrijas, pásalo a pdf, que es más cómodo.
Título: Re: Curso de iniciación en programación C
Publicado por: Ray en 23 de Junio de 2008, 17:23
Gracias, termínalo cuanto antes.

Enchincheto :$
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 23 de Junio de 2008, 21:42
Gracias a ambos.

2- El lenguaje

Es hora de comenzar con lo más interesante, la programación. Antes de meternos en ello haremos nuestro primer programa y daremos una definición que nos será útil.

2.1- Definiciones.

Función: Algoritmo escrito en un lenguaje de programación que realiza una tarea (o función) específica.

2.2- Nuestro primer programa

Compilar muchas veces es distinto en Windows que en Linux, principalmente porque no se ponen de acuerdo y nos obligan (casi siempre) a usar software distinto para cada uno de estos sistemas operativos. Por eso explicaré cómo hacerlo con un compilador para Windows y a su vez con uno para Linux. Este es el código que usaremos:

#include <stdio.h>

/*Esto es un comentario*/

int main (void){
printf ("Este es mi primer programa");
return (0);
}


2.2.1- Compilando en Windows.

Aquí es tan fácil como abrir nuestro compilador habitual, copiar el código en el procesador de código de este y darle al botón de compilar. Espero que nadie se allá perdido :P...

2.2.2- Compilado en Linux.

Ahora entramos en una zona un poco más revoltosa, no porque sea difícil, sino porque hay muchos pasos. Primero abrimos un procesador de texto que nos permita guardar archivos sin formato, copiamos el código y lo guardamos con el nombre que queramos y la extension .c (por ejemplo, primprog.c), luego vamos al intérprete de comandos (shell) y, suponiendo que tenemos instalado el gcc, vamos a la carpeta donde está para escribir: gcc -o [nombre del archivo (sin extensión)] [nombre del archivo más la extensión], lo hacemos así para que el ejecutable que genere tenga el mismo nombre que el código fuente, pero en el primer apartado puedes poner lo que quieras.

Al final nos debe quedar algo así (en ambos casos):

(http://img461.imageshack.us/img461/2333/primprogcu6.th.jpg) (http://img461.imageshack.us/my.php?image=primprogcu6.jpg)

Si nos da un fogonazo de MS-DOS al ejecutar el programa, simplemente ve al cmd y ejecutarlo desde allí para ver la salida.

2.3- Estructura de un programa

Bien, ahora que hemos hecho nuestro primer programa, veamos qué características básicas debe tener todo programa que escribamos, para eso nos valeremos del código anterior.

Citar#include <stdio.h>

Con esto le decimos a nuestro compilador que vamos a usar las funciones ya definidas que vienen en la biblioteca stdio, no desesperen, esto lo veremos con profundidad más adelante en este capítulo.

Citar/*Esto es un comentario*/

Para quien no lo haya entendido, esto es un comentario. También lo veremos mejor más adelante.

Citarint main (void){

Aquí decimos que comienza la función principal del programa, que vendría a ser como la puerta de entrada de una casa y es la que se ejecuta primero en todo después de compilarlo. En definitiva, es el proceso principal del programa.

Citarprintf ("Este es mi primer programa");

En esta ocasión llamamos a la función printf (que imprime un texto en pantalla) con la cadena "Este es mi primer programa" para que imprima.

Citarreturn (0);
}

Bien, muchos programadores experimentados se preguntarán para que retorno el valor 0 en un programa tan insignificante, antes de responder voy a explicar algunas cosas. Aquí lo que hacemos es concluir enviando el valor cero al sistema operativo, es decir diciéndole que acabamos el programa sin ningún percance, si no lo hubiese hecho este creería que las instrucciones que vienen después (que no nos interesa saber cuales son) son válidas, si usamos windows esto como mucho provoca el cierre de la ventana, pero si estamos programando en MS-DOS (no tiene nada de malo, yo me vicie mucho tiempo con MS-DOS ¬¬), nos saldrán un montón de errores y se nos colgará la máquina, por eso esta ahí esa función.

Habrán notado que el desarrollo del programa se puso entre { y }, es porque todo bloque de código va escrito en entre estos dos signos.

De esta pequeña biblia podemos sacar en limpio:
a) Un programa en C comienza incluyendo las bibliotecas que va a usar.
b) Un programa en C puede tener comentarios.
c) La función principal es el alma de un programa en C y la que describe todo su proceso principal.
d) Los bloque de código (conjunto de instrucciones) van entre { y }.

2.4- Los comentarios.

Los comentarios no son más que aclaraciones que escribimos en el código y las cuales, además, no se van a compilas. Sirven para dos motivos fundamentales. Uno, que otro entienda lo que queremos hacer con el programa (en ocasiones un código en un lenguaje que dominamos nos puede parecer a chino). Dos, orientarte tú mismo en tus propios códigos para hacer depuraciones de este o actualizarlo. Si eres como yo cuando empecé (en mis tiempos mozos :P), esto te parecerá una tontería ¿para qué gastar tiempo en algo que no sirve a la ejecución del programa?, yo aprendí por las malas, espero que tú no. Cuando llevas quinientas (o más) líneas de código, compilas y el programa te da un error como regalo, buscarlo y solucionarlo en ese tochazo son comentarios es una tortura que debería envidiar la Inquisición.

Un comentarios debe ser claro y proporcionar información sencilla, concreta y directa. En nuestro programa, el comentario no cumple ni una de estas funciones, pero si lo modificamos sólo  con leerlos los comentarios que escribiésemos sabríamos qué hace y cómo, pero eso lo harán ustedes en los ejercicios.

Un consejo, no esperes a haber terminado de escribir el código para ponerle comentarios, en uno de 20 líneas es irrelevante, pero hay que tener mucha fuerza de voluntad para revisar 1000 dos veces, una para escribirlo y otra para agregarle los comentarios, así que hazlo mientras programas.

Ahora, ¿cómo se escriben los comentarios?, simple entre /* y */. Todo lo que haya entre ellos dos no se ejecutará. No importa si el comentario ocupa más de una línea o si ponemos funciones dentro de él. Así que no quiero que me vengan con errores que consisten en haber escrito código del programa en un comentario, tengan en cuenta que una vez lo abren (escriben /*), este se extiende hasta que lo cerremos (escribamos */).

2.5- Bibliotecas

C usa bibliotecas. Estas son colecciones de funciones que nos sirven para utilizar código predefinido (como mostrar textos en pantalla, abrir archivos, emitir sonido...) que nos facilitan nuestro trabajo. Para poder usar estas funciones debemos llamar a la biblioteca como hicimos en nuestro programa. A medida que vaya avanzando y utilizando más y más funciones (como la ya probada printf) iré poniendo las bibliotecas a las que pertenecen, y al final daré las bibliotecas estándar. Quizá si me emociono (cosa muy probable) enseñe a leerlas y a crear nuestras propias bibliotecas.

Para usar una, sólo debemos poner al principio "#include" y entre los signos < y > el archivo de cabecera, que posee el mismo nombre que la biblioteca que vallamos a usar más la extensión .h, por ejemplo:

Citar#include <alloc.h>

2.6 Ejercicios

1- Modifica el primer programa que hicimos agregando comentarios suficientes a fin de que no se haga necesario leer el código fuente para entender el programa.

EL RETO

Cada capítulo pondré un ejercicio que supondría un "reto" para quien no ha dado nunca programación, en un principio serán fáciles pero con el paso de los temas se irán complicando. Eso sí, se pueden resolver con un poco de lógica, no hace falta ser un genio para solucionarlos.

El reto: Modifica el código fuente para que en vez de decir: "Este es mi primer programa" diga: "He modificado mi primer programa".
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 23 de Junio de 2008, 22:24
Está de madre, aunque con alguna screen sería lo mejor, lo que te tiene que salir... porque yo no veo qué se puede hacer con eso. Amén de que me lo acabo de bajar(el dev-c++) he hecho eso y le doy a compilar y no pasa nada.

Una descarguita del programa, por si acaso, tampoco estaba mal.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 23 de Junio de 2008, 22:44
Cita de: Blanquito en 23 de Junio de 2008, 22:24
Está de madre, aunque con alguna screen sería lo mejor, lo que te tiene que salir... porque yo no veo qué se puede hacer con eso. Amén de que me lo acabo de bajar(el dev-c++) he hecho eso y le doy a compilar y no pasa nada.

Una descarguita del programa, por si acaso, tampoco estaba mal.

¿A qué te refieres con no pasa nada?. Puse una screen de lo que tendría que dar el programa... ¿qué otras te parece que debería agregar?, gracias por los comentarios.
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 23 de Junio de 2008, 22:58
Perdona, es que la imagen no se me carga.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 23 de Junio de 2008, 23:07
Cita de: Blanquito en 23 de Junio de 2008, 22:58
Perdona, es que la imagen no se me carga.

Ah, vale. En cuanto al error, ¿pudiste solucionarlo?, ¿qué exactamente lo que pasa?, ¿un error de compilación?, ¿se ve un pantallazo de conosla de MS-DOS y listo?
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 23 de Junio de 2008, 23:11
Le doy y no pasa nada, sale una ventana pequeña durante 1 décima que no me da tiempo a ver. Ya veré...
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 23 de Junio de 2008, 23:23
CitarSi nos da un fogonazo de MS-DOS al ejecutar el programa, simplemente ve al cmd y ejecutarlo desde allí para ver la salida.

Ahí tienes la solución. Abres la terminal (ejecutar -> cmd), tecleas:

Citarcd Directorio-donde-este-el-programa

Y escribes el nombre que le hayas puesto al programa. Otra alternativa es agregar esto al código:

getchar();

Justo antes del return 0. Pasa eso porque el programa muestra el contenido y cierra. No espera. Pero con esta última línea haces que hasta que presiones una tecla.
Título: Re: Curso de iniciación en programación C
Publicado por: Ray en 24 de Junio de 2008, 04:54
Pues yo tengo el Dev-C++ y no funciona.

Qué es, nuevo código fuente? Porque si le doy a ejecutar dice que no está compilado, y si lo abro una vez compilado me lo manda al Dev-C++ como código. Con em cmd no lo abre, puedes decirme el comando?
Que lio  :wiiiiii:
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 24 de Junio de 2008, 08:00
Cita de: rayden en 24 de Junio de 2008, 04:54
Pues yo tengo el Dev-C++ y no funciona.

Qué es, nuevo código fuente? Porque si le doy a ejecutar dice que no está compilado, y si lo abro una vez compilado me lo manda al Dev-C++ como código. Con em cmd no lo abre, puedes decirme el comando?
Que lio  :wiiiiii:

Es que me parece que no lo compilaste :P. Así es bastante difícil ayudarte, por ponme una captura en la que me muestres el Dev-C++ con el programa después de que lo hayas compilado y de que hayas apretado el botón para ejecutar.
Título: Re: Curso de iniciación en programación C
Publicado por: Ray en 24 de Junio de 2008, 18:09
Cita de: Thylzos en 24 de Junio de 2008, 08:00
Cita de: rayden en 24 de Junio de 2008, 04:54
Pues yo tengo el Dev-C++ y no funciona.

Qué es, nuevo código fuente? Porque si le doy a ejecutar dice que no está compilado, y si lo abro una vez compilado me lo manda al Dev-C++ como código. Con em cmd no lo abre, puedes decirme el comando?
Que lio  :wiiiiii:

Es que me parece que no lo compilaste :P. Así es bastante difícil ayudarte, por ponme una captura en la que me muestres el Dev-C++ con el programa después de que lo hayas compilado y de que hayas apretado el botón para ejecutar.
Si que lo he compilado. De hecho lo intenté varias veces ejecutando, compilando, ejecutando y compilando y más cosas xD

Mira, hago Control+N para un nuevo código fuente, copio el código y le doy a compilar.

(http://img50.imageshack.us/img50/3153/c1gc3.th.jpg) (http://img50.imageshack.us/my.php?image=c1gc3.jpg)

(http://img371.imageshack.us/img371/8784/c2ig3.th.jpg) (http://img371.imageshack.us/my.php?image=c2ig3.jpg)

Pero me queda esto!

(http://img143.imageshack.us/img143/1493/c3br8.jpg)

Que se abre con el Dev-C++ para editarlo, y yo lo que quiero es ver cómo funciona.
Dices que lo abra con el cmd? Pues dime cómo, porque una vez en el directorio donde se encuentra no puedo abrirlo.
Gracias por todo :lol:
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 24 de Junio de 2008, 18:55
Cretino, compilar no cambia el archivo del código, sino que genera un ejecutable.
Título: Re: Curso de iniciación en programación C
Publicado por: Ray en 24 de Junio de 2008, 19:16
Cita de: Faerindel en 24 de Junio de 2008, 18:55
Cretino, compilar no cambia el archivo del código, sino que genera un ejecutable.
Y no hay ejecutable por ningún lado >.<
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 24 de Junio de 2008, 19:31
Cita de: rayden en 24 de Junio de 2008, 19:16
Cita de: Faerindel en 24 de Junio de 2008, 18:55
Cretino, compilar no cambia el archivo del código, sino que genera un ejecutable.
Y no hay ejecutable por ningún lado >.<
Te tiene que salir un .exe con el mismo nombre en la misma carpeta donde está el .cpp, pero claro, a menos que lo busques mirando todas las extensiones no lo vas a encontrar.
Título: Re: Curso de iniciación en programación C
Publicado por: Ray en 24 de Junio de 2008, 19:49
Que no, que solo hace ese archivo.

Seguro que tengo el programa correcto? O Falta hacer algo, yo que se...
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 24 de Junio de 2008, 20:00
Pues yo lo acabho de bajar, instalar esa misma versión (la última) y probar y me ha salido perfectamente.

(http://img262.imageshack.us/img262/3356/sinnombre1qr8.png)

PD: Y con compilar (ctrl+F9) vale.
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 24 de Junio de 2008, 20:22
Vale, ahora sale todo, pero abres el .exe y la ventana no dura nada prácticamente y no se ve si lo he hecho mal o bien.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 24 de Junio de 2008, 21:03
Cita de: Thylzos en 23 de Junio de 2008, 23:23
CitarSi nos da un fogonazo de MS-DOS al ejecutar el programa, simplemente ve al cmd y ejecutarlo desde allí para ver la salida.

Ahí tienes la solución. Abres la terminal (ejecutar -> cmd), tecleas:

Citarcd Directorio-donde-este-el-programa

Y escribes el nombre que le hayas puesto al programa. Otra alternativa es agregar esto al código:

getchar();

Justo antes del return 0. Pasa eso porque el programa muestra el contenido y cierra. No espera. Pero con esta última línea haces que hasta que presiones una tecla.

Te lo había explicado antes :P.
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 24 de Junio de 2008, 21:17
Vale, mierda, sorry, ya sale.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 24 de Junio de 2008, 21:20
Cita de: Blanquito en 24 de Junio de 2008, 21:17
Vale, mierda, sorry, ya sale.

Entonces, si no tienen más dudas, ¿les parece que meta el siguiente capítulo?
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 24 de Junio de 2008, 21:22
Claro.
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 24 de Junio de 2008, 21:25
Mételo.

PD: ¿Han hecho ejercicio de modificar el código? Ese es una pajada, pero para entender las cosas luego será mejor que los hagan.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 24 de Junio de 2008, 21:31
3 -Variables y constantes

3.1 -Definiciones:
-Variable: Valor utilizado en un programa que puede cambiar durante su ejecución.
-Constante: Valor utilizado en un programa que no puede cambiar durante su ejecución.
-Identificador: Nombre que recibe una variable, una constante o cualquier otro elemento que pueda contener valor para poder trabajar con él.

3.2 -Identificadores
Como vimos con "nombres" que les damos a los valores que vayamos a utilizar en un programa. Para verlo más claro. Pensemos en PI, este número es siempre 3,1416..., entonces es una constante con el identificador "PI". Ahora pasemos a la clásica x, esta puede tomar cualquier valor, con lo que es una variable de identificador 'x'. Espero que lo hayan entendido.

En el lenguaje C no se puede poner cualquier identificador, sólo se pueden usar letras mayúsculas, minúsculas, los números y el guión bajo; no se pueden usar ni la ñ o la Ñ, ni espacios, ni tildes. Además, un identificador no puede empezar nunca por un número ni puede ser igual a cualquiera de las palabras reservadas de C. A la hora de poner identificadores recuerden que C es un lenguaje Case sensitive, es decir, distingue minúsculas y mayúsculas.

Estas son las palabras reservadas:


·auto·double·int·struct
·break·else·long·switch
·case·enum·register·typedef
·char·extern·return·union
·const·float·short·unsigned
·continue·for·signed·void
·default·goto·sizeof·volatile
·do·if·static·while

Ejemplos de identificadores correctos:

Edad
Coche
mi_edad
Top10
PI
Yo
Tu

Ejemplos de identificadores incorrectos:

10Top
código
niño
el contador
auto
#yo
litros-agua

Bien recordemos que al utilizar un lenguaje Case Sensitive, coche y Coche no serán la misma variable, cada vez que programéis tenedlo en cuenta. En cuanto a la longitud, por si alguien se lo estaba preguntando, en C no hay un límite máximo de caracteres (no por eso vayáis a hacer una de 100 palabra).

Bien, pero ¿cómo sabemos qué identificador usar? Lo lógico es darle a la variable o constante (o lo que sea) una que nos permita reconocer qué vamos a almacenar. Es decir, no le pongan "su_edad" de identificador a una constante que tendrá el número áureo, un poco de criterio, tengan en cuenta que muchas veces ese código lo verá también otra persona (especialmente si os dedicáis a esto) y tiene que entenderlo. Por último, es tradición entre los programadores de C ponerle a las constantes identificadores en mayúsculas y a las variables en minúsculas.

3.3 -Tipos de datos
Cuando declaremos en un programa que vamos a usar una variable o constante, debemos especificar qué tipo de valor va a contener ésta (un entero, un carácter, etc.) a continuación veremos los tipos de datos de C.

Citar


TIPO     TAMAÑO     RANGO DE VALORES
char1 bytede -128 a 127
int2 bytesde -32768 a 32767
float4 bytesde 3'4 E-38 a 3'4 E+38
double8 bytesde 1'7 E-308 a 1'7 E+308


3.3.1 -Tipo int
Este tipo indica que usaremos un entero, es decir, un número "sin coma". Podemos escribirlo en decimal (nomenclatura normal), octal (con un 0 delante: 037, 02, 014) o un hexadecimal (anteponiendo 0x: 0x1AB, 0xA23, 0x13).

3.3.2 -Tipos float y double
Estos tipos "tienen coma", es decir, números con parte fraccionaria. Para darles un valor, lo hacemos así: primero podemos poner un signo (+ o -, el primero se obvia si no se pone), segundo los dígitos enteros, luego un punto (.) para separarlo de la parte fraccionaria y, al final, la parte fraccionaria. Además, podemos agregar un exponente poniendo e o E, el signo del exponente y éste mismo. Tengan en cuenta que no puede empezar por un exponente. No se asusten, no es tan difícil como parece, veamos ejemplo: -3.14, 8.01, 4.78e-3, 7.377E4.

3.3.3 -Tipo char
Los valores de tipo char son caracteres escritos entre comillas simples (' y '). Estos pueden ser cualquiera de los que haya en el código ASCII y van desde letras minúsculas y mayúsculas hasta números y signos de puntuación. Como supongo, espero, que sepan, a cada carácter de este código le corresponde un número, así 65 corresponde a 'A' y 'A' se corresponde a 65. Por eso no debemos intentar sumar el carácter '2' al carácter '6', ya que esto no nos dará el carácter '8', porque estos son símbolos y no valores, el valor es su número en el juego de caracteres ASCII.

Hay algunos caracteres que están formados por dos símbolos:

Citar


CÓDIGO ASCII     CARÁCTER     SIGNIFICADO
7\aAlarma (Beep)
8\bRetroceso (BS)
9\tTabulador Horizontal (HT)
10\nNueva Línea (LF)
11\vTabulador Vertical (VT)
12\fNueva Página (FF)
13\rRetorno
34\"Comillas Dobles
39\'Comillas Simples
92\\Barra Invertida



3.3.4 -Tipo void
Void significa sin valor, es decir, nada. Ya sé que puede sonar raro en un principio, pero más adelante lo usaremos seguido.

3.3.5 -Modificadores long y short.
Cuando declaremos un int podemos decirle, antes de apuntar el tipo, que sea "long" o "short", si tenemos un long int, éste nos da más posibles números, si en cambio tenemos un short int, entonces le daremos menos rango de números. Además, podemos agregar un long al tipo double (double float).

3.3.6 -Modificadores signed y unsigned
Con unsigned le damos a los tipos de datos que tengan valores numéricas la cualidad de no aceptar negativos y con signed, que viene por defecto así que no debemos ocuparnos en ponerlo por lo general, lo decimos que vamos a usar valores que pueden ser negativos o positivos.

3.4 -Variables y Constantes

3.4.1 -Declaración de variables
Para usar una variable debemos avisarle al programa que vamos a usarla, es decir, declararla. Para eso debemos escribir su tipo de dato, su identificador y punto y coma ; .Por ejemplo:
int numero_manzanas

Y ahora en una función main:
int main (void){
int numero_manzanas;
char letra;
float recorrido;
}


Si, por ejemplo, queremos declarar tres variables del mismo tipos, en vez de:
int main (void){
int numero_manzanas;
int numero_peras;
int numero_platanos;
}


Podemos hacer:
int main (void){
int numero_manzanas, numero_peras, numero_platanos;
}


Mientras te encargues de escribir código claro, cualquiera de estos métodos da igual.

3.4.2 -Declaración de constantes
Podemos hacerlo o bien con la palabra reservada const o con la directiva #define. Aso sí, recuerda que se suelen escribir los identificadores de las constantes con todas mayúsculas.

3.4.2.1- Const
Para declararlas utilizando const, usaremos un método similar al de las variables. Escribimos const, el tipo de dato, su identificador, un igual y su valor (no olvidarse del punto y coma). Les damos un valor al declararlas ya que éste será siempre el mismo durante todo el código y deben inicializarse con uno, veamos ejemplo:
int main (void){
const float PI = 3,1416;
const int LADOS = 10;
const char PRIMERA_CONSONANTE = 'b';
}


3.4.2.2 -#define
Mediante #define también podemos declarar constantes, a las que llamaremos macros. Para hacerlo, sólo debemos poner: #define, su identificador y su valor. No hace falta usar ni el signo igual, ni declarar el tipo de dato ni poner punto y coma. ¿Pero cuándo hace falta poner punto y coma?, cuando declaremos una sentencia, es simple ¿verdad? Veamos un ejemplo:
#define MENSAJE "Hola"
#define PI 3,1416
#define PRIMERA:VOCAL 'b'

int main (void){
}


Como podemos ver, así podemos incluir cadenas de caracteres (van entre " y "). Tengan en cuenta que así no estamos diciendo al programa: "Guarda memoria para esta constante y ponle su valor", sino que le decimos AL COMPILADOR: "Cada vez que veas este identificador cámbialo por su valor".

3.5 -Inicialización
Cuando declaremos una variable, ésta no tiene un valor inicial. Por eso, si queremos operar con ella, debemos darle uno, sino nuestro programa podría devolver errores. Para evitarlo, es posible darles un valor en el momento en que la declaremos, pero esto no es obligatorio (al contrario que con las constantes). Veamos ejemplo, que es muy simple, sólo se pone = y es valor que le queremos dar:
int main (void){
int numero_manzanas = 3;
char letra = 'g';
float recorrido = 4,76;
}


Ahora ya podemos operar con variables libremente (cuando aprendamos a hacerlo) :].

EJERCICIOS

1- Verdadero o Falso
a) Una constante puede cambiar de valor a lo largo del programa.
b) El tipo int permite sistema octal.
c) Double tiene más precisión de decimales que float.
d) El tipo void es para carateres.
e) Declarando constantes con #define, estás reservando memoria para ellas.
f) Depués de declarar un variable siempre se pone punto y coma.

2- Di qué identificadores son incorrectos.

a) _pulsacion
b) año
c) auto
d) Tuyo
e) YaYo
f) num_camiones
g) #numero
h) mi_edad
i)  19registro
j)  cod soft

EL RETO

No se me ocurría qué poner, así que me decidí porque le hechen una mano a Juanito.

CitarHistoria de Juanito
Juanito está aprendiendo a programar en C. Para eso sigue el curso de un usuario de ogame con graves desequilibrios psicológicos llamado Thylzos. En estos momentos está estancado en el sexto capítulo ("Control de flujo") y ha hecho uno de los ejercicios que propone en el que le pedían que haga un programa. Él lo hizo, pero su compilador le marcó un error y no lo linkeó, lo revisó y no encontró ningún error, así que le preguntó a un amigo suyo que es Ingeniero en Informática, y que le dije que era un problema de edad. Juanito se iluminó y, entendiendo su problema, decidió esperar unos años, hasta ser mayor y poder usar el programa. Pero pasaron cuatro años y saguía sin poder usar el progrma. Pero esta vez algo cambió, encontró un error, lo solucionó y compiló (¡Por fin!), sin embargo el programa no hacía lo que él quería. Finalmente, desesperado, recurrió a mí.

Y yo recurro a vosotros. Miren, acá les dejo el código fuente del programa después de que Juanito corrija el error:
#include <stdio.h>
#define MAYORIA_EDAD 18;

int main (void){
int Edad_Juanito, Edad_Novia_Juanito;
char opc;
printf ("¿Cuántos años tiene Juanito?");
scanf ("%d", &Edad_Juanito);
printf ("¿Y su novia?");
scanf ("%d", &Edad_Novia_Juanito);

if (Edad_Juanito >= MAYORIA_EDAD){
printf("Juanito es mayor de edad");}

While ("mundo" == "mundo"){
printf ("¿Ha pasado un año? (S/N)");
scanf ("%c", &opc);

if (opc == 'S'){
int edad_Juanito ++;
Edad_Novia_Juanito ++;
}
}
}


Esto es lo que deben hacer:

a) Explica cómo funciona y qué hace (no hace falta que expliquen sentencia por sentencia, hay muchas cosas que no vimos).
b) Encuentra un error que no vio Juanito.
c) Encuentra el error que sí vio Juanito, explica en qué se equivocó en la correción (poruqe se equivocó) y cómo solucionarías tú el error.
Título: Re: Curso de iniciación en programación C
Publicado por: Ray en 24 de Junio de 2008, 23:06
Vale, resulta que tenía el programa mal instalado, ahora con el nuevo ya tarda un poco más en abrir, se abrió una ventana de configuración y aparece otra mientras se compila... Hasta ahora todo bien.

Sorry but you are not allowed to view spoiler contents.


Sorry but you are not allowed to view spoiler contents.

Qué tal? :wiiiiii:

Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 24 de Junio de 2008, 23:19
Compruébalo tú mismo:

Citar1- Verdadero o Falso

a) Una constante puede cambiar de valor a lo largo del programa. F, esas son las variables.
b) El tipo int permite sistema octal. V, y en hexadecimal y en decimal...
c) Double tiene más precisión de decimales que float. V, podemos comprobarlo en la tabla.
d) El tipo void es para carateres. F, es tipo nulo, para caracteres está char.
e) Declarando constantes con #define, estás reservando memoria para ellas.  F, eso es con la palabra const.
f) Depués de declarar un variable siempre se pone punto y coma. V, es una sentencia así que no hay más que hablar.

Citar2- Di qué identificadores son incorrectos.

a) _pulsacion Correcto
b) año Incorrecto, no puede llevar ñ.
c) auto Incorrecto, es una palabra reservada
d) Tuyo Correcto
e) YaYo Correcto
f) num_camiones Correcto
g) #numero Incorrecto, el signo # no se permite
h) mi_edad Correcto
i) 19registro Incorrecto, no se puede comenzar con números.
j) cod soft Incorrecto, no se permiten espacios.

Ahora pasemos a lo interesante, El reto. Este tipo de ejercicios son de los que más me gustan, sobre todo porque te hacen pensar y razonar un poco cómo penso y razonó otro, algo muy importante cuando querramos entender el código de otra persona. Veamos el programa de Juanito:

#include <stdio.h>
#define MAYORIA_EDAD 18;

int main (void){
int Edad_Juanito, Edad_Novia_Juanito;
char opc;
printf ("¿Cuántos años tiene Juanito?");
scanf ("%d", &Edad_Juanito);
printf ("¿Y su novia?");
scanf ("%d", &Edad_Novia_Juanito);

if (Edad_Juanito >= MAYORIA_EDAD){
printf("Juanito es mayor de edad");}

While ("mundo" == "mundo"){
printf ("¿Ha pasado un año? (S/N)");
scanf ("%c", &opc);

if (opc == 'S'){
int edad_Juanito ++;
Edad_Novia_Juanito ++;
}
}
}


Qué hace el programa es fácil de saber, pide la edad de Juanito y la de su novia, te dice si Juanito es mayor de edad y después te va preguntando si pasó un año para saber si estas edad aumentan.

Errores que no vio Juanito, el primero y más fácil,

Citar#define MAYORIA_EDAD 18;

Termina en punto y coma, no creo que haya más que comentar. Pero hay uno más que no tendrían por qué saberlo, y es que se crea un bucle infinito, si cada vez que nos pregunta si paso un año decimos que sí, podemos seguir y Juanito llegará a los 380 años, esto es un error lógico en la forma de plantear el programa, hay que evitar los bucles infitos, todo programa tiene un principio y un final.

Por último, qué error corrigió Juanito. eso se puede deducir con una linea muy llamativa:

Citarint edad_Juanito ++;

Si la analisamos, nos encontramos con que inicializa una nueva variable (¿para qué?) y le suma uno. Si vemos un poco más su identificador, notaremos que es casi igual que esta variable ya inicializada:

Citarint Edad_Juanito,

Sólo se diferencia en una mayúscula, así que podemos suponer que en un principio había puesto únicamente:

Citaredad_Juanito ++;

Lo había intentado compilar y su compilador le dijo que había una variable no inicializada, el la encontró y en vez de ponerle el nombre de la anterior, declaró una nueva, "engañando" así a su compilador , pero generando un error lógico en el programa.

Todo esto puede parecer muy difícil de deducir, pero no lo es. El único truco para resolver este tipo de problemas (y ya de paso encontrar errores nuestros programas y los de otros) es compilar y probar con todas las posibles aplicaciones del programa, modifcar el código, compilar de vuelta, y así hasta encontrar todos los errores, no hay que ser tan vagos como Juanito ;).
Título: Re: Curso de iniciación en programación C
Publicado por: Ray en 24 de Junio de 2008, 23:24
Mi solución no era tan... precisa :lol:
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 24 de Junio de 2008, 23:28
3.6- Ejercicios

1- Verdadero o Falso

a) Una constante puede cambiar de valor a lo largo del programa.
b) El tipo int permite sistema octal.
c) Double tiene más precisión de decimales que float.
d) El tipo void es para carateres.
e) Declarando constantes con #define, estás reservando memoria para ellas.
f) Depués de declarar un variable siempre se pone punto y coma.

2- Di qué identificadores son incorrectos.

a) _pulsacion
b) año
c) auto
d) Tuyo
e) YaYo
f) num_camiones
g) #numero
h) mi_edad
i)  19registro
j)  cod soft


Citar#include <stdio.h>
#define MAYORIA_EDAD 18; ¿No es sin punto y coma?

int main (void){
int Edad_Juanito, Edad_Novia_Juanito;
char opc;
printf ("¿Cuántos años tiene Juanito?");
scanf ("%d", &Edad_Juanito);
printf ("¿Y su novia?");
scanf ("%d", &Edad_Novia_Juanito);

if (Edad_Juanito >= MAYORIA_EDAD){
printf("Juanito es mayor de edad");}

While ("mundo" == "mundo"){
printf ("¿Ha pasado un año? (S/N)");
scanf ("%c", &opc);

if (opc == 'S'){
int edad_Juanito ++;
Edad_Novia_Juanito ++; ¿Sería int Edad_Novia_Juanito ++? O despues de int edad_Juanito ++ con coma, ¿no?
}
}
}

Lo de para qué funciona y tal ni idea, chacho.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 24 de Junio de 2008, 23:29
Blanquito estuvo mucho más cerca :P. Tienes las soluciones en la página anterior.
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 24 de Junio de 2008, 23:31
Soy el primero de la clase ray. :lol:
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 24 de Junio de 2008, 23:34
Venga, que no tengo nada que hacer y Sandman quiere que posteemos:

4- Operadores

4.1 Definiciones

-Operador: Elemento utilizado en un lenguaje de programación a fin de realizar una operación con variables.

-   Operador aritmético: Operador utilizado para realizar tratamientos matemáticos a dos variables (suma, resta, etc.).

-   Operador de asignación: Operador utilizado para dar el resultado de una expresión a una variable.

-   Operador de asignación compuesto: Operador de asignación que nos simplifica la programación abreviando expresiones.

-   Operador de incremento y de decremento: Operadores encargados de aumentar o disminuir, respectivamente, en uno una variable.

-   Operador de bit: Realizan una operación sobre cada uno de los bits de un int o un char.

-   Operador relacional: Operador encargado de evaluar la relación de dos valores.

-   Operador lógico: Operador encargado de evaluar lógicamente uno o dos valores.


4.2 – Operadores aritméticos

En esta familia nos encontramos con cinco posibilidades:

    +      Suma           
    -      Resta, negación
    *      Multiplicación 
    /      División       
    %      Resto de la div.

No creo que haga falta que explique los cuatro primeros (sería preocupante). En cuanto al quinto, nos da como resultado el resto de la división entre dos números, así la expresión "22%3" daría 1.

Veamos ejemplos:

int main (void){

int v, w, x, y, z ;

const int K=10;

v = K+1; /* Suma la constante K y 1, para almacenarlo en v*/
w = v-K; /* Resta v y K y almacena el resultado en w*/
x = w*v; /* Multiplica w y v y lo almacena en x*/
y = w/x;  /* divide w entre x y lo almacena en y*/
z = y%x; /* Dvide y entre x y el resto lo almacena en z*/
}[code]

Además podemos utilizar + y – como signos negativo y positivo. Veamos un ejemplo:

[code]int main (void) {

int v, w ;
const int K=10;
x = -K+2;
y = -x*3;
}


Amable lector, corre, llama a tu madre y anúnciale que acabas de aprender a sumar y restar.

4.3 – Operadores de asignación

Operadores de asignación simple, únicamente tenemos el signo igual ( = ). Y con éste solo ya haremos maravillas. Como ya dije, sirve para darle a una variable el resultado de una expresión. Su sintaxis es:

Variable = expresión;

Primero se pone el identificador de la variable que almacenará el resultado, un signo igual y la expresión que puede o no contener otra o esa variable. Veamos un ejemplo:

int main (void) {
int x, y ;
x = 0; /* x vale 0*/
x = 2; /* Ahora 2 */
x = x + 2; /* Ahora es 4 */
y = x * 2; /* y es igual a 8 */
y = y + 1; /* y es igual a 9 */
}


Pero esto no es todo. Además podemos usar caracteres. Recordemos que un carácter es un símbolo con un valor numérico en un juego de caracteres como el ASCII. Veamos entonces un ejemplo:

int main (void) {
char letra;
letra = 'B' ; /* 'B' es el carácter número 66 en el código ASCII*/
letra = letra – 1; /* Ahora disminuímos uno, es decir, letra vale 65 que es 'A'*/
letra = letra – 1; /* Volvemos a disminuir uno, valiendo letra 64 que no me acuerdo que carácter era en el ASCII */
}


Y hay más, podemos utilizar varios = en una misma línea, como en éste caso:

int main (void) {
int a, b, c, d ;
a = b = c = d  = 0;
}


Ahora tanto a, como b, como c, como d valen 0 :].

4.4 – Operadores de asignación compuestos

En muchas ocasiones usaremos expresiones del estilo de:

Variable_1 = Variable_1 + 3;
Variable_2 = Variable_2 – 3;

Donde la sintaxis:

Variable = Variable operador expresión;

La variable situada a  ambos lados es la misma. Pues bien, C nos da la oportunidad de simplificar esto. Para hacerlo usaremos esta sintaxis:

variable operador = expresión;

Como siempre, veamos ejemplos:

int main (void) {
int a, b;
a += b; /* Es igual a: a = a + b; */
a *= b; /* Es igual a: a = a * b; */
a >>=  b; /* Es igual a: a = a>>b: */
a &= b; /* Es igual a: a = a&b; */
}


4.5 - Operadores de incremento y decremento.

Cuando lo único que quieres hacer es sumar o restar 1 a una variable, escribimos:

variable = variable + 1;
variable = variable - 1;

o:

variable += 1;
variable -= 1;

Es un poco engorroso para los vagos como yo, para eso tenemos:

++ variable;
variable ++;
-- variable;
variable --;

Y es tan fácil como parece. Sólo con eso ya habremos incrementado o decrementado en uno una variable. Pero, veamos con atención este ejemplo:

int main (void) {
int a ;
int x = 10 ;
a = x++ ;
a = ++x ;
a = x--;
a = --x;
}


En la primera línea primero le damos a 'a' el valor de 'x' (a  = 10) y después aumentamos 'x' en uno (a = 11). En la segunda, primero aumentamos 'x' (x = 12) y después guardamos en 'a' (a = 12). En la tercera, primero guardamos (a = 12), luego disminuimos (b = 11). En la cuarta, primero disminuímos (b = 10) y después guardamos (a = 10).

Como podemos ver, en x++ primero se utiliza el valor de la variable (se guarda en a) y después se incrementa. Y en ++x es justo al revés.

4.6 – Operadores de bits.

Como ya sabemos, estos operadores funcionan sólo con char e int. Y como también sabemos lo hacen a nivel de bits. Estos son:

-   AND, representado con &.
-   OR, representado con |.
-   XOR, representado con ^.
-   Complemento a uno, representado con ~.
-   Desplazamiento hacia la izquierda, representado con <<.
-   Desplazamiento hacia la derecha, representado con >>.

Voy a dar por supuesto que saben binario, pero si alguien no lo conoce, que consulte este link: http://www.templobinario.com/index.php?mdl=la&ida=31# (http://www.templobinario.com/index.php?mdl=la&ida=31#).

Con AND comparamos bit a bit dos variable con resultados dependientes de la siguiente tabla:

Bit 1    /    Bit 2    / Resultado

0       /       0        /      0
0       /      1        /      0
1       /       0        /      0
1       /       1        /      1
Así, 72 (1001000) AND 29 (0011101) es igual a 16 (0001000).

Con OR es lo mismo pero con esta tabla:

Bit 1    /    Bit 2    / Resultado

0       /       0        /      0
0       /      1        /       1
1       /       0        /      1
1       /       1        /      1

Por ejemplo, 72 (1001000) OR 29 (0011101) es igual a 93 (1011101).

Con XOR, según su correspondiente tabla:

Bit 1    /    Bit 2    / Resultado

0       /       0        /      0
0       /      1        /       1
1       /       0        /      1
1       /       1        /      0

Otro ejemplo, 72 (1001000) XOR 29 (0011101) es igual a 85 (1010101).

El de complemento a uno (o NOT) invierte los bits de una variable.

Así ~72 (1001000) es igual a 55 (0110111).
Si a alguien le interesa profundizar en el tratamiento de bits y el álgebra booleana, visite: http://www.monografias.com/trabajos14/algebra-booleana/algebra-booleana.shtml (http://www.monografias.com/trabajos14/algebra-booleana/algebra-booleana.shtml)

Ahora vamos a los desplazamientos. Desplazamiento a la izquierda (<<) desplaza los bits hacia la izquierda tantas posiciones como se indique tras el operador, colocando el bit del final en cero, y el bit del principio eliminándolo. Ejemplo:

int main (void) {
int izquierda = 1 ;
izquierda = izquierda >> 1;
}


La variable izquierda vale 1 (00000001). Al desplazar a la izquierda, el primero se desecha (0000001) y ponemos un cero al final (00000010), ahora izquierda vale 2.

El desplazamiento derecha desplaza los bits hacia la derecha tantas posiciones como se indique al final del operador, transformando el bit del principio en 0 y el del final se desecha.

Segundo ejemplo:

int main (void) {
int derecha = 1 ;
derecha = derecha >> 1;
}


Ahora desechamos el  bit del final, pasando de 00000001 a 0000000, y luego ponemos un cero al final 00000000, valiendo ahora derecha 0.

4.7 – Operadores relacionales

Este tipo de operaciones se utilizan para evaluar una relación entre dos expresiones dando como resultado el valor verdadero o el falso. Estos son los operadores relacionales:

Acción  / Operador
Mayor que /  >
Mayor o igual que / >=
Menor que / <
Menor o igual que / <=
Igual / =
Distinto /  !=

Veamos ejemplos:
m == n -> ¿es m igual que n? -> Falso
m != n -> ¿es m distinto que n? -> Verdadero
m > n -> ¿es m mayor que n? -> Falso
m < n -> ¿es m menor que n? -> Verdadero
m >= n -> ¿es m mayor o igual que n? -> Falso
m <= n -> ¿es m menor o igual que n? -> Verdadero

4.8- Operadores lógicos

Bien estos evaluan de forma lógica, puede ser de uno o de dos valor, el NOT lo hace con uno y el AND y el OR con dos.

Operador / Acción
&& / Conjunción (Y)
|| / Disyunción (O)
! / Negación

El resultado de estas operaciones obedece las tablas que vimos antes, la de AND con la tabla de AND y la de OR con la de OR, y se interpreta así: 1 es verdadero y 0 falso. En el caso de NOT, si es verdadero es falso y si falso es verdadero. Tengan en cuenta que 0 siempre es falso y el resto de los números verdadero. Para entenderlo mejor, veamos ejemplos:

27 && -15 -> verdadero AND verdadero -> verdadero
0&&0 -> falso AND falso -> Falso
1&&0,0001 -> Verdadero AND verdadero -> verdadero
-2 || 100 -> Verdadero OR verdadero -> verdadero
0||0 -> falso OR falso -> Falso
! 0 -> Negación de falso -> verdadero
! 2 -> negación de verdadero -> Falso

Podemos combinar operadores relacionales con los lógicos, por ejemplo:

(7>3) && (12<=30)

La expresión se resuelve empezando por los paréntesis. ¿7 es mayor que 3? Sí, así que el primer resultado es verdadero. ¿12 es menor o igual que 30? Sí, entonces el resultado del segundo paréntesis es verdadero. Para terminar se calcularía la expresión del resultado obtenido en cada paréntesis con un AND:

Verdadero && Verdadero

Lo que nos de verdadero como resultado de la expresión.

4.9 - Operador de conversión explícita (cast) [Cortesía de Hartigan]

Se utiliza el operador cast, el cual proviene de la terminología inglesa casting. Realiza una conversión explícita de tipos, es decir, consiste en "obligar" a una variable o expresión a ser temporalmente de un determinado tipo mientras se evalúa en una expresión.
Ésto consiste en dar un nombre de "tipo" encerrado entre paréntesis que precederá a la variable o expresión la cual queramos convertir.
Hay que tener cuidado ya que el operador cast actúa sobre la variable más próxima.

Veámos todo esto con una serie de ejemplos:

Supongamos que tenemos la siguiente declaración:

Citarfloat f = 11;

La expresión f % 4 NO es válida porque el operando f es real en vez de entero. Sin embargo la expresión (int) f % 4 hace que el operando se tranforme temporalmente en entero y , por tanto, la expresión ya es válida, y se obtiene como resultado el valor 3 como entero.

Supongamos ahora la expresión siguiente:

Citarf = (9/5)*c +32;

Esa expresión proporcionará un valor incorrecto y no esperado, pues 9/5, al ser los dos operandos enteros, dará como resultado el valor 1 y no el 1.8 deseado.
Para solucionar esto podemos usar una de los siguientes métodos:

Citarf = (9.0 / 5) * c + 32;
f = (9 / 5.0) * c + 32;
f = ((float)9 / 5) * c + 32;

4.10 – Orden de operación de los operadores.

Como debemos de saber, los paréntesis son lo primero que se opera. Pero luego hay que tener en cuenta este orden (ascendente a descendente):

Operador

++       /         Incremento
--         /         Decremento
-          /         Signo negativo
*         /          Multiplicación
/          /          División
%        /          Módulo o resto
+         /          Suma
-          /          Resta

4.11 – Ejercicios

1-   Di si la expresión es verdadera o falsa:

! ((7>=7) || (33>33))
(8<9) && ¡(54>=78 )
! (8!=9) || !(86>=96)

2-   ¿Cuál es el valor de la variable después de estas operaciones?

int main (void){
int x = 3;
x++ ;
x++ ;
x-- ;
x += 3 ;
x -=1 ;
x *= 5;
}


El reto

¿Serías capaz de hacer un programa que incialice una variable, le dé un valor y opere con él con todos los tipos de operadores hasta el final desembarcar en el mismo del principio sin usar operaciones opuestas?, es decir, sin hacer como Juanito, que puso:

int main (void) {
int x = 1 ;
x++ ;
x-- ;
x += 2 ;
x -= 2 ;
x *= 4 ;
x /= 4 ;
}
[/code][/code]
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 25 de Junio de 2008, 12:23
Buah, para hacer éste voy a tardar unos días porque paso de meterme más cosas en el cerebro que tengo un examen de matemáticas anual y me tengo que aprender muchos teoremas.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 25 de Junio de 2008, 12:28
Cita de: Blanquito en 25 de Junio de 2008, 12:23
Buah, para hacer éste voy a tardar unos días porque paso de meterme más cosas en el cerebro que tengo un examen de matemáticas anual y me tengo que aprender muchos teoremas.

¿Todavía tienes clases :hehe:?, yo llevo más de una semana libre...
Título: Re: Curso de iniciación en programación C
Publicado por: Blanquito en 25 de Junio de 2008, 12:37
En la academia técnica superior. Tengo que ir, ya que apoquino. Y me queda el examen de matemáticas final, el sábado, que soy capaz de aprobar con una matrícula si me aprendo teoremas y demás.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 25 de Junio de 2008, 12:38
Cita de: Blanquito en 25 de Junio de 2008, 12:37
En la academia técnica superior. Tengo que ir, ya que apoquino. Y me queda el examen de matemáticas final, el sábado, que soy capaz de aprobar con una matrícula si me aprendo teoremas y demás.

Te deseo matrícula en ese caso.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 28 de Junio de 2008, 23:03
Soluciones del 4º capítulo

Citar1- Di si la expresión es verdadera o falsa:

! ((7>=7) || (33>33)) -> Falso
(8<9) && ¡(54>=78 ) -> Verdadero
! (8!=9) || !(86>=96) -> Verdadero

!((7>=7) || (33>33))

7 es mayor o igual que 7, así que en la primera parte, Verdadero. 33 no es mayor que 33, la segunda parte es Falsa. Verdadero OR Falso = Verdadero. !Verdadero (Verdadero negado) =  Falso, la solución es Falso.

(8<9) && ¡(54>=78 )

8 es menor que 9, con lo que lo primero es Verdadero. 54 no es mayor o igual que 78, así que nos da Falso, pero como a su vez está negado, nos da Verdadero (!Falso = Verdadero). Por último, Verdadero AND Verdadero = Verdadero. El resultado es Verdadero.

!(8!=9) || !(86>=96)

8 es distinto de 9, Verdadero. Pero a su vez está negado y !Verdadero = Falso. 86 no es mayor o igual que 96, resultao Falso que a su vez también está negado y !Falso = Verdadero. Para finalizar, Falso OR Verdadero = Verdadero. Resultado Verdadero.

2- ¿Cuál es el valor de la variable después de estas operaciones?

int main (void){
int x = 3;
x++ ;
x++ ;
x-- ;
x += 3 ;
x -=1 ;
x *= 5;
}


Primero, x =3.
x++ -> x=4
x++ -> x= 5
x-- -> x=4
x += 3 -> x = x + 3 -> x= 7
x -= 1 -> x = x - 1 -> x=6
x *= 5 -> x = x * 5 -> x= 30
Solución: x = 30

En cuanto al reto, hay muchas soluciones, y para no analizar una por una todas, les recomiendo a todos los que lo hayan hecho que para comprobar si estan bien agreguen esto al final a su programa:

#include <stdio.h> /* La biblioteca que vamos a utilizar */

int main (void) {

...  /* La declaración de la variable y los operadores que usó cada uno*/
printf ("Variable al final de las operaciones: %d", nombres de la variable que usaron); /* Para que nos muestre cuánto vale al final del programa */
getchar (); /* Para que nos de tiempo a verlo*/
}


Si el dato que nos muestra al final es el mismo que el que le dimos a la variable al principio, entonces el ejercicio está bien, en caso contrario revisen todo lo que hicieron.

Hasta mañana.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 01 de Julio de 2008, 00:35
5- Entrada/Salida Estándar

5.1- Definiciones

- Entrada de datos: Acción en la que el usuario ingresa datos desde un dispositivo de entrada del ordenador hasta el programa que se esté ejecutando en ese momento. Cuando hablamos de entrada estándar nos referimos a que es por medio del teclado.

- Salida de datos: Acción en la que el ordenador muestra al usuario, a traves de un dispositivo de salida, información obtenida por el programa que se está ejecutando en ese momento. Cuando hablamos de salida estándar nos estamos refiriendo a que es por medio de la pantalla.

5.2- Salida de datos

5.2.1- Salida de caracteres

Para poder mostrar un carácter usaremos la función putchar(); que tiene la siguiente cabecera:

int putchar (int caracter);

No se preocupen si no lo entienden del todo, con el paso de los capítulos quedará más que claro. Por ahora basta con saber cómo interpretarla. Y para eso debemos saber que el parámetro "caracter" es el código del carácter que queremos imprimir en pantalla. Es increíble lo difícil que suena una explicación formal del asunto, pero con este ejemplo veremos lo simple que es:

include <stdio.h> /*ponemos la biblioteca de la función "putchar" para poder usarla*/

int main (void) {
putchar ('A');
getchar ();
}


No creo que sea muy difícil entender qué hace, pero aún así lo explico. Simplemente imprime la letra 'A' y espera que se pulse Enter, así de simple.

Ahora probemos otra alternativa al anterior algoritmo:

include <stdio.h> /*ponemos la biblioteca de la función "putchar" para poder usarla*/

int main (void) {
char letra = 'A';
putchar (letra);
getchar();
}


Y nos da como resultado lo mismo que el anterior. ¿Qué conclusión sacamos de esto?, que también se pueden pasar como caracteres variables del programa, lo que es más útil de lo que parece a simple vista.

5.2.2- Salida de cadenas de caracteres

No sabía si poner o no este apartado, pero como sé que no tiene nada de divertido mostrar un sólo carácter en pantalla, decidí explicar algo aunque sea de la salida de cadenas.

Para eso, nos valeremos de la función puts(); de stdio.h, cuya cabecera, si no recuerdo mal (corríjanme en caso de error, me da pereza buscarla), es:

int puts (char cadena []);

Y utilizarla es igual que con putchar, sólo que como todavía no sabemos cómo guardar cadenas en variables, esta utilidad por ahora no la usaremos. Como de costumbre, veamos un ejemplo:

include <stdio.h> /*ponemos la biblioteca de la función "puts" para poder usarla*/

int main (void) {
puts ("Esto es una prueba");
getchar();
}


Y veremos impreso en pantalla el texto "Esto es una prueba".

5.3- Entrada de caracteres

Antes de nada, vamos a ver un par de conceptos:

- Esperar Intro: Después de introducir un dato a un programa, si la función utilizada posee esta característica, espera a que se pulse Enter para continuar.

- Con eco: Cuando introducimos un dato, si la función utilizada posee esta característica, vemos el dato introducido.

5.3.1- getchar

La función de entrada de caracteres getchar(); es con eco y espera Intro. Pertenece a la biblioteca stdio.h y su cabecera es:

int getchar (void);

Lo que retorna (ese int de ahí :P) es el código del carácter escrito, veamos un ejemplo ilustrativo:

#include <stdio.h>

int main (void) {

char letra
letra= getchar ();
putchar (letra);
getchar();
}


Me planteé poner imágenes, pero quedan muy chicas y es lo mismo que nada, así que explicaré qué sucede. Bien, como getchar() tiene eco, cuando pulsamos la tecla del carácter que queremos ingresar, obtenemos que esta se reproduce en pantalla, después tendremos que pulsar Intro para seguir y nos volverá a mostrar la letra (putchar (letra)), para pasar a esperar a que se pulse Intro. en conclusión, nos muestra dos veces (una por getchar y otra por putchar) el carácter entrado.

5.3.2- getch

En este caso, nos encontramos con una función que ni tiene eco ni espera Intro, una auténtica negada (vale, vale, dejo los chistes malos). Su cabecera es:

int getch (void);

La cual, como en el caso anterior, devuelve el código del carácter. Como en el caso anterior, veremos un ejemplo:

#include <stdio.h>
#include <conio.h> /*Biblioteca de getch()*/

int main (void) {

char letra
letra= getch ();
putchar (letra);
getch();
}


Al ejecutarlo e ingresar el dato, sólo nos muestra éste una vez en pantalla (por putchar(letra);), debido a que getch() no tiene eco.

Una de las utilidades más interesantes de esta función es la siguiente:

printf ("Pulse una tecla para continuar.");
getch ();


5.3.3- getche

En este caso, nos encontramos con una alternativo que tiene eco, pero que no espera Intro, y cuya cabecera es:

int getche (void);

Como siempre, nos retorna el código del carácter en cuestión, vamos al ejemplo:

#include <stdio.h>
#include <conio.h> /*Biblioteca de la función getche*/

int main (void) {

char letra
letra= getche ();
putchar (letra);
getchar();
}


Como podremos comprobar, en este caso nos imprime dos veces el carácter que ingresamos, pero no espera a que pulsemos Intro una vez pusimos a éste.

5.4- Entrada/Salida formateada

Todo esto muy lindo, pero ¿y si quiero mostrar el valor numérico de una variable? o ¿y si quiero que el usuario ingrese un número no un símbolo de un número (carácter)? o: y si no me gusta el sistema decimal y me manejo con octal, ¿cómo hago para mostrar datos así?. Para todas estas respuestas, existen las Entradas/Salidas formateadas, es decir, que nos permiten distintos formatos para mostrar el dato en cuestión.

5.4.1- Salida formateada (printf)

No voy a poner la cebecera de esta función porque sería marear mucho a los lectores y no es para nada ése el fin. Pero sí voy a explicar cómo funciona:

printf ("cadena_con_formato", [lista_de_argumentos]);

Para entenderlo vamos a poner ejemplos de cada parte (cómo me gustan los ejemplos ¿eh?), primero de cadena_con_formato:

#include <stdio.h>

int main (void) {
printf ("Estoy probando la función printf");
getchar():
}


El resultado es más que previsible. Sin embargo, a esto podemos agregarle caracteres "especiales", que son:

CitarEscape  /   Hexad /    ANSI /    Nombre o resultado
    /   0x00 /   NULL /   Carácter nulo
\a /   0x07 /   BELL /   Sonido de campanilla
\b /   0x08 / BS / Retroceso
\f /   0x0C /   FF /   Avance de página
\n /   0x0A /   LF /   Avance de línea
\r /   0x0D / CR /   Retorno de línea
\t /   0x09 /   HT /   Tabulador horizontal
\v  /   0x0B /   VT /   Tabulador vertical
\\  /   0x5c /   \ /   Barra descendente
\'  /   0x27    '    Comilla sencilla
\"  /   0x22    "    Comillas
\? /   0x3F    ?    Interrogación
\O /        cualquiera /   O=tres dígitos en octal
\xH    /     cualquiera /   H=número hexadecimal
\XH     /    cualquiera /   H=número hexadecimal

Por ejemplo:

#include <stdio.h>

int main (void) {
printf ("Estoy probando la función printf\n\testetexto sale tabulado/"no se entiende lo que pongo.../"");
getchar():
}


Además, y ahora viene lo interesante, podemos poner determinados comandos, los cuales nos permiten poner, por ejemplo, mostrar en pantalla el valor de una variable en binario, para lo que necesitaremos la lista_de_argumentos. Veamos el siguiente programa:

#include <stdio.h>

int main (void) {
int x = 10;
printf ("Estoy probando la función printf, x vale %d", x);
getchar():
}


En pantalla no nos va a mostrar "Estoy probando la función printf, x vale: %d", sino que: "Estoy probando la función printf, x vale: 10". ¿Por qué?, porque cuando pusimos "%d" lo que estabamos diciendo es que queremos mostrar en pantalla el valor en decimal y entero de la primera variable de la lista de argumentos.

Los comando para precisar el formato que vammos a utilizar, se escriben así:

%[anchura]. [precisión] caracter_de_tipo

Donde anchura es el número opcional mínimo de caracteres que tendrá el número a mostrar en pantalla, precisión el número opcional mínimo de decimales tras la coma y caracter_de_tipo el carácter que representa el formato en el cual queremos imprimir el número, estos caracteres son:

CitarCarácter de tipo  /    Entrada esperada /    Formato de salida
Números         
d /   Entero con signo /   Entero decimal
i /   Entero con signo /   Entero decimal
o /   Entero con signo /   Entero octal
u /   Entero sin signo /   Entero decimal
x /   Entero sin signo /   Entero hexadecimal (con a, b, c, d, e, f)
X /   Entero sin signo/    Entero hexadecimal (con A, B, C, D, E, F)
f /   Coma flotante /   Valor con signo: [-]dddd.dddd
e /   Coma flotante /   Valor con signo: [-]d.dddd...e[+/-]ddd
g /   Coma flotante /   Valor con signo, dependiendo del valor de la precisión. Se rellenará con ceros y se añadirá el punto decimal si es necesario.
E /   Coma flotante /   Valor con signo: [-]d.dddd...E[+/-]ddd
G /   Coma flotante /   Como en g, pero se usa E para los exponentes.
Caracteres         
c /   Carácter /   Un carácter.
s /   Puntero a cadena /   Caracteres hasta que se encuentre un nulo o se alcance la precisión especificada.
Especial         
% /   Nada /   El carácter '%'
Punteros         
n /   Puntero a int /   Almacena la cuenta de los caracteres escritos.
p /   Puntero /   Imprime el argumento de entrada en formato de puntero: XXXX:YYYY ó YYYY

Veamos ejemplos:

#inlcude <stdio.h>

int main (void) {
int entero = 10;
float real = 2.4313;
char caracter = 'F';

/*Vames el valor de "entero*/
printf ("%d", entero);

/*El de "float"*/
printf ("\n %f", float);

/*El de "caracter"*/
printf ("\n %c", caracter);

/*Verlo todo junto...*/
printf ("\n %d, %f, %c", entero, float, caracter);

/*El cuadrado de entero*/
printf ("\n %d", entero*entero);

/*Entero en octal*/
printf ("\n %o", entero);

/*El carçacter que representa entero en el código ASCII*/
printf ("\n %c", entero);

getchar();
}


Como pueden ver, podemos mostrar un mismo número en distintos formatos de una forma muy simple.

5.4.2- Entrada de datos formateada (scanf)

La función scanf nos permite almacenar datos de un formato especificado en una variable, lo usual es usarla de este modo:

scanf ("comando", &variable);

Camando ya sabemos lo que es (%d, %C, %o...), y variable es el identificador de la variable que contendrá los datos. Le agregamos al principio &, porque lo que usamos con esta función no es la variable en sí, sino que su dirección de memoria, pero esto lo veremos mejor con los punteros.

Bien, y esto es tna fácil como lo muestra este claro ejemplo:

#include <stdio.h>

int main (void) {
int numero;
char letra;

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

printf ("Escriba una letra: ");
scanf ("%c", &letra);

printf ("Este es el número %d, y esta la letra %c", numero, letra);
getchar();
}


¿Vieron qué fácil?

5.5 Ejercicios

1- Has un programa que le pida dos números al usuario, y le muestre su suma, su división y el resto de la división.

2- Has un programa que le pida una letra al usuario y le diga a éste entre qué otras dos se encuentra.

El reto

Has un programa que le pida un número al usuario, para luego mostrarlo en octal, hexadecimal, su representación en el códgio ASCII, su cuadrado y su doble.

Hasta la próxima, me voy que ya me duele la espalda, de tanto estar sentado.
Título: Re: Curso de iniciación en programación C
Publicado por: Ray en 07 de Julio de 2008, 12:20
Voy a tardar un poco en hacer eses que no tengo tiempo ni ahora ni en las proximas semanas. :\
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 13 de Julio de 2008, 10:03
Soluciones del 5º capítulo

Citar1- Has un programa que le pida dos números al usuario, y le muestre su suma, su división y el resto de la división.

Aquí hay un posible código (tampoco puede variar mucho, eso depende de cada uno):

#include <stdio.h>

int main (void) {
int 1_numero, 2_numero;
printf ("Ponga dos números: ");
scanf ("%d", &1_numero);
scanf ("%d", &2_numero);
printf ("\nSu suma: %d \nSu división: %d\nY su resto: %d", 1_numero+2_numero, 1_numero/2_numero, 1_numero%2_numero);
}


Citar2- Has un programa que le pida una letra al usuario y le diga a éste entre qué otras dos se encuentra.

Ahora voy a hacerlo almacenando el valor de las operaciones en otras variables, en vez de printear directamente el resultado de una operación que está en la lista de parámetros.

#include <stdio.h>

int main (void) {
char letra, anterior, posterior;
printf ("Escriba una letra: ")
scanf ("%c", &letra);
anterior =letra +1;
posterior = letra -1;
printf ("Se encuentra entre %c y %c", anterior, posterior);
}


CitarEl reto

Has un programa que le pida un número al usuario, para luego mostrarlo en octal, hexadecimal, su representación en el códgio ASCII, su cuadrado y su doble.

#include <stdio.h>

int main (void) {
int numero;
printf ("Escriba un número: ");
scanf ("%d", &numero);
printf ("Su representación en octal: %o\nSu representación en hexadecimal: %x\nSu carácter en el código ASCII: %c\nSu cuadrado: %d\nSu doble: %d", numero, numero, numero, numero*numero, numero*2);
}
Título: Re: Curso de iniciación en programación C
Publicado por: NightWalker|Kodama en 31 de Julio de 2008, 17:20
joder, mola tela, yo he dao C, llegue hasta los vectores, pero..... eso si no se practica se olvida, y yo no me acuerdo de casi nada printf("juas juas juas") XDDDD, vamos ni pajo, asi que este tuto me viene que ni al pelo, encima este año hago el FP de programacion pura y dura, asi que en cuanto lo tengais en pdf ya sabeis, a mi me vais a alegrar ^^

Saludos y gracias por el trabajaso.
Título: Re: Curso de iniciación en programación C
Publicado por: Genki en 02 de Agosto de 2008, 13:59
yo estoy empezando ahora... voy por el 3... pero no he entendido muy bien lo de... ';'
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 02 de Agosto de 2008, 16:20
El ; indica el final de cada sentencia. Es el error de sintaxis que más jode la marrana hasta que te acostumbras y lo pones automáticamente.
Título: Re: Curso de iniciación en programación C
Publicado por: Der Metzgermeister en 02 de Agosto de 2008, 22:32
Cita de: Faerindel en 02 de Agosto de 2008, 16:20
El ; indica el final de cada sentencia. Es el error de sintaxis que más jode la marrana hasta que te acostumbras y lo pones automáticamente.

Y lo dice alguien que, según él, no lo sufre porque usa FreePascal
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 02 de Agosto de 2008, 22:58
Cita de: Der Metzgermeister en 02 de Agosto de 2008, 22:32
Cita de: Faerindel en 02 de Agosto de 2008, 16:20
El ; indica el final de cada sentencia. Es el error de sintaxis que más jode la marrana hasta que te acostumbras y lo pones automáticamente.

Y lo dice alguien que, según él, no lo sufre porque usa FreePascal
Er... FP también tiene los ; y nunca he dicho que no los tuviese, mendrugo.
Título: Re: Curso de iniciación en programación C
Publicado por: Maik en 03 de Agosto de 2008, 03:24
y xml tambien.

Putos scripts del counter xD.
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 03 de Agosto de 2008, 12:41
Por cierto, Thylzos, hijoputa, actualiza esto que sólo es copypaste.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 03 de Agosto de 2008, 13:27
Sí, es que estoy un poco vago últimamente :P.

6- Punteros

Antes de empezar, quiero hacer una aclaración. Los que no sepan mucho de programación y hayan seguido el hilo, habrán notado que hay usuarios experimentados que hablan con respeto (incluso con temor) de este tema. Yo siempre dije que los punteros no tienen término medio, o te pueden parecer el invento de un sádico que quería ver sufrir a los programadores o la mejor utilidad de la programación (exagerando en ambos casos, claro). Soy de los que opinan lo segundo, y para que ustedes también puedan disfrutar de ellos, os propongo que vayáis por este tema como si cruzaseis un paraíso construido sobre arenas movedizas, maravillándose por el paisaje pero sin dejar de mirar por donde pisan. Y sin más dilaciones, me presento como guía y les digo: "pasen sólo por donde lo hago yo, habrá cosas muy hermosas que no veremos nada más entrar en le paraíso, pero a las que poco a poco nos iremos acercando."

6.1- Definiciones

-Memoria principal: Es la RAM (supongo que todos saben lo que es), donde se cargan todos los programas que ejecutamos, incluyendo sus variables, constantes y demás datos. Está organizada por segmentos dividos en unidades más pequeñas aún, con los punteros trabajaremos en estas unidades.

-Puntero: Variable que almacena direcciones de memoria con las que se va a trabajar durante la ejecución del programa.

6.2- Qué hacen los punteros.

Ya hemos pasado la entrada de nuestro recorrido y estamos listos para sumergirnos en lo más básico de la programación con punteros, empecemos con los conceptos teóricos.

Como ya sabemos, los punteros son variables que tienen una dirección de memoria de un dato x con el que vamos a trabajar, ¿por qué se complican la vida con punteros pudiendo trabajar con datos directamente?, yo también me lo pregunte cuando comenzaba, y es que hay ocasiones en que es más cómodo (lo veremos cuando aprendamos a hacer nuestra propias funciones) y otras en las que simplemente es imprescindible (con fichero no nos queda otra), por motivos que no puedo explicar en un solo tutorial. Ahora bien, entonces tenemos un puntero y cuando operamos con él es importante saber que (excepto que indiquemos lo contrario) no se afecta al puntero, sino que a los datos de la dirección de memoria que almacene, espero que se entienda porque no se me ocurre otra forma de explicarlo (soy un guía un tanto penoso).

Los punteros son tan importantes que incluso tienen "sus propios operadores" (no es una afirmación del TODO cierta, pero dejémoslo así), y con ellos nos valeremos para declararlos, tratarlos, operar con ellos, etc. Como creo que esto ya no se puede extender mucho más, pasemos a la práctica y los mejor del recorrido.

6.2 - Declaración e inicialización de punteros.

Y por fin a llegado el momento esperado y temido a la vez, el ki de la cuestión.

Para comenzar, nada mejor que un ejemplo recién cocido:

#include <stdio.h>

int main void{
int anyo_actual;
int *puntero_anyo_actual;
printf ("¿Qué año es?");
scanf ("%d", anyo_actual);
puntero_anyo_actual = &anyo_actual;
puntero_anyo_actual ++;
printf ("Ha pasado un año!!!!", anyo actual);
}


Como habrán visto, no hay muchas cosas nuevas, ya que el identificador & lo vimos el temas pasado y sabemos que hace referencia a la dirección de memoria de la variable que antecede. Sin embargo, también saltan a la vista cosas nuevas, así que vayamos despacio y con clama.

Citarint *puntero_anyo_actual;

Primera "cosa" extraña. Aquí es dónde declaramos el puntero, para eso anteponemos el signo * que sirve para advertirle al compilador que esa variable será un puntero.

Citarpuntero_anyo_actual = &anyo_actual;

Quizá muchos lo hayan adivinado (qué orgulloso me sentiría), aquí inicializamos el puntero, dándole como valor la dirección de memoria de la variable anyo_actual, por eso utilizamos el operador &.

Citar*puntero_anyo_actual ++;
printf ("Ha pasado un año!!!!", anyo actual);

Ahora lo interesante, en la primera línea estamos aumentando en uno los datos de la dirección de memoria a la que apunta el puntero, porque, como recordaremos, los cuando operamos con un puntero, en realidad lo hacemos con los datos de la dirección de memoria que almacena. Esto es importante, así que reléelo las veces que haga falta.

En la segunda línea, se printea el contenido de la variable anyo_actual, y al hacerlo comprobaremos que es un año mayor, ya que hemos aumentado su valor en uno desde su puntero en la línea anterior.

Bien, acabo de explicar lo básico con respecto a los puntero, adentrémonos un poco más en el paraíso.

6.4- Utilización de punteros.

Muchos se preguntaran, ¿por qué para declarar el puntero usó *, para inicializarlo no y luego para operar con el puntero sí?, no es que esté borracho o lo esté haciendo al azar, todo tiene una explicación lógica y a fin de entenderlo mejor, veamos cada una de las utilizaciones más comunes de las punteros (con sus repectivos operdadores):

- *Puntero: Aquí (es decir, poniendo delante de la variable el carácter *) estamos diciendo que trabajaremos con el contenido de la dirección que almacena el puntero, se utiliza está expresión para operar con los datos de la variable a la que apunta, generalmente.

- &Puntero:  Aquí para trabajar con la dirección en la cual se encuentra el puntero, no es de las más usadas.

- Puntero: Ahora lo que haremos es trabajar con los datos del puntero, es decir, la dirección de memoria de la variable a la que apunta. Por lo general se utiliza para inicializar y para la programación a un nivel bajo que no frecuentaremos y que consiste en trabajar con la memoria principal en bruto, por lo general con ayuda del lenguaje ASM.

- sizeof (puntero): Aunque no lo parezca sizeof() no es una función es un operador que nos indica el tamaño del contenido de una variable, es muy utilizado con los punteros.

Así que repasemos, con los punteros contamos con tres operadores básicos *, & y sizeof. Usando * nos referimos al contenido de la variable que apunta nuestro puntero, con & a la dirección de memoria en que está ubicado el puntero y con sizeof() el tamaño del puntero o el de la variable que almacena. Sin ninguno de todos estos, estaremos trabajando con la dirección de memoria que almacena el puntero.

Señores, el recorrido por el paraíso ha concluido por hoy, pero prometo que en otros capítulos seguiremos profundizando más y entrando en las zonas de  interés.

6.5- Ejercicios

1- 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.

2- V o F

a) Con *puntero estamos trabajando con la dirección donde está el puntero.

b) Con *puntero estamos trabajando con la dirección de la variable que almacena el puntero.

c) Con &puntero nos encontramos trabajando con el valor de la variable a la que apunta el puntero.

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

El reto

Quizá sea un poco difícil, pero le regalaré un sugus al novel que lo consiga. Haz un programa que genere una variable y un puntero, y luego ve modificándolo para que sin en ningún momento inicializar el puntero con la variable y sólo operando desde la dirección del puntero consigas que este apunte a la variable. Para los que no me hayan entendido (que deben de ser muchos), sin darle al ùntero el valor de la dirección donde está la variable, conseguir que éste apunte a ella (no, no es imposible).
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:23
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.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:25
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.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:27
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í.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:28
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.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:28
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.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:29
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;
}
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:31
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]
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:33
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.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:35
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.
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:36
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...
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:38
12- Estructuras dinámicas

La memoria, como todo recurso, es escaza. De este modo, cuando por ejemplo definimos un array de tamaño 7 y usamos sólo 3 de sus celdas, estamos desperdiciando memoria. De la misma manera, se puede dar el caso en que definamos uno de 7 y necesitemos 10, entonces nos falta memoria. Para esto existe la designación dinámica de memoria, es decir, reservamos memoria en un momento de la ejecución para el programa que estamos haciendo. Igualmente, cuando ya no necesitamos dicha memoria, tenemos que liberarla para que pueda ser usada por otro programa y que no haya errores en el sistema (sí, como los ficheros).

12.1- Definiciones

-Nodo: Unidad de una colección de estructuras dinámicas. Además de sus datos correspondientes, tienen una variable puntero que apunta hacia el siguiente elemento

- Lista: Colección de elementos. Conjunto de elementos asignados mediante estructuras dinámicas (nodos) encadenados entre sí (cada uno tiene la dirección de memoria del siguiente). La dirección de memoria del primer nodo la tiene una variable puntero independiente. De este modo queda: un puntero que apunta al primer nodo, un puntero del primer nodo que apunta al segundo, un puntero del segundo nodo que apunta al tercero... y un puntero del último que apunta a NULL.

- Pilas: Variable de las listas en las que las inserciones de nodos en ella son siempre al principio y que las consultas y borrados son siempre del primer nodo. Para entender su funcionamiento, sepamos que se comporta así: primero en entrar, último en salir.

- Cola: Variable de las listas en las que las inserciones de nodos en ella son siempre al final y que las consultas y borrados son siempre del primer nodo. Para entender su funcionamiento, sepamos que se comporta así: primero en entrar, primero en salir.

12.2- Reserva y liberación de memoria.

Para reservar memoria para un puntero, tenemos la función malloc de stdlib.h. Su forma de uso general es la siguiente:

puntero = (tipo *) malloc (bytes);

Donde puntero es el puntero para el que queremos reservar memoria. Después, tipo es el tipo de dato que vamos a almacenar en dicho espacio de memoria. Por último, bytes es el número de bytes que queremos reservar.

Después de esto, todo sigue como siempre, podemos usar los operadores de punteros y dicho puntero con total normalidad para trabajar con la porción de espacio de memoria que hemos reservado. Pero como todo, acaba y cuando dejemos de usar el puntero en cuestión, debemos liberar la memoria. Para ello tenemos la función free de stdlib.h, cuyo modo de uso es:

[code]free (puntero);


Donde, logicamente, puntero es el puntero donde anteriormente reservamos memoria con malloc. Veamos un ejemplo:

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

struct t_ficha {
   int numero;
   char palabra [30]; }

int main (void) {
struct t_ficha *mi_ficha;

mi_ficha = (struct *) malloc (sizeof(struct t_ficha));

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

fflush (stdin);

printf ("Escribe una palabra: ");
gets (mi_ficha->palabra);

free (mi_ficha);
}


No recuerdo si Master lo explicó en el capítulo de las estrcturas, pero por si acaso aclaro que para acceder a los elementos de un puntero a estructura no se usa el ., sino que una flechita: ->.

12.3- Listas

Las operaciones básicas que podemos hacer sobre una lista de estructuras dinámicas son: insertar un nodo al principio de la lista, insertar un nodo de forma ordenada según los datos que contiene, insertar un nodo al final y borrar un nodo. Para estudiarlas usaremos nodos que contendrás como dato propio sólo un número. Así:

struct t_nodo {
int numero;
t_nodo *siguiente; }


Numero es el número, *siguiente es el puntero al siguiente nodo. Iremos haciendo funciones que manejen la lista (una que añada elementos, una que los busque...) para entender todos los conceptos de éstas.

12.3.1- Insertar al principio

Para realizar esta función, en primer lugar crearemos el nodo y le daremos valores. Luego nos fijamos en el puntero que apunte al primer elemento de la lista, si su valor es NULL, quiere decir que la lista está vacía. En dicho caso, le daremos al puntero el valor de la dirección de memoria del nuevo nodo. Si no, ponemos en la variable "siguiente" la dirección a la que apunta el puntero que apunta al principio (apartir de ahora lo llamaremos "primero") y a dicho puntero le ponemos como valor la dirección de memoria donde está el nuevo nodo. Quizá sea algo difícil de comprender en principio, así que veamos un ejemplo:

void insertar_al_principio (struct t_nodo *p) { /**p es primero (el puntero que apuntaba al primer nodo*/
/*Creamos el nuevo nodo*/
struct t_nodo *nuevo_nodo;
nuevo_nodo= (struct *) malloc (sizeof (struct t_nodo));

/*Le damos un valor*/
printf ("Introduzca un número: ");
scanf ("%d", &nuevo_nodo->numero);

/*Insertamos el nodo al principio*/

/*Hacemos que el campo "siguiente" del nuevo nodo apunte a p (el anterior primer nodo)*/
nuevo_nodo->siguiente = p;
/*Ahora p, es decir, el puntero que apunta al principio, pasa a apuntar al nuevo nodo*/
p = nuevo_nodo;
}


12.3.2- Insertar al final

Para hacer esta función, empezamos como la anterior. Creamos el nodo y le damos datos. Luego, si la lista está vacía, pongo que primero apunte al nuevo nodo y al campo "siguiente" del nuevo nodo le pongo el valor NULL. Si no está vacía, le ponemos al puntero "siguiente" del nuevo nodo el valor NULL y para agregarlo a la lista, la recorremos hasta llegar al final y a este último le ponemos en el valor "siguiente" la dirección de memoria donde está el nuevo nodo.

void insertar_al_final (struct t_nodo *p) {
struct t_nodo *nuevo_nodo, *aux; /**aux es para recorrer la lista*/

/*Reservamos memoria*/
nuevo_nodo = (struct *) malloc (sizeof (struct t_nodo));

/*Escribimos los datos*/
printf ("Ponga un número: ");
scanf ("%d", &nuevo_nodo->numero);

/*Le ponemos NULL a su "siguiete", ya que será el último*/
nuevo_nodo->siguiente = NULL;

/*E insertamos al final: */
if (p != NULL) {
   aux = p; /**aux apunta al primero*/
   while (aux->siguiente != NULL)
      aux = aux->siguiente; /*E irá apuntando a los siguientes hasta llegar al final*/
   aux->siguiente = nuevo_nodo; /*El puntero "siguiente" del último nodo pasa a apuntar al nuevo nodo*/
}
else
   p = nuevo_nodo;
}


12.3.3- Insertar ordenado

Además de lo anterior, en función a un criterio de ordenación en base a los datos de los nodos, podemos insertar a los nuevos de forma ordenada. Para hacer esto, creamos el nodo, le ponemos los datos, recorremos la lista hasta llegar a donde debería estar y lo ubicamos. Así, podría acabar al principio, al final o en medio de dos. Si acaba en medio de dos, debemos poner al puntero "siguiente" del nuevo nodo la dirección de memoria del nodo de la derecha y al "siguiente" de la izquierda la dirección de memoria del nuevo.

Por ejemplo, podemos insertar el nodo en una lista ordenada de menor a mayor:

void insertar_ordenado (struct t_nodo *p) {
struct t_nodo *nuevo_nodo, *anterior, *actual;

/*Creamos y rellenamos el nuevo*/
nuevo_nodo = (struct *) malloc (sizeof (struct t_nodo));
printf ("Inserte un número: ");
scanf ("%d", &nuevo_nodo->numero);

/**actual apunta al principio: */
actual = p;

/*Mientras no se llegue al final y el número del nodo en el que nos encontramos sea menor que el número del nuevo nodo*/
while ((actual != NULL) && (actual->numero < nuevo_nodo->numero)) {
   anterior = actual;
   actual = actual->siguiente; }

if (p == actual) {
/*Si actual es igual al primero, es que hay que ponerlo al principio*/
   nuevo_nodo->siguiente = p;
   p = nuevo_nodo; }
else {
/*Y si no, se inserta entre anterior y actual*/
anterior->siguiente = nuevo_nodo;
nuevo_nodo->siguiente = actual; }
}


12.3.4- Borrar

Para borrar uno, recorremos la lista hasta encontrar el correspondiente. Asignamos al campo "siguiente" del nodo a borrar al campo "siguiente" del nodo situado a la izquierda (o a "primero" o "p" si estamos en el primer nodo). Por último, asignamos al campo "siguiente" del nodo a borrar el valor NULL y se elimina.

void borrar (struct t_nodo *p) {
struct t_nodo anterior, actual;
int numero;

printf ("¿Qué número de la lista desea borrar?");
scanf ("%d", &numero);

actual = p;

/*Mientras no lleguemos al final y el valor "numero" del nodo "actual" sea distinto a "numero"*/
while ((actual != NULL) && (actual->numero != numero)) {
   anterior = actual;
   actual = actual->siguiente; }

/*Comprobamos que el número esté en la lista*/
if (actual != NULL) {
   if (actuall == p) {
      p = actual->siguiente;
      actual->siguiente = NULL;
      free (actual); }
   else {
      anterior->siguiente = actual->siguiente;
      actual->siguiente = NULL;
      free (actual); } }
}


12.3.5- Buscar

Ahora vamos a buscar un número en la lista en base a lo que nos dice uno que nos dice el usuario.

void buscar (struct t_nodo *p) {
struct t_nodo actual;
int encontrado = 0;
int numero;

printf ("Escribe el número que quieres buscar: ");
scanf ("%d", &numero);

actual = p;

while ((actual != NULL) && (encontrado == 0)) {
   if (actual->numero == numero)
      encontrado = 1;
   actual = actual->siguiente; }

if (encontrado == 1)
   printf ("Se ha encontrado el número.");
else
   printf ("No se ha encontrado el número.");
}


12.3.6- Mostrar

También podemos hacer una función que muestre todos los números.

void mostrar_todo (struct *p) {
struct t_nodo *actual;

actual = p;

if (p == NULL)
   printf ("La lista está vacía.");
else {
   while (actual != NULL ) {
      printf ("%d ", actual->numero);
      actual = actual->siguiente; } }
}


12.3.7- Borrar todo.

Como no podía ser de otra forma, también podemos borrar todos los datos de la lista:

void borrar_todo (struct t_nodo *p) {
struct t_nodo *nodo;

while (p != NULL) {
   nodo = p;
   p = p->siguiente;
   nodo->siguiete = NULL;
   free (nodo); }
}


12.4- Pilas

Como tanto las colas como las pilas son variables de las listas, pero en escencia lo mismo, las iremos viendo por encima y con menos profundidad que las anteriores.

12.4.1- Insertar

Recordemos que la pila inserta siempre al principio ("Último en entrar, primero en salir"). Para entender esto mejor, piensen en una pila de libros, vas poniendo libros en la cima y para quitarlos empiezas por el último, nosotros asumiremos que la variable "p" o "primero" apunta a la cima de la pila.

void insertar_en_pila (struct t_nodo *p) {
struct t_nodo *nuevo_nodo;

nuevo_nodo = (struct *) malloc (sizeof (struct t_nodo));

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

nuevo_nodo->siguiente = p;
p = nuevo_nodo;
}


12.4.2- Borrar

Como es una pila, tendremos que borrar el que esté encima de ella, es decir, al que apunte "primero" o "p".

void borrar (struct t_nodo *p) {
atruct t_nodo *actual;

actual = p;

if (p != NULL) {
   p = actual->siguiente;
   actual->siguiente = NULL;
   free (actual); }
else
   printf ("La pila está vacía.");
}


12.4.3- Mostrar

Y para ver el número que está en la cima de la pila:

void mostrar (struct t_nodo *p) {
struct t_nodo *actual;

actual = p;

if (p == NULL)
   printf ("La pila está vacía.");
else
   printf ("%d", actual->numero);
}


12.4.4- Borrar todo

Para borrar todo en una pila, la función de la lista funciona a la perfección.

12.5- Colas

12.5.1- Insertar

Como en las colas los nodos que se insertan van siempre al final, la función insertar_al_final vendría que ni pintada. Eso sí, es normal que para facilitar el trabajo con las colas se use también un puntero que apunte al final.

12.5.2- Borrar

Igual que la función borrar de las pilas.

12.5.3- Mostrar

Igual que la función mostrar_todo de las pilas-

12.5.4- Borrar todo

Igual que la función borrar_todo de las pilas.

12.6- Ejercicios

Pues ahora empezamos con los ejercicios interesantes:

1.- Haz un programa que imite el comportamiento de una cola para entrar al cine. Es decir, que agregue una persona a la cola, que la quite... en fin, todo lo que se hace en esos divertidos momentos.

2.- Haz un programa que tengaun registro de alumnos, el nodo deberá tener como mínimo: el nombre del alumno, su nota en matemáticas, su nota en lengua, su nota en inglés y la media. Deberán estar ordenados en orden alfabético en función del nombre. Haz que se pueda ingresar nuevos alumnos, que se pueda buscar el que tenga la mejor nota en tal materia o el mejor promedio y al que tenga peor nota en tal materia o el peor promedio. También da la posibilidad de buscar en base a un nombre y mostrar todos los datos de ese alumno. Por supuesto, debes permitir que se muestren todos los datos de todos los alumnos, sólo sus notas, sólo sus promedios y sólo sus nombre. Además, deberás dejar que el programa deje borrar al usuario los que no se van a usar. Y como fruta del postre, que se puedan guardar los datos en un fichero binario.


El Reto

Harás un mini-juego. En él habrá dos listas, una con todos los objetos que se pueden conseguir y que estará en un archivo que se leerá al comienzo del juego y otra con los objetos que tiene su personaje. El funcionamiento será el siguiente: habrá un 50% de que se encuentre un nuevo objeto, si se hace se encontrará el que esté más cerca del principio que no se tenga ya; luego un 30 % de perder un sólo objeto (se perderá uno aleatorio) y un 20% de perderlos todos. Ganas cuando los encuentras a todos. Además, el juego se puede guardar en otro archivo binario a parte del ya mencionado.

Truco: para generar números aleatorios y manejar posibilidades tenemos la función rand (), de stdlib.h. Ésta genera un número entre 0 y RAND_MAX (una constante con un número muy muy grande). Para ajustar la función a un conjuto de números entre 0 y N, tendríamos que hacer:

numero = rand () % (N + 1);

Si, por ejemplo, no queremos que empiece por N, tendríamos que sumar una cantidad a lo anterior. Por ejemplo, genremos números entre 1 y 10:

numero = rand () % 10 + 1;




P.D.: Me salió un capítulo muy lioso, recomiendo leerlo más de una vez porque me expliqué de pena.[/code]
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:38
Soluciones del 12º capítulo

Citar1.- Haz un programa que imite el comportamiento de una cola para entrar al cine. Es decir, que agregue una persona a la cola, que la quite... en fin, todo lo que se hace en esos divertidos momentos.

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

struct t_persona {
   char nombre [30];
   struct t_persona *siguiente; }

void nueva_persona (struct t_persona *p);

void persona_menos (struct t_persona *p);

void mostrar (struct t_persona *p);

void cerrar (struct t_persona *p);

int main (void) {
struct t_persona *principio = NULL;
int opcion;

printf ("¿Qué es lo que pasa?\n1- Llega alguien a la cola\n2- Alguien entra al cine (y sale de la cola)\n3- Miras a ver quién hay\n4- Cierra el cine\n5- Salir");
scanf ("%d", &opcion);

do {
   if (opcion == 5)
      return 0;
   switch (opcion) {
      case 1: nueve_persona (principio);
              break;
      case 2: persona_menos (principio);
              break;
      case 3: mostrar (principio);
              break;
      case 4: cerrar (principio);
              break;
      case 5: printf ("Nos vamos...");
              break;
      default: printf ("Opción inválida");
               break;

   printf ("\n¿Qué es lo que pasa?\n1- Llega alguien a la cola\n2- Alguien entra al cine (y sale de la cola)\n3- Miras a ver quién hay\n4- Cierra el cine\n5- Salir");
   scanf ("%d", &opcion);
} while (opcion != 5)
return 0;
}

void nueva_persona (struct t_persona *p) {
struct t_persona *nuevo_nodo, *aux;

nuevo_nodo = (struct *) malloc (sizeof (struct t_nodo));

printf ("\n¿Cómo se llama?");
gets (nuevo_nodo->nombre);

nuevo_nodo->siguiente = NULL;

if (p != NULL) {
   aux = p;
   while (aux->siguiente != NULL)
      aux = aux->siguiente;
   aux->siguiente = nuevo_nodo;
}
else
   p = nuevo_nodo;
}

void persona_menos (struct t_persona *p) {
struct t_persona *actual;

actual = p;

if (p != NULL) {
   p = actual->siguiente;
   actual->siguiente = NULL;
   free (actual); }
else
   printf ("\nLa cola está vacía.");
}

void mostrar (struct t_persona *p) {
struct t_persona *actual;

actual = p;

if (p == NULL)
   printf ("La lista está vacía.");
else {
   while (actual != NULL ) {
      printf ("\n%s ", actual->nombre);
      actual = actual->siguiente; } }
}

void cerrar (struct t_persona *p) {
struct t_persona *nodo;

while (p != NULL) {
   nodo = p;
   p = p->siguiente;
   nodo->siguiete = NULL;
   free (nodo); }
}


Citar2.- Haz un programa que tengaun registro de alumnos, el nodo deberá tener como mínimo: el nombre del alumno, su nota en matemáticas, su nota en lengua, su nota en inglés y la media. Deberán estar ordenados en orden alfabético en función del nombre. Haz que se pueda ingresar nuevos alumnos, que se pueda buscar el que tenga la mejor nota en tal materia o el mejor promedio y al que tenga peor nota en tal materia o el peor promedio. También da la posibilidad de buscar en base a un nombre y mostrar todos los datos de ese alumno. Por supuesto, debes permitir que se muestren todos los datos de todos los alumnos, sólo sus notas, sólo sus promedios y sólo sus nombre. Además, deberás dejar que el programa deje borrar al usuario los que no se van a usar. Y como fruta del postre, que se puedan guardar los datos en un fichero binario.

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

struct t_alumno {
   char nombre [30];
   int mates;
   int lengua;
   int ingles;
   float media;
   struct t_alumno *siguiente }

void insertar_alumno (struct t_alumno *p);

void buscar (struct t_alumno *p);

void mostrar (struct t_alumno *p);

void quitar_alumno (struct t_alumno *p);

void guardar (struct t_alumno *p);

void recuperar (struct t_alumno *p);

int main void {
struct t_alumno *principio = NULL;
int opcion;

printf ("¿Qué desea hacer?\n1- Agregar un alumno\n2- Buscar un alumno\n3- Mostrar alumno\n4- Quitar alumno\n5- Guardar\n6- Recuperar\n7- Salir");
scanf ("%d", &opcion);

while (opcion !=6 ) {
   switch (opcion) {
      case 1: insertar_alumno (principio);
              break;
      case 2: buscar (principio);
              break;
      case 3: mostrar (principio);
              break;
      case 4: quitar_alumno (principio);
              break;
      case 5: guardar (principio);
              break;
      case 6: recuperar (principio);
              break;
      case 7: printf ("Adiós...");
              break;
      default: printf ("Opción inválida");
               break; }
   
   printf ("\n¿Qué desea hacer?\n1- Agregar un alumno\n2- Buscar un alumno\n3- Mostrar alumno\n4- Quitar alumno\n5- Guardar\n6- Recuperar\n7- Salir");
   scanf ("%d", &opcion);
}
return 0;
}

void insertar_alumno (struct t_alumno *p) {
struct t_alumno *nuevo_nodo, *anterior, *actual;

nuevo_nodo = (struct *) malloc (sizeof (struct t_nodo));

printf ("\nInserte el nombre: ");
gets (nuevo_nodo->nombre);
printf ("\nNota de mates: ");
scanf ("%d", nuevo_nodo->mates);
printf ("\nNota de lengua: ");
scanf ("%d", nuevo_nodo->lengua);
printf ("\nNota de ingles: ");
scanf ("%d", nuevo_nodo->ingles);
media = (nuevo_nodo->mates + nuevo_nodo->ingles + nuevo_nodo->lengua) / 3;

actual = p;

while ((actual != NULL) && (strcmp (nuevo_nodo->nombre, actual->nombre) < 0)) {
   anterior = actual;
   actual = actual->siguiente; }

if (p == actual) {
   nuevo_nodo->siguiente = p;
   p = nuevo_nodo; }
else {
   anterior->siguiente = nuevo_nodo;
   nuevo_nodo->siguiente = actual; }
}

void buscar (struct t_alumno *p) {
struct t_alumno actual, encontrar;
int opcion;
int encontrado = 0;
int nota = 0;
char nombre [30];

printf ("\n¿Cómo desea buscar?\n1- En base a sus nombres\n2- Al mejor promedio\n3- Al peor promedio\n4- Al mejor en mates\n5- Al peor en mates\n6- Al mejor en lengua\n7- Al peor en lengua\n8- Al mejor en ingles\n9- Al peor en ingles");
scanf ("%d", &opcion);

actual = p;

switch (opcion) {
   case 1: printf ("Escriba un nombre");
           gets (nombre);
           while ((actual != NULL) && (encontrado == 0)) {
               if (strcmp (actual->nombre, nombre) == 0) {
                  encontrado = 1;
                  encontrar = actual; }
               actual = actual->siguiente; }

           if (encontrado == 1)
              printf ("Se ha encontrado el nombre, estas son sus notas: lengua %d\nmates %d\nIngles %d\nmedia %d.", encontrar->lengua, encontrar->mates, encontrar->ingles, encontrar->media);
           else
              printf ("No se ha encontrado el nombre.");
           break;

   case 2: while ((actual != NULL) && (encontrado == 0)) {
               if (actual->media > nota) {
                  nota = actual->media;
               actual = actual->siguiente; }

           while ((actual != NULL) && (encontrado == 0)) {
               if (nota == actual->media) {
                  encontrar = actual; }
               actual = actual->siguiente; }

           printf ("El mejor promedio es %d y es de %s", nota, encontrar->nombre);
           break;

   case 3: nota = actual->media;
           while ((actual != NULL) && (encontrado == 0)) {
               if (actual->media < nota) {
                  nota = actual->media;
               actual = actual->siguiente; }

           while ((actual != NULL) && (encontrado == 0)) {
               if (nota == actual->media) {
                  encontrar = actual; }
               actual = actual->siguiente; }

           printf ("El peor promedio es %d y es de %s", nota, encontrar->nombre);
           break;

   case 4: nota = actual->mates;
           while ((actual != NULL) && (encontrado == 0)) {
               if (actual->mates > nota) {
                  nota = actual->mates;
               actual = actual->siguiente; }

           while ((actual != NULL) && (encontrado == 0)) {
               if (nota == actual->mates) {
                  encontrar = actual; }
               actual = actual->siguiente; }

           printf ("El mejor en mates es %d y es de %s", nota, encontrar->nombre);
           break;

   case 5: nota = actual->mates;
           while ((actual != NULL) && (encontrado == 0)) {
               if (actual->mates < nota) {
                  nota = actual->mates;
               actual = actual->siguiente; }

           while ((actual != NULL) && (encontrado == 0)) {
               if (nota == actual->mates) {
                  encontrar = actual; }
               actual = actual->siguiente; }

           printf ("El peor en mates es %d y es de %s", nota, encontrar->nombre);
           break;

   case 6: nota = actual->lengua;
           while ((actual != NULL) && (encontrado == 0)) {
               if (actual->lengua > nota) {
                  nota = actual->lengua;
               actual = actual->siguiente; }

           while ((actual != NULL) && (encontrado == 0)) {
               if (nota == actual->lengua) {
                  encontrar = actual; }
               actual = actual->siguiente; }

           printf ("El mejor en lengua es %d y es de %s", nota, encontrar->nombre);
           break;

   case 7: nota = actual->lengua;
           while ((actual != NULL) && (encontrado == 0)) {
               if (actual->lengua < nota) {
                  nota = actual->lengua;
               actual = actual->siguiente; }

           while ((actual != NULL) && (encontrado == 0)) {
               if (nota == actual->lengua) {
                  encontrar = actual; }
               actual = actual->siguiente; }

           printf ("El peor en lengua es %d y es de %s", nota, encontrar->nombre);
           break;

   case 8: nota = actual->ingles;
           while ((actual != NULL) && (encontrado == 0)) {
               if (actual->ingles > nota) {
                  nota = actual->ingles;
               actual = actual->siguiente; }

           while ((actual != NULL) && (encontrado == 0)) {
               if (nota == actual->ingles) {
                  encontrar = actual; }
               actual = actual->siguiente; }

           printf ("El mejor en ingles es %d y es de %s", nota, encontrar->nombre);
           break;

   case 9: nota = actual->ingles;
           while ((actual != NULL) && (encontrado == 0)) {
               if (actual->ingles < nota) {
                  nota = actual->ingles;
               actual = actual->siguiente; }

           while ((actual != NULL) && (encontrado == 0)) {
               if (nota == actual->ingles) {
                  encontrar = actual; }
               actual = actual->siguiente; }

           printf ("El peor en inglés es %d y es de %s", nota, encontrar->nombre);
           break;

   default: printf ("Opción no válida);
            break; } } /*Hay una forma de hacer más corto el algoritmo de búsqueda que usé, pero hubiese sido algo más lioso y por ahora me conformo con que se entienda...*/

void mostrar (struct t_alumno *p) {
int opcion;
struct t_alumno *actual;

printf ("¿Qué deseas ver?\n1- Todo\n2- Sólo los nombres\n3- Nombres y promedio\n4- Nombres y notas");
scanf ("%d", opcion);

switch (opcion) {
   case 1: if (p == NULL)
              printf ("La lista está vacía.");
           else {
              while (actual != NULL ) {
                 printf ("\n%s %d %d %d %d", actual->nombre, actual->mates, actual->lengua, actual->ingles, actual->media);
                 actual = actual->siguiente; } }
           break;

   case 2: if (p == NULL)
              printf ("La lista está vacía.");
           else {
              while (actual != NULL ) {
                 printf ("\n%s", actual->nombre);
                 actual = actual->siguiente; } }
           break;

   case 3: if (p == NULL)
              printf ("La lista está vacía.");
           else {
              while (actual != NULL ) {
                 printf ("\n%s %d", actual->nombre, actual->mates, actual->media);
                 actual = actual->siguiente; } }
           break;

   case 4: if (p == NULL)
              printf ("La lista está vacía.");
           else {
              while (actual != NULL ) {
                 printf ("\n%s %d %d %d", actual->nombre, actual->mates, actual->lengua, actual->ingles);
                 actual = actual->siguiente; } }
           break; }
}

void quitar_alumno (struct t_alumno *p) {
struct t_alumno anterior, actual;
char nombre [30];

printf ("¿Qué alumno de la lista desea borrar?");
gets (nombre);

actual = p;

while ((actual != NULL) && (strcmp (acutal->nombre, nombre) != 0)) {
   anterior = actual;
   actual = actual->siguiente; }

/*Comprobamos que el número esté en la lista*/
if (actual != NULL) {
   if (actuall == p) {
      p = actual->siguiente;
      actual->siguiente = NULL;
      free (actual); }
   else {
      anterior->siguiente = actual->siguiente;
      actual->siguiente = NULL;
      free (actual); } }
}

void guardar (struct t_alumno *p) {
FILE *fichero;
struct t_alumno *actual = p;

fichero = fopen ("binario.bin", "wb");

while (actual != NULL) {
   fwrite (actual, sizeof (p), 1, fichero);
   actual = actual->siguiente; }
}

void recuperar (struct t_alumno *p) {
FILE *fichero;
struct t_alumno *actual = (struct t_alumno *) malloc (sizeof (struct t_alumno));

if ((fichero = fopen ("binario.bin", "wb")) == NULL) {
   printf ("El fichero no existe"); }
else {
   fread (actual, sizeof (actual), 1, fichero);
   actual->siguiente = NULL;
   p = actual;
   while (feof (fichero) == 0) {
      actual->siguiente = (struct t_alumno *) malloc (sizeof (struct t_alumno));
      actual = actual->siguiente;
      fread (actual, sizeof (actual), 1, fichero);
      actual->siguiente = NULL; } }
}


CitarEl Reto

Harás un mini-juego. En él habrá dos listas, una con todos los objetos que se pueden conseguir y que estará en un archivo que se leerá al comienzo del juego y otra con los objetos que tiene su personaje. El funcionamiento será el siguiente: habrá un 50% de que se encuentre un nuevo objeto, si se hace se encontrará el que esté más cerca del principio que no se tenga ya; luego un 30 % de perder un sólo objeto (se perderá uno aleatorio) y un 20% de perderlos todos. Ganas cuando los encuentras a todos. Además, el juego se puede guardar en otro archivo binario a parte del ya mencionado.

Me di cuenta que con una lista es mejor, así que lo hice de ese modo. Una cosa, el código lo que busca es que se entienda (no creo que lo haya logrado) y utilizar, dentro de lo posible, todo lo visto, no optimizar el programa.

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

struct t_item {
   char nombre [30];
   int encontrado;
   struct t_item *siguiente; }

void generar_lista (struct t_item *p);

void perder_item (struct t_item *p);

void ganar_item (struct t_item *p);

void perder_todo (struct t_item *p);

void guardar_partida (struct t_item *p);

void recuperar_partida (struct t_item *p);

char **predeterminado = {"Espada", "Escudo", "Arco" };

int main (void) {
int opcion;
int numero;
struct t_item *principio = (struct t_item *) malloc (sizeof (struct t_item));
principio = NULL;

do {
printf ("\n\tBienvenido a este juego de calidad\n\n¿Qué desea hacer?\n1- Generar una lista de items\n2- Recuperar una partida\n3- Probar suerte\n4- Guardar\n5- Salir");
scanf ("%d", &opcion);

switch (opcion) {
   case 1: generar_lista (principio);
           break;
   case 2: recuperar_partida (principio);
           break;
   case 3: if (principio == NULL) {
              printf ("\nNo se ha inicializado la lista.");
              break; }
           numero = rand () % 10 + 1;
           if ((numero >= 1) || (numero <= 5)
              ganar_item (principio);
           else {
              if (numero > 5) || (numero < 9)
                 perder_item (principio);
              else
                 perder_todo (principio); }
           break;
   case 4: guardar_partida (principio);
           break;
   case 5: printf ("\nSaludos...");
           break;
   default: printf ("Opción no válida");
            break; }
} while (opcion != 5)
return 0;
}

void generar_lista (struct t_item *p) {
char opcion;
struct t_item *actual;
int i;
char **items;

do {
printf ("\n¿Deseas generar una lista personalizada?(S/N)");
scanf ("%c", &opcion);
} while ((opcion != 'S') || (opcion != 'N'))

actual = p;

if (opcion == 'N') {
   printf ("\nGenerando lista predeterminada...");
   
   for (i = 0; i < 3; i ++) {
      actual->encontrado = 0;
      strcpy (actual->nombre, predeterminado [i]);
      actual->siguiente = (struct t_item *) malloc (sizeof (struct t_item));
      actual = actual->siguiente; }
     
   actual = NULL; }
}
else {
   for (i = 0; opcion != 'N'; i++) {
      actual->encontrado = 0;
      printf ("\nIntroduce el nombre del nuevo item: ");
      gets (items [i]);
      strcpy (actual->nombre, items [i]);
      actual->siguiente = (struct t_item *) malloc (sizeof (struct t_item));
      actual = actual->siguiente;
      printf ("\n¿Desea seguir añadiendo items?");
      scanf ("%c", &opcion); }
   
   actual = NULL;
}
}

void perder_item (struct t_item *p) {
int item = rand ();
struct t_item *anterior, *actual;
int i;

actual = p;

for (i = 0; i < item; i++) {
   anterior = actual;
   if (actual->siguiente == NULL)
      actual = p;
   else
      actual = actual->siguiente; }

if (actual == p) {
   p = actual->siguiente;
   actual->siguiente = NULL;
   free (actual); }
else {
   anterior->siguiente = actual->siguiente;
   actual->siguiente = NULL;
   free (actual); }
}

void ganar_item (struct t_item *p) {
struct t_item *actual;

actual = p;

while ((actual->encontrado != 0) && (actual != NULL))
   actual = actual->siguiente;

if (actual == NULL)
   printf ("\nFelicidades, haz encontrado todos los items");
else {
   actual->encontrado = 1;
   printf ("\nHaz encontrado un %s", actual->nombre); }
}

void perder_todo (struct t_item *p){
struct t_item *actual;

actual = p;

while (actual != NULL) {
   actual->encontrado = 0;
   actual = actual->siguiente; }

printf ("\nHaz perdido todo");
}

void guardar_partida (struct t_alumno *p) {
FILE *fichero;
struct t_item *actual = p;

fichero = fopen ("items.bin", "wb");

while (actual != NULL) {
   fwrite (actual, sizeof (p), 1, fichero);
   actual = actual->siguiente; }
}

void recuperar_partida (struct t_alumno *p) {
FILE *fichero;
struct t_item *actual = (struct t_item *) malloc (sizeof (struct t_item));

if ((fichero = fopen ("items.bin", "wb")) == NULL) {
   printf ("No hay ninguna partida guardada"); }
else {
   fread (actual, sizeof (actual), 1, fichero);
   actual->siguiente = NULL;
   p = actual;
   while (feof (fichero) == 0) {
      actual->siguiente = (struct t_item *) malloc (sizeof (struct t_item));
      actual = actual->siguiente;
      fread (actual, sizeof (actual), 1, fichero);
      actual->siguiente = NULL; } }
}
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 08 de Agosto de 2008, 22:40
13- Bibliotecas estándar en C

13.1- Definiciones.

- Biblioteca: Conjunto de rutinas que se usan de manera frecuente.
- Archivos de encabezado: Archivo de una biblioteca que define variables, constantes, estructuras, funciones y demás tipos de datos en una biblioteca.
- Archivos de implementación: Archivos que implementas las funciones declaradas en las archivos de encabeza en una biblioteca
- Macro-identificador: Identificador con que se conoce a la biblioteca.

13.2- Bibliotecas más usadas

13.2.1- stdlib.h

Aquí tenemos una serie de funciones aritméticas, de números aleatorios y conversión de cadenas.

Citarint abs(int j);
long int labs(long int j);
div_t div(int numer, int denom);
ldiv_t ldiv(long int numer, long int denom);

Las dos primeras, calculan el valor absoluto del entero pasado por parámetro. Las otras dos calculan el valor numer entre denom y devuelven el cociente y el resto en una estructura llamada div_t que contiene dos miembros llamados quot y rem.

La estructura div_t:

Citarstruct div_t {
        int quot;  /* cociente */
        int rem;   /* residuo  */
}

Citarint rand(void);
void srand(unsigned int semilla);

Una de éstas ya la conocemos, es rand que devuelve un número aleatorio entre 0 y RAND_MAX (215 - 1). La otra, srand, usa semilla como una la semilla de una nueva serie de enteros pseudo-aleatorios.

Y después las cadenas:

Citardouble atof(const char *cadena) Convierte una cadena a un valor flotante.
int atoi(const char *cadena) Convierte una cadena a un valor entero.
int atol(const char *cadena) Convierte una cadena a un valor entero largo.
double strtod(const char *cadena, char **finap) Convierte una cadena a un valor de punto flotante.
double strtol(const char *cadena, char *finap, int base) Convierte una cadena a un entero largo de acuerdo a una base dada, la cual deberá estar entre 2 y 36 inclusive.
unsigned long strtoul(const char *cadena, char *finap, int base) Convierte una cadena a un entero largo sin signo.

Además tenemos un par de funciones de ordenamiento y búsqueda, pero son un poco más "retorcidas" y prefiero ahorrármelas.

13.2.2- math.h

Como su nombre indica de forma muy elocuente, esta biblioteca se caracteriza por tener funciones aplicadas a las matemáticas. Realmente no tiene ningún secreto, el único es que además de incluirla con un #include en el programa, hay que ligarla a la hora de compilar. En los sistemas *n?x sería algo así:

gcc programa.c -o programa -lm

Sus funciones:

Citardouble acos(double x) Calcula el arco coseno de x.
double asin(double x) Calcula el arco seno de x.
double atan(double x) Devuelve el arco tangente en radianes.
double atan2(double y, double x) Calcula el arco tangente de las dos variables x e y. Es similar a calcular el arco tangente de y / x, excepto en que los signos de ambos argumentos son usados para determinar el cuadrante del resultado.
double ceil(double x) Redondea x hacia arriba al entero más cercano.
double cos(double x) devuelve el coseno de x, donde x está dado en radianes.
double cosh(double x) Devuelve el coseno hiperbólico de x.
double exp(double x) Devuelve el valor de e (la base de los logaritmos naturales) elevado a la potencia x.
double fabs(double x) Devuelve el valor absoluto del número en punto flotante x.
double floor(double x) Redondea x hacia abajo al entero más cercano.
double fmod(double x, double y) Calcula el resto de la división de x entre y. El valor devuelto es x - n * y, donde n es el cociente de x / y.
double frexp(double x, int *exp) Se emplea para dividir el número x en una fracción normalizada y un exponente que se guarda en exp $x=res \times 2^{exp}$.
long int labs(long int j) Calcula el valor absoluto de un entero largo.
double ldexp(double x, int exp) Devuelve el resultado de multiplicar el número x por 2 elevado a exp (inversa de frexp).
double log(double x); Devuelve el logaritmo neperiano de x.
double log10(double x) Devuelve el logaritmo decimal de x.
double modf(double x, double *iptr) Divide el argumento x en una parte entera y una parte fraccional. La parte entera se guarda en iptr.
double pow(double x, double y) Devuelve el valor de x elevado a y.
double sin(double x) Devuelve el seno de x.
double sinh(double x) Regresa el seno hiperbólico de x.
double sqrt(double x) Devuelve la raíz cuadrada no negativa de x.
double tan(double x) Devuelve la tangente de x.
double tanh(double x) Devuelve la tangente hiperbólica de x.

Realmente no tiene mucho truco. Además cuenta con algunas constantes que nos pueden ser de utilidad:

CitarM_E La base de los logaritmos naturales e.
M_LOG2E El logaritmo de e de base 2.
M_LOG10E El logaritmo de e de base 10.
M_LN2 El logartimo natural de 2.
M_LN10 El logaritmo natural de 10.
M_PI PI
M_PI_2 PI/2
M_PI_4 PI/4
M_1_PI 1/PI
M_2_PI 2/PI
M_2_SQRTPI 2/(raiz cuadrad de PI)
M_SQRT2 La raíz cuadrada positiva de 2
M_SQRT1_2 La raíz cuadrada positiva de 1/2

13.2.3- stdio.h

Bueno, ahora una biblioteca que tiene funciones para dar y regalar. Pero como la mayoría ya las vimos, pues no tendremos problemas.

Citarvoid perror(const char *s) Produce un mensaje que va a la salida estándar de errores.
void exit(int status) Se va del programa y devuelve el estado en que terminó al Sistema Operativo o al proceso padre.
int getchar(void) Lee un carácter.
int putchar(char ch) Escribe un carácter.
int printf( const char *formato, lista arg ...) Salida formateada.
int scanf( const char *formato, lista arg ...) Entrada formateada.
int getc () Lee un carácter de distintos flujos.
int putc () Escribe un carácter a partir de diversos flujos
FILE *fopen(const char *nomb, const char *modo) Abre un archivo.
int fprintf(FILE *flujo, const char *formato, args ... ) Escritura formateada de un archivo.
int fscanf(FILE *flujo, const char *formato, args ... ) Lectura formateada de un archivo.
int fgetc(FILE *flujo) Lectura de un carácter desde un fichero
int fputc(char ch, FILE *s) Escritura de un carácter a un fichero.
int fflush(FILE *flujo) Limpia el flujo de datos.
int fclose(FILE *flujo) Desasocia un flujo de datos.
int sprintf(char *cadena, char *formato, args ... ) Escribe una cadena.
int sscanf(char *cadena, cahr *formato, args ... ) Lee una cadena.
int feof(FILE *flujo) Nos dice dónde está el final de un fichero.
int ferror(FILE *flujo) Nos dice si ocurrió un error en el flujo de datos.
void clearerr(FILE *flujo) Limpia el fichero de errores.
int fileno(FILE *flujo) Devuelve el descriptor del fichero.
int open(char* nomb, int flag) Abre archivo binario, si lo hacemos con esta función, no podremos hacer acciones formateadas.
int creat(char* nomb, int perms) Crea un fichero, como la anterior, si se usa no se pueden usar E/S formateadas.
int close(int fd) Cierra un fichero abierto con open ().
int read(int fd, char *buffer, unsigned longitud) Lee desde un fichero binario.
int write(int fd, char *buffer, unsigned longitud) Escribe desde un fichero binario.

13.2.4- string.h

Ahora veremos funciones para manejar cadenas de caracteres.

Citarchar *strcpy(const char *dest, const char *orig) Copia a orig a dest.
int strcmp(const char *s1, const char *s2) Compara las dos cadenas de caracteres.
char *strerror(int errnum) Devuelve un mensaje de error que corresponde a un número de error.
int strlen(const char *s) Calcula la longitud de la cadena.
char *strncat(char *s1, const char *s2, size_t n) Agrega n caracteres de s2 a s1.
int strncmp(const char *s1, char *s2, size_t n) Compara los primeros n caracteres de dos cadenas.
char *strncpy(const char *s1, const char *s2, size_t n) Copia los primeros n caracteres de s2 a s1.
strcasecmp(const char *s1, const char *s2) versión no sensible a mayúsculas de strcmp().
strncasecmp(const char *s1, const char *s2, size_t n) versión no sensible a mayúsculas de strncmp().
char *strchr(const char *s, int c) -- Devuelve un puntero a la primera aparición del carácter c en la cadena s.
char *strrchr(const char *s, int c) -- Encuentra la última aparición del carácter c en la cadena s.
char *strstr(const char *s1, const char *s2) -- Localiza la primera aparición de la cadena s2 en la cadena s1.
char *strpbrk(const char *s1, const char *s2) -- Regresa un apuntador a la primera aparición en la cadena s1 de cualquier carácter de la cadena s2, o un apuntador nulo si no hay un carácter de s2 que exista en s1.
size_t strspn(const char *s1, const char *s2) -- Calcula la longitud del segmento inicial de s1 que consta únicamente de caracteres en s2.
size_t strcspn(const char *s1, const char *s2) -- Regresa el número de caracteres al principio de s1 que no coinciden con s2.
char *strtok(char *s1, const char *s2) -- Divide la cadena apuntada a s1 en una secuencia de tokens, cada uno de ellos esta delimitado por uno o más caracteres de la cadena apuntada por s2.
void *memchr(void *s, int c, size_t n) -- Busca un caracter en un buffer.
int memcmp(void *s1, void *s2, size_t n) -- Compara dos buffers.
void *memcpy(void *dest, void *fuente, size_t n) -- Copia un buffer dentro de otro.
void *memmove(void *dest, void *fuente, size_t n) -- Mueve un número de bytes de un buffer a otro.
void *memset(void *s, int c, size_t n) -- Pone todos los bytes de un buffer a un caracter dado.

Por Dios, qué aburrida esta parte...

13.3- Creando nuestras bibliotecas estáticas.

Si tenemos una serie de funciones y estructuras hechas por nostros que usamos frecuentemente o que necesitaremos para una serie de programas que tenemos pensado hacer, para no tener que estar definiéndolas constantemente, se pueden unir todas en una biblioteca personalizada en archivos de encabezado y de implementación.

13.3.1- Archivos de encabezado.

Como bien se dice más arriba, sirven para definir todos los datos que vallamos a usar. Los archivos de cabecera tienen la extencion archi-conocida ".h" y la siguiente estructura:

#ifndef identificador
#define identificador

/*Las declaraciones van aquí*/

#endif /*Identificador*/

Donde identificador es el macro-identificador de nuestra biblioteca, que no sirve para otra cosa más que para que el procesador se aclare. En la línea:

[code]#ifndef identificador


Comprueba que no esté ya definido el macro-identificador. Si no lo está pasa a definirlo:

#define identificador

Y cuando se terminan de declarar los datos, se termina la definición:

#endif /*Identificador*/

En estos archivos podemos poner desde funciones hasta datos como variables, estrcuturas o constantes. Estos últimos son útiles cuando necesitamos datos comunes entre muchos programas o archivos de un programa. Además, puedes agregar bibliotecas a tu propia biblioteca. Una cosa a tener en cuenta, es que si queremos que los programas que usen nuestra biblioteca puedan acceder a sus variables o funciones, en su declaración debemos anteponer extern. Para aclarar un poco la cosa, veamos un ejemplo, el archivo "mibibli.h:

#ifndef _MIBIBLI_H
#define _MIBIBLI_H

#include <stdio.h>
#define NUMERO_MAXIMO_DE_ALGO 2

extern int variable_inservible;
extern void funcion_sin_sentido (void);

#endif /*_MIBIBLI_H*/


13.3.2- Archivos de implementación.

En estos pondremos las funciones que hemos declarado antes, llevan ".c" como extensión y también deben compilarse. Por ejemplo, el archivo mibibli.c:

#include "mibibli.h"

void funcion_sin_sentido (void) {
printf ("Está función no tiene sentido");
}


Y listo. Lo que queda es compilar, cosa que depende bastante de la plataforma que usemos, así que no se me ocurre cómo dar una explicasión universal. Igual, si alguno tiene una duda con este paso, que consulte.

13.4- Ejercicios

Como este capítulo no tuvo mucho contenido que digamos, sólo habrá un ejercicio:

1- Haz una biblioteca que tenga todo lo necesario para manejar estadísticas.





Y hasta aquí, todo lo publicado hasta ahora. Mañana o pasado pongo lo siguiente.[/code]
Título: Re: Curso de iniciación en programación C
Publicado por: Genki en 11 de Agosto de 2008, 03:00
bueno... posteo medio obligado por mskina... porque me decía no se qué de un riñón... bueno, al grano...

siento mi ineptitud, pero en el punto 3, en lo de tipos de datos, me lo he leido varias veces(más de lo que me veía capaz) y sigo sin entenderlo muy bien...
alguien podría explicarlo para tontos? :$
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 11 de Agosto de 2008, 08:24
Cita de: Genki en 11 de Agosto de 2008, 03:00
bueno... posteo medio obligado por mskina... porque me decía no se qué de un riñón... bueno, al grano...

siento mi ineptitud, pero en el punto 3, en lo de tipos de datos, me lo he leido varias veces(más de lo que me veía capaz) y sigo sin entenderlo muy bien...
alguien podría explicarlo para tontos? :$

¿Te refieres al capítulo 3?, ¿el de las variables?
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 11 de Agosto de 2008, 09:43
Cita de: Genki en 11 de Agosto de 2008, 03:00
bueno... posteo medio obligado por mskina... porque me decía no se qué de un riñón... bueno, al grano...

siento mi ineptitud, pero en el punto 3, en lo de tipos de datos, me lo he leido varias veces(más de lo que me veía capaz) y sigo sin entenderlo muy bien...
alguien podría explicarlo para tontos? :$
No es nada difícil, pero si pusieses unas preguntas más concretas sería mejor.

Una variable ha de ser de un tipo determinado para poder manejarla. En el tema 3 se explican los tipos básicos: char (para guardar un solo caracter ASCII), int (números enteros), float (números reales) y double (números reales más grandes).

Luego están los modificadores long y short, cuando se declara una variable se reserva una cantidad de memoria para ella, si un número necesita más memoria para ser almacenado que el que tiene reservada la variable, ocurre un desbordamiento de memoria (básicamente el programa peta), de ahí que se tenga en cuenta el rango de números que acepta una variable. Long y short incrementan o decrementan la cantidad de memoria reservada, aumentando o disminuyendo el rango.

Y signed y unsigned simplemente indican si la variable acepta valores negativos o no. Por defecto int, float y double aceptan negativos, poner signed sería redundante. En cambio si ponemos unsigned int sólo aceptará valores positivos (y creo que además aprovecha todo el rango para positivos pasando de -32768..32767 a 0..65355).
Título: Re: Curso de iniciación en programación C
Publicado por: Genki en 11 de Agosto de 2008, 18:11
ah joder...
mi problema era con el tipo de datos, del capítulo 3, pero con lo que explicó faerindel, lo entiendo mejor...
muchas gracias :$

ah, y si tengo más dudas puedo preguntar? o hay un tope? xDDDD
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 11 de Agosto de 2008, 18:22
Cita de: Genki en 11 de Agosto de 2008, 18:11
ah joder...
mi problema era con el tipo de datos, del capítulo 3, pero con lo que explicó faerindel, lo entiendo mejor...
muchas gracias :$

ah, y si tengo más dudas puedo preguntar? o hay un tope? xDDDD

Suelta todas las que tengas.
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 11 de Agosto de 2008, 23:11
Cita de: Genki en 11 de Agosto de 2008, 18:11
ah joder...
mi problema era con el tipo de datos, del capítulo 3, pero con lo que explicó faerindel, lo entiendo mejor...
muchas gracias :$

ah, y si tengo más dudas puedo preguntar? o hay un tope? xDDDD
20k de caracteres por post es tu tope, más de eso tendrás que doblepostear.
Título: Re: Curso de iniciación en programación C
Publicado por: Memnoch en 14 de Agosto de 2008, 19:26
Visitante      07:22     Imprimiendo el tema "Curso de iniciación en programación C".

Mandad ésto a Meneame cuando tengamos el nuevo server troncos :lol:
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 14 de Agosto de 2008, 19:38
Aún le quedan 2 capítulos por hacer, y viendo lo que son es como si fuesen 7. :lol:
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 14 de Agosto de 2008, 22:11
Cita de: Faerindel en 14 de Agosto de 2008, 19:38
Aún le quedan 2 capítulos por hacer, y viendo lo que son es como si fuesen 7. :lol:

Qué querrás decir ¬¬. En fin, esta semana pongo las soluciones del capítulo 13 y ya después los sockets. En OGame me parece que ni me molestaré en actualizar :/
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 03 de Septiembre de 2008, 20:25
Cita de: Thylzos en 14 de Agosto de 2008, 22:11
Cita de: Faerindel en 14 de Agosto de 2008, 19:38
Aún le quedan 2 capítulos por hacer, y viendo lo que son es como si fuesen 7. :lol:

Qué querrás decir ¬¬. En fin, esta semana pongo las soluciones del capítulo 13 y ya después los sockets. En OGame me parece que ni me molestaré en actualizar :/
AHEM.

Y en hojame te solicitan. xD
Título: Re: Curso de iniciación en programación C
Publicado por: Thylzos en 03 de Septiembre de 2008, 21:21
Cita de: Faerindel en 03 de Septiembre de 2008, 20:25
Cita de: Thylzos en 14 de Agosto de 2008, 22:11
Cita de: Faerindel en 14 de Agosto de 2008, 19:38
Aún le quedan 2 capítulos por hacer, y viendo lo que son es como si fuesen 7. :lol:

Qué querrás decir ¬¬. En fin, esta semana pongo las soluciones del capítulo 13 y ya después los sockets. En OGame me parece que ni me molestaré en actualizar :/
AHEM.

Y en hojame te solicitan. xD

Por OGame ya casi no me paso :oops:. Los usuarios que leen mi curso me desaniman y me llevan a pensar que o es muy malo o el foro está lleno de tontos :(.

Mañana actualizo. Lo prometo por la Warner Bros.
Título: Re: Curso de iniciación en programación C
Publicado por: elmasnoob en 30 de Septiembre de 2008, 17:14
Me lo voy a imprimir que lo voy a dar en breves, y empiezo de 0. Ya me cuestan organigramas sencillos no me quiero imaginar cuando esté más arriba...

Luego si eso le echo un ojo que ahora me voy a la siesta xD.

Gracias por el curro.
Título: Re: Curso de iniciación en programación C
Publicado por: master ageof en 07 de Octubre de 2008, 13:03
Quiero sockets quiero sockets hu, hu. Quiero sockets quiero sockets hu, hu.
Título: Re: Curso de iniciación en programación C
Publicado por: YoYo en 08 de Octubre de 2008, 18:45
Venga va, ya si eso lo acabas y ordenas el índice para que te redireccione al post correspondiente  :gñe:
Título: Re: Curso de iniciación en programación C
Publicado por: Faerindel en 12 de Enero de 2009, 12:52
Cita de: master ageof en 07 de Octubre de 2008, 13:03
Quiero sockets quiero sockets hu, hu. Quiero sockets quiero sockets hu, hu.
Donde estarán los calcetines tralará ( 8 )...
Título: Re: Curso de iniciación en programación C
Publicado por: isomax en 14 de Enero de 2009, 19:18
Por lo que parece el curso esta sin terminar... vaya... pues bueno, aun asi, creo que lo seguire, que yo el lenguaje mas moderno que apredi fue clipper  :omg: a ver si me actualizo un poquito.
Título: Re: Curso de iniciación en programación C
Publicado por: Maik en 17 de Febrero de 2009, 14:16
:lol:

promt @1,1 Inicio

Estoy hasta los huevos de el.
Título: Re: Curso de iniciación en programación C
Publicado por: raul_isl en 16 de Mayo de 2009, 17:34
!down

Lo tomaremos como descontinuado por el momento. Si el tema sigue, vuelve a subirse y santas pascuas.
Título: Re:Curso de iniciación en programación C
Publicado por: Mskina en 13 de Marzo de 2022, 21:34
Qué tiempos en los que había hasta material didáctico por aquí :(
EhPortal 1.39.2 © 2024, WebDev