Curso de C - Nacho Cabanes - Indice global

Anterior | Índice del tema | Índice global | Posterior
Curso de C, Nacho Cabanes

Curso de C y C++

Versión de libre distribución 2.27h
Nacho Cabanes, Ene. 2003

Escoja una de las siguientes posibilidades:

  • Temas básicos.
  • Ampliaciones sobre los temas básicos.
  • Fuentes de ejemplo.
  • Introducción a C++.
  • Introducción a C++ Builder.
Otros apartados:
  • Compiladores empleados.
  • Ejercicios frecuentes.
  • Mini-referencia del C y C++.
  • Indice alfabético, de ejemplos, tablas.
  • ¿Por qué ha cambiado el formato del curso?
  • Otros temas.
  • Conseguir la última versión.
  • ¿Como puedo colaborar?
  • Contactar con el autor.
  • Próximas mejoras / Cambios entre versiones.
  • Notas finales sobre el curso.
Anterior | Índice del tema | Índice global | Posterior
Ultima versión disponible en:
http://www.pobox.com/users/ncabanes


Curso de C - Nacho Cabanes

Curso de C. Temas básicos.

Introducción.

  • Tema 0. Sobre el curso. Cuenta cómo nació la idea del curso y poco más.
  • Tema 1. Generalidades. Una primera toma de contacto con el lenguaje C.
  • Tema 2. Introd. a variables Introducción a las variables: cómo se definen y se usan.
  • Tema 3. Recapitulación. Un poco más detalle sobre las "cosas raras" que se han pasado un poco por alto en el tema anterior, como el #include.
  • Tema 4. Entrada/Salida básica. Volvemos a "printf" con algo más de detalle, y vemos cómo aceptar la introducción de datos por parte del usuario.
  • Tema 5. Operaciones matemáticas. Cómo hacer las operaciones matemáticas más habituales (sumar, restar, multiplicar, dividir, etc.).
  • Tema 6. Condiciones. Formas de comprobar condiciones: "si ocurre esto, haz aquello; en caso contrario haz esto otro".
  • Tema 7. Bucles. Para permitir que una parte de un programa se repita un cierto número de veces, o hasta que se cumpla una condición, o mientras que se cumpla una condición.
  • Tema 8. Constantes y tipos. Mejoremos eso de las variables: qué ocurre cuando realmente no va a variar o cuando no nos basta con los tipos de datos que existen en el C.
  • Tema 9. Funciones. Cómo crear nuestras propias rutinas, para que los programas sean más fáciles de leer y de modificar/ampliar.
  • Tema 10. Punteros. Cómo reservar memoria dinámicamente (sólo en el momento en que el programa la necesite)
  • Tema 11. Ficheros. Para acceder a ficheros: como leer su contenido, grabar en ellos o crear nuestros propios ficheros.
  • Tema 12. Cadenas de texto. Las cadenas de texto son más difíciles de manejar en C que en otros lenguajes como Pascal o Basic. Por eso, les dedicamos un tema aparte.


Curso de C. Tema 0: Sobre el curso.

Este curso nace como ampliación de un curso de Pascal que impartí en la conferencia Pascal_E de Fidonet entre Enero y Agosto de 1995 (los que tengan dicho curso podrán comprobar que sigo prácticamente la misma estructura).

Por ello, supondré que se entienden términos como compilador, editor, linker, etc., y que se tienen unos conocimientos básicos de programación, a fin de poder avanzar más rápido.

En cuanto a estos conocimientos básicos, daré por supuesto también que son conocimientos de lenguaje Pascal (Turbo Pascal), que es en el que me apoyaré para la mayoría de las comparaciones. Aun así, no debería ser difícil seguirlo si se ha programado en otros lenguajes, como Basic (especialmente si es alguno "moderno", como QBasic).

Este programa se distribuye "tal cual", sin garantía de ningún tipo, implícita ni explícita. Aun así, mi intención es que resulte útil, así que le rogaría que me comunique cualquier error que encuentre.

Esta versión del curso es de libre distribución (“gratis”). Se puede distribuir a otras personas libremente, siempre y cuando no se modifique.

Para cualquier sugerencia, no dude en contactar conmigo.

N.


Curso de C. Tema 1: Generalidades.

Introducción.

C es un lenguaje de programación de ordenadores. No es (posiblemente) un lenguaje para principiantes, pero aun así creo que lo correcto es comenzar con las nociones más básicas:
  • Un programa es un conjunto de órdenes para un ordenador.
  • Estas órdenes se le deben dar en un cierto lenguaje, que el ordenador sea capaz de comprender.
  • El problema es que los lenguajes que realmente entienden los ordenadores resultan difíciles para nosotros, porque son muy distintos de los que nosotros empleamos habitualmente para hablar. Escribir programas en el lenguaje que utiliza internamente el ordenador (llamado “lenguaje máquina” o “código máquina”) es un trabajo duro, tanto a la hora de crear el programa como (especialmente) en el momento de corregir algún fallo o mejorar lo que se hizo.
  • Por eso, en la práctica se emplean lenguajes más parecidos al lenguaje humano, llamados “lenguajes de alto nivel”. Normalmente, estos son muy parecidos al idioma inglés, aunque siguen unas reglas mucho más estrictas.
  • Después, esta serie de órdenes que nosotros hemos preparado en un lenguaje parecido al inglés (lo que se conoce como “programa fuente”) se debe convertir al lenguaje que realmente entiende el ordenador (obteniendo el “programa ejecutable”). Esta conversión se hace mediante unas herramientas llamadas compiladores.

Otras tres palabras que se manejan con mucha frecuencia en la programación, y que es preferible mencionar ya, son:

  • Un algoritmo es la secuencia de pasos que es necesario para resolver un cierto problema (realmente hay alguna condición más, por ejemplo, debe ser un número finito de pasos). Por tanto, un programa de ordenador es un algoritmo expresado no en nuestro lenguaje sino en un lenguaje de programación.
  • Pseudocódigo es el nombre que se suele dar a un lenguaje de programación ficticio, parecido a los de verdad, pero no tan estricto, y muchas veces escrito incluso en español. Por ejemplo, un algoritmo que controlase los pagos que se realizan en una tienda con tarjeta de crédito, escrito en pseudocódigo, podría ser:

      • Leer banda magnética de la tarjeta.
      Conectar con la central de cobros.
      Si hay conexión y la tarjeta es correcta:
        Pedir código PIN
        Si el PIN es correcto, comprobar saldo existente
        Si todo es correcto, aceptar la venta y descontar importe del saldo.
  • Un intérprete es una herramienta parecida a un compilador, con la diferencia de que en los intérpretes, el programa en alto nivel se convierte a código máquina, línea por línea, justo en el momento en que se pone a funcionar. No se crea ningún "programa ejecutable" capaz de funcionar "por sí solo", de modo que si queremos distribuir nuestro programa a alguien, deberemos entregarle el programa fuente y también el intérprete que es capaz de entenderlo.

Dentro de los lenguajes de programación, C es un lenguaje que tiene un cierto "prestigio". Esto se debe fundamentalmente a dos razones:

  • Es bastante “portable”: un programa bien hecho en C se podrá llevar a un ordenador distinto o incluso a un sistema operativo distinto (de MsDos a Unix, por ejemplo) con muy pocos cambios o quizás incluso sin ningún cambio. El motivo es que existe un estándar: el ANSI C, que soportan casi todos los compiladores. Por eso, si nos ceñimos al estándar, es seguro que nuestros programas funcionarán en distintos sistemas; cuanto más nos separemos del estándar (en ocasiones podremos hacerlo), más difícil será que funcionen en otro sistema distinto.
  • Permite hacer “casi de todo”: podemos usar órdenes de alto nivel (muy cercanas al lenguaje humano), pero también de bajo nivel (más cercanas a lo que realmente entiende el ordenador). De hecho, podremos incluso incorporar órdenes en lenguaje ensamblador (que es una traducción directa del "lenguaje máquina", y a menudo se abrevia como Asm) en medio de un programa escrito en C, aunque eso supone que ganemos en control de la máquina que estamos manejando, a costa de perder en portabilidad (el programa ya no se podrá llevar a otros ordenadores que no usen el mismo lenguaje ensamblador)

En su contra, el lenguaje C tiene que puede resultar difícil de leer, especialmente cuando todavía no se conoce bien, y que es más difícil de aprender que otros, sobre todo si no se ha programado antes en otros lenguajes.

Como iremos viendo, el lenguaje C guarda muchas similitudes con el Pascal (por eso me apoyaré en el Pascal para la mayoría de las comparaciones), o incluso con los Basic modernos (que permitan programar de forma estructurada, como TurboBasic, QBasic y QuickBasic), aunque la experiencia me dice que la mayoría de gente que programa en Basic tiende a hacerlo de una forma bastante "desordenada", aunque use estas versiones más modernas.

Los pasos que seguiremos para crear un programa en C serán los mismos que en cualquier otro lenguaje compilado (como Pascal o las últimas versiones de Basic):

  1. Escribir el programa en lenguaje C (fichero fuente), con cualquier editor de textos.
  2. Compilarlo con nuestro compilador. Esto creará un "fichero objeto", ya convertido a un lenguaje que el ordenador es capaz de entender.
  3. Enlazarlo con otros ficheros del compilador, unas bibliotecas auxiliares que incluirán en nuestro programa aquellas posibilidades que hayamos empleado nosotros pero que realmente no sean parte del lenguaje C básico, sino ampliaciones de algún tipo. Esto dará lugar al fichero ejecutable, que ya podremos usar desde MsDos o el sistema operativo que estemos manejando, en nuestro ordenador o en cualquier otro, aunque ese otro ordenador no tenga el compilador que nosotros hemos utilizado.

La mayoría de los compiladores actuales permiten dar todos estos pasos desde un único entorno. Por ejemplo, Turbo C++ 1.01 (y superiores) incluye un editor con el que escribimos, modificamos, grabamos, etc. el programa. Desde ese mismo editor pulsamos ciertas teclas (en este caso, Ctrl+F9), y el compilador se encargará de crear el fichero objeto, enlazarlo con las bibliotecas, crear el fichero ejecutable y ponerlo a funcionar (a no ser que encuentre algún error, claro).

Pero a lo que íbamos... como a programar se aprende programando, voy a tratar de enfocar este curso al revés de como lo hace la mayoría de los libros: en vez de dar primero toda la carga teórica y después aplicarlo, voy a ir poniendo ejemplos, y a continuación la explicación.

Así que empezamos. Ahí va uno:

/*-------------------------*/
/*  Primer ejemplo en C:   */
/*  EJ01.C
*/
/*
*/
/*  Programa elemental     */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01     */
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

#include <stdio.h>

main()
{
   printf("Hola");
}

/*-------------------------*/

Este es un programa que escribe "Hola" en la pantalla. Nos lo creemos, ¿verdad? Bueno, pues ahora vamos a ir comentando cosas sobre él:

  • Lo que hay escrito entre /* y */ es un COMENTARIO, que sirve para aclararnos algo a nosotros, pero que el compilador "se salta" sin ningún tipo de reparo. Es conveniente escribir comentarios que aclaren la misión de las partes de nuestros programas que puedan resultar menos claras a simple vista. Incluso es aconsejable que el programa comience con un comentario (como en este caso), que nos recuerde qué hace el programa sin que necesitemos mirarlo de arriba a abajo. Un comentario puede empezar en una línea y terminar en otra distinta, así:

      /*  Esto
          es un comentario que
          ocupa más de una línea
      */

  • Eso de "#include" nos permite AMPLIAR el lenguaje base (para quien conozca Pascal, es algo parecido al "uses"). En este caso, hemos incluido un "fichero de cabecera" llamado "stdio.h" (standard i/o, entrada y salida estándar), que es el que define la orden "printf" (que no es parte del lenguaje C básico). ¿Y por qué se pone entre < y >? ¿Y por qué eso de # al principio? "Pues porque sí..." No, esto no es cierto del todo, hay razones... que ya iremos viendo un poco más adelante.
  • Ya que estamos con él: por si es poco evidente, "printf" es la orden que se encarga de mostrar un texto en pantalla. Como he comentado, esta función es la responsable de que hayamos escrito "stdio.h" al principio del programa. Resulta que en el lenguaje C base no hay predefinida ninguna orden para escribir en pantalla (!), sino que están definidas en "stdio.h". Pero esto tampoco es mayor problema, porque vamos a encontrar el dichoso "stdio.h" en cualquier compilador que usemos.
  • Aun quedan cosas: ¿qué hacen esas llaves { y } ? Al igual que el Pascal y la mayoría de los lenguajes actuales, el C es un lenguaje estructurado, en el que un programa está formado por diversos "bloques". Todos los elementos que componen este bloque deben estar relacionados entre sí, lo que se indica encerrándolos entre llaves: { y }.
Nota para quien ya haya programado en lenguaje Pascal: Las llaves { y } son el equivalente de las órdenes “begin” y “end” de Pascal.
  • Finalmente, qué es eso de "main"? Es algo que debe existir siempre, e indica el punto en el que realmente comenzará a funcionar el programa. Después de "main" van dos llaves { y }, que delimitan lo que realmente es el cuerpo del programa.
Insisto: este bloque llamado "main" debe existir siempre.
  • ¿Y por qué tiene un paréntesis vacío a continuación? Pues para indicar que se trata de una función (pero eso no debe preocuparnos por ahora; ya estudiaremos las funciones más adelante).
Nota para usuarios "avanzados" de Pascal: En Pascal, cuando una función no tiene parámetros, no se pone ni siquiera los paréntesis; en C hay que ponerlos, aunque queden vacíos (si no hay ningún parámetro, como en este caso).
Y la cosa no acaba aquí. Aún queda más miga de la que parece en este programa, pero cuando ya vayamos practicando un poco, iremos concretando más alguna que otra cosa de las que aquí han quedado un tanto "por encima".

Sólo un par de cosas más antes de seguir adelante:

  • Cada orden de C debe terminar con un punto y coma (;) -al igual que en Pascal; aun así, hay algunas pequeñas diferencias que ya iremos viendo-.
  • El C es un lenguaje de formato libre, de modo que puede haber varias órdenes en una misma línea, u órdenes separadas por varias líneas o espacios entre medias. Lo que realmente indica dónde termina una órden y donde empieza la siguiente son los puntos y coma, como acabamos de comentar. Por ese motivo, el programa anterior se podría haber escrito también así (aunque no es aconsejable, porque puede resultar menos legible):
    #include <stdio.h>
    main()    { printf("Hola"); }
De hecho, hay dos formas especialmente frecuentes de colocar la llave de comienzo, y yo usaré ambas indistintamente. La primera forma es como hemos hecho en el primer ejemplo: en una línea, sola, y justo encima de la llave de cierre correspondiente. La segunda forma frecuente es situándola a continuación del nombre del bloque que comienza (en nuestro caso, a continuación de la palabra “main”), así:
    #include <stdio.h>
    main() {
      printf("Hola");
    }

  • La gran mayoría de las órdenes que encontraremos en el lenguaje C son palabras en inglés o abreviaturas de éstas, al igual que ocurre en la mayoría de los lenguajes de programación. En cambio, en C y al contrario que en otros lenguajes (como Pascal o Basic, por ejemplo) sí existe distinción entre mayúsculas y minúsculas, por lo que "printf" es una palabra reconocida, pero "Printf", "PRINTF" o "PrintF" no lo son.

1.2. Teclear y probar un programa.

¿Y cómo tecleamos y probamos este programa? Pues depende de cada compilador, pero habrá dos casos especialmente frecuentes:

  • Muchos compiladores, generalmente productos comerciales, incluyen un entorno integrado de desarrollo. Al entrar a ese entorno, podemos comenzar a teclear nuestro programa, grabarlo, crear el fichero ejecutable y probar su funcionamiento.
  • Otros compiladores, normalmente de bajo coste, son los llamados “de línea de comandos”. No incluyen entorno integrado, de modo que debemos dar varios pasos:
    1. Utilizamos cualquier editor de textos para teclear el programa en lenguaje C (el “fichero fuente”), y guardarlo con el nombre que nos interese (por ejemplo, el anterior podría ser EJ01.C, y se podría crear desde el propio EDIT que incorpora MsDos).
    2. Después utilizamos el compilador para crear el “fichero objeto”. Teclearíamos algo parecido a “BCC EJ01.C” (en el caso de nuestro fuente EJ01.C, y del compilador Borland C++), y obtendríamos un fichero llamado EJ01.OBJ.
    3. Finalmente, lo enlazaríamos para obtener el “fichero ejecutable”, utilizable por sí solo. Esto se conseguiría tecleando algo similar a “LINK EJ01.OBJ”, y se crearía un fichero llamado EJ01.EXE, que se podría utilizar desde MsDos simplemente tecleando “EJ01”.

Estos pasos podrían ser ligeramente distintos con otros compiladores y especialmente en otros sistemas operativos. Por ejemplo, la mayoría de los compiladores de línea de comandos omiten el tercer paso, porque automáticamente crean el fichero objeto y lo enlazan para dar lugar al fichero ejecutable. En otro apartado del curso veremos lo fundamental del manejo de varios compiladores, así como la forma de conseguirlos.

Ya sabemos escribir, pero eso no nos bastará en la mayoría de los casos, así que sigamos viendo cosas nuevas...


Curso de C. Tema 2: Introducción al manejo de variables.

Las variables son algo que no contiene un valor predeterminado, un espacio de memoria al que nosotros asignamos un nombre y en el que podremos almacenar datos.

En el primer ejemplo nos permitía escribir "Hola", pero normalmente no sabremos de antemano lo que vamos a escribir, sino que dependerá de una serie de cálculos previos (el total de una serie de números que hemos leído, por ejemplo).

Por eso necesitaremos usar variables, en las que guardemos los datos con los que vamos a trabajar y también los resultados temporales.

Vamos a ver primero cómo sumaríamos dos números enteros que fijemos en el programa:

/*-------------------------*/
/*  Ejemplo en C nº 2:     */
/*  EJ02.C
*/
/*
*/
/*  Introd. a variables    */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01     */
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

#include <stdio.h>

int primerNumero;    /* Nuestras variables */
int segundoNumero;
int suma;

main()
{
   primerNumero = 234;
   segundoNumero = 567;
   suma = primerNumero + segundoNumero;

   printf("Su suma es %d", suma);
}

/*-------------------------*/

Para usar una cierta variable primero hay que declararla: indicar su nombre y el tipo de datos que querremos guardar.

En este ejemplo vemos como se declaran las variables en C: indicando primero el tipo de datos que puede almacenar (en este caso "int", números enteros) y a continuación el nombre que les vamos a dar:

    int primerNumero;

Esta línea quiere decir que usaremos una variable, cuyo nombre es "primerNumero", y que el valor que guardará esa variable será un número entero. Posteriormente, con

    primerNumero = 234;

asignamos un valor a esa variable.

Estos nombres de variable (identificadores) siguen prácticamente las mismas reglas de sintaxis que en otros lenguajes como Pascal: pueden estar formados por letras, números o el símbolo de subrayado (_) y deben comenzar por letra o subrayado. No deben tener espacios entre medias, y hay que recordar que las vocales acentuadas y la eñe son problemáticas, porque no son letras "estándar" en todos los idiomas. Algunos compiladores permiten otros símbolos, como el $, pero es aconsejable no usarlos, de modo que el programa sea más portable.

Por eso, no son nombres de variable válidos:

   1numero        (empieza por número)
   un numero      (contiene un espacio)
   Año1           (tiene una eñe)
   MásDatos       (tiene una vocal acentuada)

Tampoco podremos usar como identificadores las palabras reservadas de C. Por ejemplo, la palabra "int" se refiere a que cierta variable guardará un número entero, así que esa palabra "int" no la podremos usar tampoco como nombre de variable. No voy a incluir una lista de palabras reservadas de C, porque eso supondría tener que comentar al menos para que sirven, y además nos encontramos con que hay incluso ciertas palabras que están reservadas en unos compiladores sí y en otros no... La conclusión es que deberíamos usar nombres de variables que a nosotros nos resulten claros, y que tengan pinta de poder ser alguna orden de C.

Sigamos... Insisto en que en C las mayúsculas y minúsculas se consideran diferentes, de modo que si intentamos hacer

    PrimerNumero = 0;
    primernumero = 0;

o cualquier variación similar, el compilador protestará y nos dirá que no conoce esa variable, porque la habíamos declarado como

    int primerNumero;

El número de letras que puede tener un "identificador" (el nombre de una variable, por ejemplo) depende del compilador que usemos. Es frecuente que permitan cualquier longitud, pero que realmente sólo se fijen en unas cuantas letras (por ejemplo, en las primeras 8 o en las primeras 32). Eso quiere decir que puede que algún compilador considerase como iguales las variables NumeroParaAnalizar1 y NumeroParaAnalizar2, porque tienen las primeras 18 letras iguales. El C estándar (ANSI C) permite cualquier longitud, pero sólo considera las primeras 31.

Por cierto, como las tres variables van a guardar números enteros, podemos declararlas a la vez, separadas por comas:

  int primerNumero;
  int segundoNumero;
  int suma;

es lo mismo que

  int primerNumero, segundoNumero, suma;

o que

  int primerNumero,
      segundoNumero,
      suma;

Pero en este ejemplo hay más cosas "raras". ¿Por qué ahora en printf aparece ese "%d"? Habíamos hablado de printf como si escribiera en pantalla lo que nosotros le indicamos entre comillas. Esto no es del todo cierto:

Eso que le hemos indicado entre comillas es realmente un código de FORMATO. Dentro de ese código de formato podemos tener caracteres especiales, con los que le indicamos dónde y cómo queremos que aparezca un número. Esto lo veremos con detalle un poco más adelante (en el tema 4), pero de momento anticipo que cuando el mensaje aparezca en pantalla, ese "%d" se sustituirá por un número entero. Qué número? El que le indicamos a continuación, separado por una coma (en este caso, "suma").

TEMA 2.2

En C es posible dar un valor a las variables a la vez que se las declara (algo parecido a lo que se puede hacer Turbo Pascal empleando "constantes con tipo"). Así, el programa anterior quedaría:

  /*-------------------------*/
/*  Ejemplo en C nº 3:
*/
/*  EJ03.C
*/
/*
*/
/*  Inicialización de
*/
/*  variables
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int primerNumero = 234;    /* Nuestras variables
*/
int segundoNumero = 567;
int suma;

  main()
  {
     suma = primerNumero + segundoNumero;

     printf("Su suma es %d", suma);
  }

  /*-------------------------*/

Al igual que en el caso anterior, como las tres variables son del mismo tipo, podemos declararlas a la vez haciendo:

  int primerNumero=234, segundoNumero=567, suma;

¿Y cómo escribimos más de un valor a la vez? Pues vamos a verlo con otro ejemplo...

TEMA 2.3.

Vamos a ver cómo imprimir más de un valor, también ampliando el ejemplo anterior:

  /*-------------------------*/
/*  Ejemplo en C nº 4:
*/
/*  EJ04.C
*/
/*
*/
/*  Escribir más de una    */
/*  variable
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int primerNumero = 234;    /* Nuestras variables
*/
int segundoNumero = 567;
int suma;

  main()
  {
     suma = primerNumero + segundoNumero;

     printf("El primer número es %d, el segundo %d y su suma %d.",
       primerNumero, segundoNumero, suma);
  }

  /*-------------------------*/

Un único comentario a este programa: hemos dicho que el C es un lenguaje de formato libre. Por eso, no hay ningún problema en dividir en dos la línea de la orden "printf", como hemos hecho, con la intención de que se vea la orden entera en una pantalla "normal". Lo de que la segunda línea del "printf" comience un poco más a la derecha es simplemente buscando mayor legibilidad: para que se vea que es continuación de la línea anterior, en vez de ser una orden nueva.

Sigamos. Hasta ahora hemos estado viendo cómo escribir variables, pero si queremos que realmente sean variables (que sus valores puedan variar), es lógico pensar que no tendríamos que ser nosotros quienes les damos su valor, sino los usuarios de nuestros programas. Entonces, debe haber alguna forma de que el usuario introduzca datos. Vamos a verlo...

TEMA 2.4.

Veamos cómo dejar que el usuario sea quien introduzca los datos:

  /*-------------------------*/
/*  Ejemplo en C nº 5:
*/
/*  EJ05.C
*/
/*
*/
/*  Leer valores para
*/
/*  variables
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int primerNumero, segundoNumero, suma;    /* Nuestras variables */

  main()
{
printf("Introduce el primer número
");
scanf("%d", &primerNumero);
printf("Introduce el segundo número
");
scanf("%d", &segundoNumero);
suma = primerNumero + segundoNumero;
printf("Su suma es %d", suma);
}

  /*-------------------------*/

Por si alguien no lo intuye, la instrucción para que el usuario pueda dar un valor a una variable es "scanf".

Una vez que hemos visto que era ese %d de "printf", ya intuimos que en este caso servirá para indicar que lo que vamos a leer es un número entero.

Pero ¿qué es ese & que aparece antes de cada variable? Es porque lo que le estamos indicando a "scanf" es la DIRECCIÓN en la que se debe guardar los datos. En nuestro caso, será la dirección de memoria que habíamos reservado para la variable "primerNumero" y posteriormente la reservada para "segundoNumero".

Bueno, ahora que ya vamos viendo cómo podemos ver en pantalla el contenido de una variable, cómo podemos fijarlo, y como podemos dejar que sea el usuario quien le dé un valor, creo que ya va siendo hora de ver qué tipos de variables más habituales que podemos usar...

  • int. Ya comentado: es un número entero (en el DOS, desde -32768 hasta 32767; en otros sistemas operativos, como UNIX, pueden ser valores distintos -hasta unos dos mil millones-).
  • char. Un carácter (una letra, una cifra, un signo de puntuación, etc.). Se indica entre comillas simples: letra = 'W'
  • float. Un numero real (con decimales).

TEMA 2.5.

Los que hemos visto son los tipos más sencillos. También podemos crear arrays y registros:
  • Un array (que algunos autores traduce por "arreglo") es un conjunto de elementos, todos los cuales son del mismo tipo. Es la estructura que emplearemos normalmente para crear vectores y matrices. Por ejemplo, para definir un grupo de 5 números enteros y hallar su suma podemos hacer:

/*-------------------------*/
/*  Ejemplo en C nº 6:
*/
/*  EJ06.C
*/
/*
*/
/*  Primer ejemplo de
*/
/*  arrays
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero[5];
/* Un array de 5 números enteros */
int suma;
/* Un entero que será la suma */

  main()
{
numero[0] = 200;
/* Les damos valores */
numero[1] = 150;
numero[2] = 100;
numero[3] = -50;
numero[4] = 300;
suma = numero[0] +    /*
Y hallamos la suma */
numero[1] + numero[2] + numero[3]
+ numero[4];
printf("Su suma es %d", suma);
/* Nota: esta es la forma más ineficiente
e incómoda */
/* Ya lo iremos mejorando */
}

  /*-------------------------*/

Una primera observación: hemos declarado un array de 5 elementos. Para numerarlos, se empieza en 0, luego tendremos desde "numero[0]" hasta "numero[4]", como se ve en el ejemplo.
Esto es muy mejorable. La primera mejora evidente (o casi) es no tener que dar los valores uno por uno. Podemos dar un valor a las variables, como hicimos en el ejemplo 3...

TEMA 2.6.

Vamos a dar un valor inicial a un "array":

/*-------------------------*/
/*  Ejemplo en C nº 7:
*/
/*  EJ07.C
*/
/*
*/
/*  Inicialización de
*/
/*  arrays
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero[5] =
/* Un array de 5 números enteros */
{200, 150, 100, -50, 300};
int suma;
/* Un entero que será la suma */

  main()
{
suma = numero[0] +    /*
Y hallamos la suma */
numero[1] + numero[2] + numero[3]
+ numero[4];
printf("Su suma es %d", suma);
/* Nota: esta forma es algo menos engorrosa,
pero todavía no está */
/* bien hecho.  Lo seguiremos mejorando
*/
}

  /*-------------------------*/

Quien haya llegado hasta aquí y ya sepa algo de programación, imaginará que la siguiente mejora es no repetir los valores en

           suma = numero[0] + ...

La forma de hacerlo será empleando algún tipo de estructura que nos permita repetir varios pasos sin tener que indicarlos uno a uno. Es el caso del bucle "for" en Pascal (o en Basic), que ya veremos cómo usar en C.

Podemos declarar arrays de dos o más dimensiones, para guardar matrices, por ejemplo. A quien no haya estudiado nada de matrices, esto le puede sonar a chino, así que pongamos un ejemplo: Queremos guardar datos sobre 100 personas, y para cada persona nos interesa almacenar 15 números, todos ellos reales. Haríamos:
  float datos[100][15]
El dato número 10 de la persona 20 sería "datos[19][9]" (recordemos que se empieza a numerar en 0).

Aún nos queda algo por ver...

TEMA 2.7.

Sigamos. El próximo paso es ver qué es eso de los registros.

  • Un registro es una agrupación de datos, los cuales no necesariamente son del mismo tipo. Se definen con la palabra clave "struct", y su manejo (muy parecido a como se usan los "records" en Pascal) es: para acceder a cada uno de los datos que forman el registro, se debe indicar el nombre de la variable y el del dato (o campo) separados por un punto:

/*-------------------------*/
/*  Ejemplo en C nº 8:
*/
/*  EJ08.C
*/
/*
*/
/*  Registros (struct)     */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  struct {
    char  inicial;
    int   edad;
    float nota;
    } persona;

  main()
  {
     persona.inicial = 'J';
     persona.edad = 20;
     persona.nota = 7.5;
     printf("La edad es %d", persona.edad);
  }

  /*-------------------------*/

Como es habitual en C, primero hemos indicado el tipo de la variable: (struct { ...} ) y después el nombre de la variable (persona).
Hemos guardado varios datos de una persona. Se pueden almacenar los de varias personas si combinamos esto con los array que vimos anteriormente. Por ejemplo, si queremos guardar los datos de 100 alumnos podríamos hacer:

  struct {
    char  inicial;
    int   edad;
    float nota;
    } alumnos[100];

La inicial del primer alumno sería "alumnos[0].inicial", y la edad del último sería "alumnos[99].edad".
También hay otros tipos especiales de "struct", aunque de manejo más avanzado. Es el caso de uniones y campos de bits (que veremos apenas un poco más adelante)

Los tipos permiten modificadores: unsigned o signed, y long o short.

Por ejemplo, un char por defecto se considera que es un valor con signo, luego va desde -128 hasta +127. Si queremos que sea positivo, y entonces que vaya desde 0 hasta 255, deberíamos declararlo como "unsigned char".

De igual modo, un int va desde -32768 hasta 32767. Si queremos que sea positivo, y entonces vaya desde 0 hasta 65535, lo declararemos como "unsigned int".

Podemos ampliar el rango de valores de un entero usando "long": un "long int" irá desde -2147483648 hasta 2147483647. Por contraposición, un "short int" será un entero no largo ("normal").

También hay otros tipos de datos "menos habituales". Se trata de valores reales que permiten una mayor precisión y un mayor rango de valores que "float". Estos son "double" y "long double".

Los modificadores permitidos son:

      Modificador    Aplicable a

        short       int
        long        int, double
        signed      int, char
        unsigned    int, char

Pero quizá sea mejor recopilar todo esto en una tabla...

TEMA 2.8.

Vamos a recopilar todo esto de los modificadores en una tabla. Los paréntesis indican que una cierta parte del nombre es el valor "por defecto" (el que se considera, mientras no se indique lo contrario). A la derecha indico el valor mínimo y el valor máximo que puede tomar una variable de ese tipo:

  Nombre                  Bytes       Min           Max

  (signed) char             1        -128           127
  unsigned char             1          0            255

  (signed) (short) int      2       -32768         32767
  unsigned (short) (int)    2          0           65535
  (signed) long (int)       4    -2147483648    2147483647
  unsigned long (int)       4          0        4294967295

  float                     4       3.4E-38        3.4E+38
  double                    8       1.7E-308      1.7E+308
  long double              10      3.4E-4932     1.1E+4932

Con los paréntesis me refiero a que "signed char" es lo mismo que "char", y que "long" es lo mismo que "long int" y que "signed long int", por ejemplo.

Nota: estos tamaños y rangos de valores son válidos en la mayoría de los compiladores bajo DOS (16 bits). Puede existir compiladores para otros sistemas operativos (incluso algún caso bajo DOS) en los que estos valores cambien. Por ejemplo, con GCC para Linux, el rango de los "int" coincide con el que en MsDos suele ser un "long int".

En cualquier caso, estos valores se pueden comprobar, porque tenemos definidas unas constantes como MAXINT o MAXLONG (en el fichero de cabecera "values.h"), que nos dicen hasta qué valor podemos llegar con cada tipo de número.

¿Y los Booleanos? Recordemos que en Pascal (y en otros lenguajes) contamos con un tipo especial de datos que puede valer "verdadero" o "falso". En C no es así, sino que se trabaja con enteros. Entonces, al comprobar una condición no obtendremos directamente "verdadero" o "falso", sino 0 (equivalente a FALSO) o un número distinto de cero (equivalente a VERDADERO; normalmente será 1).

¿Y las cadenas de texto? Aquí la cosa se complica un poco. No existe un tipo "string" como tal, sino que las cadenas de texto se consideran "arrays" de caracteres. Están formadas por una sucesión de caracteres terminada con un carácter nulo (0), y no serán tan fáciles de manejar como lo son en Pascal y otros lenguajes. Por ejemplo, no podremos hacer cosas como

  Nombre := 'Don ' + Nombre;

Es decir, algo tan habitual como concatenar cadenas se va a complicar (para quien venga de Pascal o Basic). Tendremos que usar unas funciones específicas, pero eso lo veremos más adelante. Ahora vamos a ver un primer ejemplo del uso de cadenas de texto, en su forma más sencilla, pero que también es la menos habitual en la práctica.

  /*-------------------------*/
/*  Ejemplo en C nº 9:
*/
/*  EJ09.C
*/
/*
*/
/*  Introducción a las
*/
/*  cadenas de texto
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  char texto1[80] = "Primer texto";
  char texto2[80];

  main()
  {
     printf("Introduzca un texto: ");
     scanf("%s", &texto2);
     printf("El primer texto es %s", texto1);
     printf(" y Vd. ha tecleado %s", texto2);
  }

  /*-------------------------*/

Creo que el ejemplo se entiende por sí mismo, porque la única novedad con respecto a ejemplos anteriores es "%s" para indicar que lo que queremos leer o escribir es una cadena de texto.

Eso sí, varios comentarios:

  • Hemos reservado 80 caracteres (realmente 79, porque recordemos que al guardarlo en memoria se le añade un carácter 0 para indicar el final de la cadena), así que si tecleamos más de 79 caracteres, nadie nos asegura lo que vaya a pasar: puede que nuestro programa intente guardar todo lo que hemos tecleado, sin pensar que, como no cabe, puede estar "machacando" otros datos o incluso instrucciones de nuestro programa.
  • Segundo comentario: si la cadena contiene espacios, se lee sólo hasta el primer espacio. Esto se puede considerar una ventaja o un inconveniente, según el uso que se le quiera dar. En cualquier caso, en el tema 4 veremos cómo evitarlo (empleando la orden “gets” en vez de "scanf").
  • Tercero: no hace falta el "&" de "&texto2" en "scanf", porque habíamos dicho que ese símbolo hacía referencia a la dirección en la que se nos había reservado la memoria para esa variable, cosa que no es necesaria en un array, porque es el propio nombre lo que está haciendo referencia a la dirección. ¿A que parece un trabalenguas? No es tan complicado: para que se vea a qué me refiero, va un ejemplo, comparando una variable "normal" y un array:

   int Dato = 5;

     Dato vale 5;
     &Dato nos da la dirección en que está guardado ese valor

   char Texto[40] = "Una prueba";

     Texto[0] vale 'U' (la primera letra).
     Texto[1] vale 'n' (la segunda), y así sucesivamente.
     Texto (sin corchetes) da la dirección en está guardado el array.

Por tanto, como "scanf" espera que le digamos la dirección en la que queremos guardar la cadena de texto (un array), podemos usar
    scanf("%s", texto2);
  • Cuarto (y último): Algo que igual alguien ha pasado por alto, así que lo quiero recalcar. Las cadenas de texto se encierran entre comillas dobles ("Hola") y las letras aisladas entre comillas simples ('H').

TEMA 2.9.

Vamos a ver unos tipos especiales de registros (struct), pero bastante menos básicos. Son las uniones y los campos de bits.

Una unión recuerda a un "struct", con la diferencia de que sus "campos" comparten el mismo espacio de memoria:

   union {
char letra;
/* 1 byte */
int numero;
/* 2 bytes */
} ejemplo;

En este caso, la variable "ejemplo" ocupa 2 bytes en memoria (suponiendo que estemos trabajando en un compilador de MsDos 16 bits). El primer byte está compartido por "letra" y "numero", y el segundo byte sólo pertenece a "numero".

Si hacemos

   ejemplo.numero := 25;
   ejemplo.letra  := 50;
   printf("%d", ejemplo.numero);

Veremos que "ejemplo.numero" ya no vale 25, puesto que al modificar "ejemplo.letra" estamos cambiando su primer byte. Ahora "ejemplo.numero" valdría 50 o 50*256, según si el ordenador que estamos utilizando almacena en primer lugar el byte más significativo o el menos significativo.

Un campo de bits es un elemento de un registro (struct), que se define basándose en su tamaño en bits. Se define de forma muy parecida (pero no igual) a un "struct" normal, indicando el número de bits que se debe reservar a cada elemento:

   struct  bit_field {
           int bit_1        : 1;
           int bits_2_a_5   : 4;
           int bit_6        : 1;
           int bits_7_a_16  : 10;
   } variableDeBits;

Esta variable ocuparía 1+4+1+10 = 16 bits (2 bytes, como un número entero en la mayoría de compiladores de MsDos).


Curso de C. Tema 3: Pequeña recapitulación.

Bueno, parece que ya vamos sabiendo algo. Pero antes de avanzar, va siendo el momento de puntualizar y/o concretar algunas de las cosas que hemos ido viendo, para que esto tenga también algo de rigor...
Nota: Este es un tema mucho más difícil que los anteriores, y que puede resultar útil para quien ya conozca otros lenguajes de programación, pero será mucho más duro para quien sea nuevo en este mundo. Por eso, mi consejo es que quien no haya programado en otros lenguajes pase al tema 4, porque estos conceptos se irán viendo poco a poco más adelante...

#include y .h

Es muy frecuente que un programa en C esté dividido en varios módulos (o subprogramas), varios ficheros fuente, que se deben ensamblar en el momento de crear el programa ejecutable.

Entonces puede ocurrir que en "prog2.c" hayamos definido una función que necesita "prog1.c". Cuando "prog1" comienza a compilar descubre que no conoce una de las funciones que aparecen por ahí, y el compilador protesta.

Una forma de evitarlo es poniendo dentro de “prog1.c” la "cabecera" de la función que falta, es decir, su nombre y sus parámetros (pero sin detallar los pasos que debe dar; de eso ya se encarga "prog2.c"):

int funcion1 (int parametro1, int parametro2);

Así "prog1" sabe que esa función existe, que no es que hayamos tecleado algo mal, sino que "está ahí", y que ya le llegarán los detalles concretos más adelante, cuando termine de ensamblar todos los módulos.

Nota para Pascal: A quien venga de Pascal, eso de "la cabecera" ya le sonará, porque es igual que lo que ponemos en la sección "interface" de las unidades que creemos nosotros mismos.

Pero además tenemos más casos que éstos, porque incluso si nuestro programa está formado por un sólo módulo, normalmente necesitaremos funciones como "printf", que, como hemos comentado, no forman parte del lenguaje base. Nos encontramos con el mismo problema: si no ponemos la cabecera, el compilador no sabrá que esa función "existe pero aún no se la hemos explicado", sino que supondrá que hemos escrito algo mal y protestará.

Así que, entre unas y otras, tendríamos que llenar nuestro programa con montones y montones de cabeceras. La forma habitual de evitarlo es juntar esas cabeceras en "ficheros de cabecera", que luego incluimos en nuestro programa.

Así, todas las funciones que están relacionadas con la entrada y salida estándar, y que se enlazan en el momento de crear el ejecutable, las tenemos declaradas en el fichero de cabecera "stdio.h". Por eso, cuando escribimos

   #include <stdio.h>

el compilador coge el fichero "stdio.h", lee todas las cabeceras que contiene y las "inserta" en ese mismo punto de nuestro programa. A partir de entonces, ya “conoce” funciones como “printf”, aunque todavía no sepa los detalles exactos de cómo funcionan.

¿Y porque eso de #? Pues porque "include" no es una orden del lenguaje C, sino una orden directa al compilador (una "directiva"). Realmente es una orden a una cierta parte del compilador que se llama “preprocesador”. Estas directivas, como #include o como #define (que veremos más adelante), indican una serie de pasos que se deben dar antes de empezar realmente a traducir nuestro programa fuente.

En este caso, cuando llega el momento de comprobar la sintaxis del lenguaje C ya no existe eso de "include", sino que en su lugar el compilador ya ha dejado todas las cabeceras que queríamos.

¿Y eso de < >? Pues podemos encontrar líneas como

   #include <stdio.h>

y como

   #include "misdatos.h"

El primer caso es un fichero de cabecera estándar del compilador. Lo indicamos entre < y > y así el compilador sabe que tiene que buscarlo en su directorio de "includes". El segundo caso es un fichero de cabecera que hemos creado nosotros, por lo que lo indicamos entre " y ", y así el compilador sabe que no debe buscarlo entre SUS directorios, sino en el mismo directorio en el que está nuestro programa.

Warnings y main()

Según el compilador que se use, puede que con los ejemplos anteriores haya aparecido un "warning" que diga algo como

   Warning: function should return a value in function main

Es decir "aviso: la función debería devolver un valor, en la función MAIN".

Vayamos por partes:

  • Eso de los WARNING son avisos del compilador, una forma de decirnos "Cuidado, que puede que esto no esté del todo bien"
  • ¿Por qué avisa en este caso? Porque en C no hay funciones y procedimientos, sino sólo funciones. Por tanto, siempre deberán devolver un valor, aunque puede que este sea de un tipo especial "nulo". Y en cambio, en nuestros programas se veía por ahí ningún valor devuelto en "main"...
  • ¿Formas de hacer que esté del todo correcto, para que el compilador no proteste? Hay dos: una (menos correcta) es decir que "main" es de ese tipo "nulo", con lo cual no hay que devolver ningún valor, y otra (más correcta) es obligar a que devuelva un valor, que normalmente será 0 (para indicar que todo ha funcionado correctamente; otro valor indicaría algún tipo de error). Este valor es el "errorlevel" que se devuelve al DOS cuando termina la ejecución del programa. Pues vamos con la primera forma:

/*-------------------------*/
/*  Ejemplo en C nº 10:    */
/*  EJ10.C
*/
/*
*/
/*  "main" como int
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int main()
  {
     printf("Hola");
     return 0;
  }

  /*-------------------------*/

Con eso de "int main()" decimos que la función "main" va a devolver un valor entero. Es lo que se considera por defecto (si no ponemos "int", el compilador considera de todas formas que lo es).

Con eso de "return 0" devolvemos el valor 0 a la salida de "main", que indica que todo ha ido correctamente. Como la función ya devuelve un valor, el Warning desaparece.

La otra forma de que desaparezca el "Warning" es

  /*-------------------------*/
/*  Ejemplo en C n 11:    */
/*  EJ11.C
*/
/*
*/
/*  "main" como void
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  void main()
  {
     printf("Hola");
  }

  /*-------------------------*/

Eso de "void" es el tipo nulo al que nos referíamos. Así indicamos que la función no va a devolver valores.

Esta es la opción "menos buena" por dos motivos:

  • Nos puede interesar que nuestro programa devuelva un cierto código al terminar, para indicar si todo ha funcionado correctamente o si ha surgido algún tipo de problema. Es lo que en MsDos se llamaba el “errorlevel”.
  • Puede ocurrir que a algún compilador no le guste eso de que "main" no devuelva un valor entero, aunque no es lo habitual: les suele bastar con que exista la función "main", sea del tipo que sea.


Curso de C. Tema 4: Entrada/salida básica.

Hemos visto por encima cómo mostrar datos en pantalla y cómo aceptar la introducción de datos por parte del usuario, mediante "printf" y "scanf", respectivamente. Veamos ahora su manejo y algunas de sus posibilidades con más detalle, junto con otras órdenes alternativas:

El formato de printf es

   printf( formato, lista de variables);

Dentro del apartado de "formato" habíamos comentado que "%d" indicaba que se iba a escribir un número entero, y que "%s" indicaba una cadena de texto. Vamos a resumir en una tabla las demás posibilidades, que no habíamos tratado todavía:

  Código  Formato

    %d    Número entero con signo, en notación decimal
    %i    Número entero con signo, en notación decimal
    %u    Número entero sin signo, en notación decimal
    %o    Número entero sin signo, en notación octal (base 8)
    %x    Número entero sin signo, en hexadecimal (base 16)
    %X    Número entero sin signo, en hexadecimal, mayúsculas
    %f    Número real (coma flotante, con decimales)
    %e    Número real en notación científica
    %g    Usa el más corto entre %e y %f
    %c    Un único carácter
    %s    Cadena de caracteres
    %%    Signo de tanto por ciento: %
    %p    Puntero (dirección de memoria)
    %n    Se debe indicar la dirección de una variable entera
          (como en scanf), y en ella quedará guardado el
          número de caracteres impresos hasta ese momento

Además, las órdenes de formato pueden tener modificadores, que se sitúan entre el % y la letra identificativa del código.

  • Si el modificador es un número, especifica la anchura mínima en la que se escribe ese argumento.
  • Si ese número empieza por 0, los espacios sobrantes (si los hay) de la anchura mínima se rellenan con 0.
  • Si ese número tiene decimales, indica el número de dígitos enteros y decimales si los que se va a escribir es un número, o la anchura mínima y máxima si se trata de una cadena de caracteres.
  • Si el número es negativo, la salida se justificará a la izquierda (en caso contrario, es a la derecha -por defecto-).
  • Hay otros dos posibles modificadores: la letra l, que indica que se va a escribir un long, y la letra h, que indica que se trata de un short.

Todo esto es para printf, pero coincide prácticamente en el caso de scanf.

Antes de seguir, vamos a ver un ejemplo con los casos más habituales...

TEMA 4.2.

Este es un ejemplo de los formatos más habituales:

  /*-------------------------*/
/*  Ejemplo en C nº 12:    */
/*  EJ12.C
*/
/*
*/
/*  Formatos con "printf"  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int   entero = 1234;
  int   enteroNeg = -1234;
  float real = 234.567;
  char  letra = 'E';
  char  mensaje[20] = "Un texto";
  int   contador;

  main()
  {
     printf("El número entero vale %d en notación decimal,\n", entero);
     printf("  y %o en notación octal,\n", entero);
     printf("  y %x en notación hexadecimal,\n", entero);
     printf("  y %X en notación hexadecimal en mayúsculas,\n", entero);
     printf("  y %ld si le hacemos que crea que es entero largo,\n", entero);
     printf("  y %10d si obligamos a una cierta anchura,\n", entero);
     printf("  y %-10d si ajustamos a la izquierda.\n", entero);
     printf("El entero negativo vale %d\n", enteroNeg);
     printf("  y podemos hacer que crea que es positivo: %u (incorrecto).\n",
       enteroNeg);
     printf("El número real vale %f en notación normal\n", real);
     printf("  y %3.2f si limitamos a dos decimales,\n", real);
     printf("  y %e en notación científica (exponencial).\n", real);
     printf("La letra es %c y el texto %s.\n", letra, mensaje);
     printf("  Podemos poner \"tanto por ciento\": 50%%.\n");
     printf("Finalmente, podemos escribir direcciones. de memoria: %p.\n",
       &letra);
     printf("  y contar lo escrito hasta aquí%n", &contador);
     printf(", que ha sido: %d letras.\n", contador);
  }

  /*-------------------------*/

Aparecen cosas nuevas, como eso de "\n", hace que avancemos una línea en la pantalla, y que veremos con más detalle en un instante, pero antes vamos a analizar el resultado de este programa con Turbo C++:

  - - - - - - - - - - - - - - - - - - - - - - - - - - -

  El número entero vale 1234 en notación decimal,
    y 2322 en notación octal,
    y 4d2 en notación hexadecimal,
    y 4D2 en notación hexadecimal en mayúsculas,
    y 1234 si le hacemos que crea que es entero largo,
    y       1234 si obligamos a una cierta anchura,
    y 1234       si ajustamos a la izquierda.
  El entero negativo vale -1234
    y podemos hacer que crea que es positivo: 64302 (incorrecto).
  El número real vale 234.567001 en notación normal
    y 234.57 si limitamos a dos decimales,
    y 2.345670e+02 en notación científica (exponencial).
  La letra es E y el texto Un texto.
    Podemos poner "tanto por ciento": 50%.
  Finalmente, podemos escribir direcciones. de memoria: 00B0.
    y contar lo escrito hasta aquí, que ha sido: 32 letras.

  - - - - - - - - - - - - - - - - - - - - - - - - - - -

Creo que queda todo bastante claro (puede volver al apartado anterior para consultar los distintos formatos si no es así), pero aun así hay una cosa desconcertante: Por qué el número real aparece como 234.567001, si nosotros lo hemos definido como 234.567? Porque los números reales se almacenan con una cierta pérdida de precisión. Si esta pérdida es demasiado grande para nosotros, deberemos usar otro tipo, como double.

Lo de que el número negativo quede mal al intentar escribirlo como positivo, lo comento de pasada para quien sepa ya algo de aritmética binaria: el primer bit a uno en un número con signo indica que es un número negativo, mientras que en uno positivo es el más significativo. Por eso, tanto el número -1234 como el 64302 se traducen en la misma secuencia de ceros y unos, que la sentencia "printf" interpreta de una forma u otra según le digamos que el número el positivo o negativo.

Y aun hay más: si lo compilamos y lo ejecutamos con Symantec C++, vemos que hay dos diferencias:

  -> Finalmente, podemos escribir direcciones. de memoria: 0068.

Esto no es problema, porque la dirección de memoria en la que el compilador nos reserve una variable no nos importa; nos basta con saber que realmente contamos con esa memoria para nuestros datos.

  -> y 182453458 si le hacemos que crea que es entero largo,

Esto ya asusta más. ¿Por qué el 1234 se ha convertido en esa cosa tan rara? Pues porque un entero largo ocupa el doble que un entero normal, así que es muy posible que si hacemos cosas como éstas, esté intentando leer 4 bytes donde nosotros sólo hemos definido 2, tomará datos que hayamos reservado para otras variables, y el resultado puede ser erróneo.

Para evitar esto, se puede hacer una "conversión de tipos" (en inglés "typecast"), para que el valor que vayamos a imprimir sea realmente un entero largo:

    printf("  y %ld si le hacemos que crea que es entero largo,\n",
      (long) entero);

Así, antes de imprimir coge nuestro valor, lo convierte realmente en un entero largo (el dato leído, porque no modifica el original) y lo muestra correctamente. Así nos aseguramos de que funcionará en cualquier compilador.

Finalmente, si lo compilamos con GCC (bajo Linux) los resultados también son "casi iguales", y esta vez las diferencias son:

   -> Finalmente, podemos escribir direcciones. de memoria: 0x2010.

Esto, como antes, no debe preocuparnos.

   -> El entero negativo vale -1234
   ->   y podemos hacer que crea que es positivo: 4294966062 (incorrecto).

Este valor es distinto del visto anteriormente simplemente porque para GCC bajo Linux, un entero ocupa 4 bytes en vez de 2. Por tanto, -1234 y 4294966062 dan lugar a la misma secuencia de ceros y unos, pero esta vez con 32 bits en vez de 16.

Este es el mismo resultado que se obtiene con Djgpp 2.01.

En el caso del compilador PCC 2.1c, obtenemos "casi" lo mismo, pero no idéntico, porque no permite algunos de los códigos de formato, como "%p" y "%n":

  - - - - - - - - - - - - - - - - - - - - - - - - - - -

  El número entero vale 1234 en notación decimal,
    y 2322 en notación octal,
    y 4D2 en notación hexadecimal,
    y FFEC04D2 en notación hexadecimal en mayúsculas,
    y -1309486 si le hacemos que crea que es entero largo,
    y       1234 si obligamos a una cierta anchura,
    y 1234       si ajustamos a la izquierda.
  El entero negativo vale -1234
    y podemos hacer que crea que es positivo: 64302 (incorrecto).
  El número real vale 234.567001 en notación normal
    y 234.57 si limitamos a dos decimales,
    y 2.345670E2 en notación científica (exponencial).
  La letra es E y el texto Un texto.
    Podemos poner "tanto por ciento": 50%.
  Finalmente, podemos escribir direcciones. de memoria: p.
    y contar lo escrito hasta aquín, que ha sido: 0 letras.

  - - - - - - - - - - - - - - - - - - - - - - - - - - -

TEMA 4.3.

Sigamos. Vamos a ver ahora qué era eso de "\n". Las letras que escribamos después de una barra invertida (en inglés, "backslash") tendrán un significado especial. Se trata simplemente de códigos de control (llamados también "secuencias de escape") que podemos meter entre medias del texto. En este caso, ese es el carácter de avance de línea (en inglés "new line"), que hace que se siga escribiendo al comienzo de la línea siguiente. Veamos en una tablita cuáles podemos usar:

  Código   Significado

    \n     nueva línea (new line / line feed)
    \r     retorno de carro (carriage return)
    \b     retroceso (backspace)
    \f     salto de página (form feed)
    \t     tabulación horizontal
    \v     tabulación vertical
    \"     comillas dobles (")
    \'     apóstrofe o comillas simples (')
    \\     barra invertida (\)
    \a     alerta (un pitido)
    \0     carácter nulo
   \ddd    constante octal (máximo tres dígitos)
   \xddd   constante hexadecimal (ídem)

Estos códigos equivalen a ciertos caracteres de control del código ASCII. Por ejemplo, el carácter de salto de página es el 12, luego sería equivalente hacer:

   char FF = 12;
/* Asignación normal, sabiendo el código */
char FF = '\f';
/* Como secuencia de escape */
char FF = '\xC';      /*
Como constante hexadecimal */
char FF = '\140;      /*
Como constante octal */

TEMA 4.4.

Para leer datos del teclado, hemos visto cómo usar "scanf". Una vez vista la base y conocidos los códigos de formato, no tiene mayor dificultad. Por ejemplo, para leer un número real, haríamos:

   scanf("%f", &real);

Podemos leer más de un dato seguido, que el usuario deje separados por espacios, usando construcciones como

   scanf("%f %f", &real1, &real2);

pero su uso es peligroso. Personalmente, yo prefiero leerlo como una cadena de texto y analizarlo yo mismo, para evitar errores. Y cómo leemos cadenas de texto, si habíamos visto que "scanf" paraba en cuanto encontraba un espacio? Vamos a verlo...

Cuando queremos trabajar con cadenas de texto, tenemos otras posibilidades: con "puts" podemos escribir un texto y con "gets" leer lo que se teclee (esta vez no se para en cuanto lea un espacio en blanco).

Va el ejemplo de turno:

  /*-------------------------*/
/*  Ejemplo en C nº 13:    */
/*  EJ13.C
*/
/*
*/
/*  "gets" y "puts"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  char texto[80];

  main()
  {
     puts("Teclee una frase.");
     gets(texto);
     puts("Ha escrito: ");
     puts(texto);
  }

  /*-------------------------*/

Al ejecutar este programa, vemos que "puts" ya avanza automáticamente de línea tras escribir el texto, sin necesidad de que pongamos un '\n' al final.

En "stdio.h" tenemos muuuuchas más funciones, pero vamos sólo a comentar dos más: "getchar()" lee un carácter y "putchar()" escribe un carácter.

En la práctica, es más habitual usar otras funciones como "getch()", pero estas dependen (en teoría) del compilador que se use, así que las veremos cuando tratemos la pantalla en modo texto.


Curso de C. Tema 5: Operaciones matemáticas.

Al igual que en Pascal, Basic y otros lenguajes, contamos con una serie de operadores para realizar sumas, restas, multiplicaciones y otras operaciones no tan habituales. Vamos a empezar por las cuatro elementales:

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

¿Qué ocurre en casos como el de 10/3? Si 10 y 3 son números enteros, ¿qué ocurre con su división? El resultado sería 3, la parte entera de la división. Será 3.3333 cuando ambos números sean reales.

  /*-------------------------*/
/*  Ejemplo en C nº 14:    */
/*  EJ14.C
*/
/*
*/
/*  Operaciones con
*/
/*  números enteros
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int e1   = 10;
  int e2   =  4;
  float r1 = 10.0;
  float r2 =  4.0;

  main()
{
printf("La suma de los enteros es: %d\n",
e1+e2);
printf("  Su producto: %d\n", e1*e2);
printf("  Su resta: %d\n", e1-e2);
printf("  Su división: %d\n",
e1/e2);
printf("  El resto de la división:
%d\n", e1%e2);
printf("La suma de los reales es: %f\n",
r1+r2);
printf("  Su producto: %f\n", r1*r2);
printf("  Su resta: %f\n", r1-r2);
printf("  Su división: %f\n",
r1/r2);
printf("Un real entre un entero, como
real: %f\n", r1/e2);
printf("  Lo mismo como entero: %d
(erróneo)\n", r1/e2);
printf("  Un entero entre un real,
como real: %f\n", e1/r2);
}

  /*-------------------------*/

Estos operadores son "binarios". Se llaman así porque trabajan con dos datos: sumamos dos números, restamos dos números, multiplicamos dos números, etc. Por otro lado, tenemos operadores "unarios" (o monarios), que sólo afectan a un dato. El ejemplo más claro es el de la negación: -a pero veremos más adelante otros (++, --, &, ...)

Cuidado quien venga de Pascal, porque en C el operador + (suma) NO se puede utilizar también para concatenar cadenas de texto. Tendremos que utilizar funciones específicas (en este caso "strcat"). Tampoco podemos asignar valores directamente, haciendo cosas como "texto2 = texto1", sino que deberemos usar otra función ("strcpy"). Todo esto lo veremos más adelante, en un tema dedicado sólo a las cadenas de texto.

En Turbo Pascal (no en cualquier versión de Pascal), tenemos también formas abreviadas de incrementar una variable:

   inc(a);        en vez de       a := a+1;

Algo parecido existe en C, aunque con otra notación:

   a++;
es lo mismo que    a = a+1;
a--;
es lo mismo que    a = a-1;

Pero esto tiene más misterio todavía del que puede parecer en un primer vistazo: podemos distinguir entre "preincremento" y "postincremento". En C es posible hacer asignaciones como

   b = a++;

Así, si "a" valía 2, lo que esta instrucción hace es dar a "b" el valor de "a" y aumentar el valor de "a". Por tanto, al final tenemos que b=2 y a=3 (postincremento: se incrementa "a" tras asignar su valor).

En cambio, si escribimos

   b = ++a;

y "a" valía 2, primero aumentamos "a" y luego los asignamos a "b" (preincremento), de modo que a=3 y b=3.

Por supuesto, también tenemos postdecremento (a--) y predecremento (--a).

Y ya que estamos embalados con las asignaciones, hay que comentar que en C es posible hacer asignaciones múltiples:

   a = b = c = 1;

Pero aún hay más. Tenemos incluso formas reducidas de escribir cosas como "a = a+5". Allá van

   a += b ;
es lo mismo que    a = a+b;
a -= b ;
es lo mismo que    a = a-b;
a *= b ;
es lo mismo que    a = a*b;
a /= b ;
es lo mismo que    a = a/b;
a %= b ;
es lo mismo que    a = a%b;

TEMA 5.2. Operadores lógicos, comparaciones.

En la próxima lección veremos cómo hacer comparaciones del estilo de "si A es mayor que B y B es mayor que C". Así que vamos a anticipar cuales son los operadores de comparación en C.
Operador | Operación
== | Igual a
!= | No igual a (distinto de)
< | Mayor o igual que
> | Mayor o igual que
<= | Mayor o igual que
>= | Mayor o igual que

Cuidado con el operador de igualdad: el formato será if (a==b) ... Si no nos damos cuenta y escribimos if (a=b) estamos asignando a "a" el valor de "b" (lo veremos con más detalle en la próxima lección).

Afortunadamente, la mayoría de los compiladores nos avisan con un mensaje parecido a "Possibly incorrect assignment" (que podríamos traducir por "posiblemente esta asignación es incorrecta") o "Possibly unintended assignment" (algo así como "es posible que no se pretendiese hacer esta asignación").

Estas condiciones se puede encadenar con "y", "o", etc., que se indican de la siguiente forma (también lo aplicaremos en la próxima lección)

  Operador Significado

     &&     Y
     ||     O
     !      No

Así, una orden que sea mezcla de C y español (porque todavía no sabemos bien cómo comprobar condiciones) y que compruebe si una variable "x" vale 2 y si otra variable "z" no es mayor de 5, sería

    Si (x==2) && !(z>5) entonces ...

TEMA 5.3. Operadores entre bits

Podemos hacer operaciones entre bits de dos números (producto, suma, suma exclusiva, etc.), que se indican:

Operador | Operación
~ | Complemento (cambiar 0 por 1 y viceversa)
& | Producto lógico (and)
| | Suma lógica (or)
^ | Suma exclusiva (xor)
<< | Desplazamiento hacia la izquierda
>> | Desplazamiento a la derecha

Explicar para qué sirven estos operadores implica conocer qué es eso de los bits, cómo se pasa un número decimal a binario, etc. Supondré que se tienen las nociones básicas, y pondré un ejemplo, cuyo resultado comentaré después:

  /*-------------------------*/
/*  Ejemplo en C nº 14b:   */
/*  EJ14B.C
*/
/*
*/
/*  Operaciones de bits    */
/*  en números enteros
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int a   = 67;
  int b   =  33;

  main()
{
printf("La variable a vale %d\n", a);
printf("y b vale %d\n", b);
printf("  El complemento de a es:
%d\n", ~a);
printf("  El producto lógico
de a y b es: %d\n", a&b);
printf("  Su suma lógica es:
%d\n", a|b);
printf("  Su suma lógica exclusiva
es: %d\n", a^b);
printf("  Desplacemos a a la izquierda:
%d\n", a << 1);
printf("  Desplacemos a a la derecha:
%d\n", a >> 1);
}

  /*-------------------------*/

Veamos qué ha ocurrido. La respuesta que nos da Turbo C++ 1.01 (y que coincide con Symantec C++ 6.0) es la siguiente:

  - - - - - - - - - - - - - - - - - - - - - - - - - - -
  La variable a vale 67
  y b vale 33
    El complemento de a es: -68
    El producto lógico de a y b es: 1
    Su suma lógica es: 99
    Su suma lógica exclusiva es: 98
    Desplacemos a a la izquierda: 134
    Desplacemos a a la derecha: 33
  - - - - - - - - - - - - - - - - - - - - - - - - - - -

Para entender esto, deberemos convertir al sistema binario esos dos números:

   67 = 0100 0011
   33 = 0010 0001

- En primer lugar complementamos "a", cambiando los ceros por unos:

    1011 1100 = -68

- Después hacemos el producto lógico de A y B, multiplicando cada bit,
de modo que 1*1 = 1, 1*0 = 0, 0*0 = 0

    0000 0001 = 1

- Después hacemos su suma lógica, sumando cada bit, de modo que
1+1 = 1, 1+0 = 1, 0+0 = 0

    0110 0011 = 99

- La suma lógica exclusiva devuelve un 1 cuando los dos bits son distintos:
1^1 = 0, 1^0 = 1, 0^0 = 0

    0110 0010 = 98

- Desplazar los bits una posición a la izquierda es como multiplicar por dos:

    1000 0110 = 134

- Desplazar los bits una posición a la derecha es como dividir entre dos:

    0010 0001 = 33

¿Y qué utilidades puede tener todo esto? Posiblemente, más de las que parece a primera vista. Por ejemplo: desplazar a la izquierda es una forma muy rápida de multiplicar por potencias de dos; desplazar a la derecha es dividir por potencias de dos; la suma lógica exclusiva (xor) es un método rápido y sencillo de cifrar mensajes; el producto lógico nos permite obligar a que ciertos bits sean 0; la suma lógica, por el contrario, puede servir para obligar a que ciertos bits sean 1...

Un último comentario: igual que hacíamos operaciones abreviadas como

   x += 2;

también podremos hacer cosas como

   x <<= 2;
   x &= 2;
   x |= 2;
...

Pasemos ya al tema siguiente, para ver cómo se comprueban condiciones...


Curso de C. Tema 6: Condiciones.

Vamos a ver cómo podemos evaluar condiciones. Supongo que está memorizado del tema anterior cómo se expresa "mayor o igual", "distinto de", y todas esas cosas.

La primera construcción sería el "si ... entonces ..." (if..then en Pascal y otros lenguajes). El formato en C es

   if (condición) sentencia;

Vamos a verlo con un ejemplo:

  /*-------------------------*/
/*  Ejemplo en C nº 15:    */
/*  EJ15.C
*/
/*
*/
/*  "if" elemental
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero;

  main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero>0)  printf("El número
es positivo.\n");
}

  /*-------------------------*/

Todo claro, verdad? Pero (como suele ocurrir en C) esto tiene más miga de la que parece: recordemos que en C no existen los números booleanos, luego la condición no puede valer "verdadero" o "falso". Dijimos que "falso" iba a corresponder a un 0, y "verdadero" a un número distinto de cero (normalmente uno).

Por tanto, si escribimos el número 12 y le pedimos que escriba el valor de la comparación (numero>0), un compilador de Pascal escribiría TRUE, mientras que uno de C escribiría 1.

TEMA 6.2.

Y aún hay más: como un valor de "verdadero" equivale a uno distinto de cero, podemos hacer cosas como ésta:

  /*-------------------------*/
/*  Ejemplo en C nº 16:    */
/*  EJ16.C
*/
/*
*/
/*  "if" abreviado
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero;

  main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero != 0)
/* Forma "normal" */
printf("El número
no es cero.\n");
if (numero)
/* Forma "con truco" */
printf("Y sigue sin
serlo.\n");
}

  /*-------------------------*/

TEMA 6.3.

La "sentencia" que se ejecuta si se cumple la condición puede ser una sentencia simple o una compuesta. Las compuestas se forman agrupando varias simples entre llaves ( { y } ):

  /*-------------------------*/
/*  Ejemplo en C nº 17:    */
/*  EJ17.C
*/
/*
*/
/*  "if" y sentencias
*/
/*  compuestas
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero;

  main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero<0)
{
printf("El número es
");
printf("negativo.\n");
}
}

  /*-------------------------*/

En este caso, si el número es negativo, se hacen dos cosas: escribir un un texto y luego... escribir otro! Claramente, esos dos "printf" podrían ser uno solo, pero es que entonces me quedaría sin ejemplo.

TEMA 6.4.

Sigamos... También podemos indicar lo que queremos que se haga si no se cumple la condición. Para ello tenemos la construcción "if (condición) sentencia1; else sentencia2;":

  /*-------------------------*/
/*  Ejemplo en C nº 18:    */
/*  EJ18.C
*/
/*
*/
/*  "if" y "else"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero;

  main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero<0)
printf("El número
es negativo.\n");
else
printf("El número
es positivo o cero.\n");
}

  /*-------------------------*/

Nota para Pascal: En C sí se debe poner un punto y coma antes de "else", al contrario que en Pascal.

TEMA 6.5.

Las sentencias "if (condición) s1; else s2;" se pueden encadenar:

  /*-------------------------*/
/*  Ejemplo en C nº 19:    */
/*  EJ19.C
*/
/*
*/
/*  "if..else" encadenados */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero;

  main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero < 0)
printf("El número
es negativo.\n");
else if (numero == 0)
printf("El número
es cero.\n");
else
printf("El número
es positivo.\n");
}

  /*-------------------------*/

Atención en el ejemplo anterior al operador de comparación == para ver si dos valores son iguales. Insisto en que "if (numero = 0)" daría a número el valor 0. Ahora que sabemos más, nos damos cuenta de que además esto haría que tomase la condición como falsa. Para que se vea mejor, no hay más que teclear 0 como entrada a este programa:

TEMA 6.6.

/*-------------------------*/
/*  Ejemplo en C nº 20:    */
/*  EJ20.C
*/
/*
*/
/*  "if" incorrecto
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero;

  main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero < 0)
printf("El número
es negativo.\n");
else if (numero = 0)
/* Error: asignación */
printf("El número
es cero.\n");
else
printf("El número
es positivo.\n");
}

  /*-------------------------*/

Y si esto es un error, ¿por qué el compilador "avisa" en vez de parar y dar un error "serio"? Pues porque no tiene por qué ser necesariamente un error: podemos hacer

   a = b
   if (a > 2) ...

o bien

   if ((a=b) > 2) ...

Es decir, en la misma orden asignamos el valor y comparamos (parecido a lo que hacíamos con "b = ++a", por ejemplo).

Como ya hemos comentado en el apartado anterior, puede darse el caso de que tengamos que comprobar varias condiciones simultáneas, y entonces deberemos usar "y" (&&), "o" (||) y "no" (!) para enlazarlas:

   if ((opcion==1) && (usuario==2))  ...
if ((opcion==1) || (opcion==3))   ...
if ((!(opcion==opcCorrecta)) || (tecla==kESC))
...

TEMA 6.7.

En C hay otra forma de asignar un valor según se dé una condición o no. Es el "operador condicional" ? : que se usa

   condicion ?  v1 : v2;

y es algo así como "si se da la condición, toma el valor v1; si no, toma el valor v2". Un ejemplo de cómo podríamos usarlo sería

   resultado = (operacion == '-') ?  a-b : a+b;

que, aplicado a un programita quedaría:

  /*-------------------------*/
/*  Ejemplo en C nº 21:    */
/*  EJ21.C
*/
/*
*/
/*  Operador condicional ? */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int a, b, resultado;
  int operacion;

  main()
{
printf("Escriba un número: ");
scanf("%d", &a);
printf("Escriba otro: ");
scanf("%d", &b);
printf("Escriba una operación (1
= resta; otro = suma): ");
scanf("%d", &operacion);
resultado = (operacion == 1) ?  a-b
: a+b;
printf("El resultado es %d.\n", resultado);
}

  /*-------------------------*/

(Recordemos que en C las expresiones lógicas valían cero -falso- o distinto de cero -verdadero-, de modo que eso que he llamado "condición" puede ser realmente otros tipos de expresiones, como por ejemplo una operación aritmética).

Una nota sobre este programa: alguien avispado puede haberse dado cuenta de que en el ejemplo comparo con el símbolo '-' y en cambio en el programa comparo con 1. ¿Y este cambio de actitud? ¿No se podría haber usado scanf("%c",...) o bien "getchar()" para leer si se pulsa la tecla '-'?

Pues no es tan sencillo, desafortunadamente. El motivo es que cuando pulsamos INTRO tras teclear un número, esta pulsación se queda en el buffer del teclado, y eso es lo que leería el getchar.

TEMA 6.8.

Vamos a verlo con un ejemplo:

  /*-------------------------*/
/*  Ejemplo en C nº 22:    */
/*  EJ22.C
*/
/*
*/
/*  Problemas de "getchar" */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int a, b, resultado;
  char operacion;

  main()
{
printf("Escriba un número: ");
scanf("%d", &a);
printf("Escriba otro: ");
scanf("%d", &b);
printf("Escriba una operación (1
= resta; otro = suma): ");
operacion = getchar();
/* Da resultado inesperado */
printf("\nLa operación es %c (%d).\n",
resultado, resultado);
resultado = (operacion == '-') ?
a-b : a+b;
printf("Y el resultado es %d.\n", resultado);
}

  /*-------------------------*/

Leyéndolo, parece que todo debería salir bien, pero al ejecutarlo no nos deja ni teclear el símbolo, sino que toma el valor que hubiera en el buffer del teclado. Obtenemos el mismo resultado con

    scanf("%c", &operacion);

Por tanto, deberíamos vaciar primero el buffer del teclado, usando órdenes como “gets”. Otra opción mejor es utilizar “getch” en vez de “getchar”, si nuestro compilador lo permite (veremos su manejo en la Ampliación 1).

TEMA 6.9.

Finalmente, cuando queremos ver varios posibles valores, sería muy pesado tener que hacerlo con muchos "if" seguidos o encadenados. La alternativa es la orden "switch", cuya sintaxis es

  switch (expresión)
  {
     case caso1: sentencia1;
            break;
     case caso2: sentencia2;
            sentencia2b;
            break;
      ...
     case casoN: sentenciaN;
            break;
    default:
            otraSentencia;
  };

Nota para Pascal: la construcción equivalente sería
  case expresión of
    caso1: sentencia1;
    caso2: begin sentencia2; sentencia2b; end;
    ...
    casoN: sentenciaN;
  else
    otraSentencia;
  end;

Como la mejor forma de verlo es con un ejemplo, vamos allá:

  /*-------------------------*/
/*  Ejemplo en C nº 23:    */
/*  EJ23.C
*/
/*
*/
/*  Uso de "switch"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  char tecla;

  main()
  {
     printf("Pulse una tecla y luego Intro: ");
     tecla = getchar();
     switch (tecla)
     {
       case ' ': printf("Espacio.\n");
                 break;
       case '1':
       case '2':
       case '3':
       case '4':
       case '5':
       case '6':
       case '7':
       case '8':
       case '9':
       case '0': printf("Dígito.\n");
                 break;
       default:  printf("Ni espacio ni dígito.\n");
     }
  }

  /*-------------------------*/

Comentarios generales

  • En primer lugar, insisto en que getchar() es lo mismo que scanf("%c",...). ¿Por qué lo digo? Porque no basta con escribir la letra: la letra se leerá realmente cuando se procese todo el buffer del teclado, es decir, que debemos pulsar Intro después de la tecla que elijamos.
  • Otra cosa: no se pueden definir subrangos de la forma '0'..'9' ("desde 0 hasta 9", cosa que sí ocurre en Pascal), sino que debemos enumerar todos los casos. Pero, como se ve en el ejemplo, para los casos repetitivos no hace falta repetir las sentencias a ejecutar para cada uno, porque cada opción se analiza hasta que aparece la palabra "break". Por eso, las opciones '1' hasta '0' hacen lo mismo (todas terminan en el "break" que sigue a '0').
  • Finalmente, "default" indica la acción a realizar si no es ninguno de los casos que se han detallado anteriormente. Esta parte es opcional (si no ponemos la parte de "default", simplemente se sale del "switch" sin hacer nada cuando la opción no sea ninguna de las indicadas). Después de "default" no hace falta poner "break", porque se sabe que ahí acaba la sentencia "switch".


Curso de C. Tema 7: Bucles.

Vamos a ver cómo podemos crear partes del programa que se repitan un cierto número de veces (bucles).

Según cómo queramos que se controle ese bucle, tenemos tres posibilidades, que básicamene se pueden describir como:

  • for: La orden se repite desde que una variable tiene un valor inicial hasta que alcanza otro valor final.
  • while: Repite una sentencia mientras que sea cierta la condición que indicamos. La condición se comprueba antes de realizar la sentencia.
  • do..while: Igual, pero la condición se comprueba después de realizar la sentencia.

Las diferencias son: "for" normalmente se usa para algo que se repite un número concreto de veces, mientras que "while" se asa en comprobar si una condición es cierta (se repetirá un número indeterminado de veces).

For

El formato de "for" es

   for (valorInic; CondicRepetic; Incremento)
     Sentencia;

Así, para contar del 1 al 10, tendríamos 1 como valor inicial, <=10 como condición de repetición, y el incremento sería de 1 en 1. Por tanto, el programa quedaría:

  /*-------------------------*/
/*  Ejemplo en C nº 24:    */
/*  EJ24.C
*/
/*
*/
/*  Escribe del 1 al 10,   */
/*  con "for".
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int contador;

  main()
  {
     for (contador=1; contador<=10; contador++)
       printf("%d ", contador);
  }

  /*-------------------------*/

Recordemos que "contador++" es una forma abreviada de escribir "contador=contador+1", con lo que aumentamos la variable de uno en uno.

Realmente, la parte de la orden que he llamado "Incremento" no tiene por qué incrementar la variable, aunque ése es su uso más habitual. Es simplemente una orden que se ejecuta cuando se termine la "Sentencia" y antes de volver a comprobar si todavía se cumple la condición de repetición. Por eso, si escribimos la siguiente línea:

     for (contador=1; contador<=10; )

la variable "contador" no se incrementa nunca, por lo que nunca se cumplirá la condición de salida: nos quedamos encerrados dando vueltas dentro de la orden que siga al "for".

Un caso todavía más exagerado de algo a lo que se entra y de lo que no se sale sería la siguiente orden:

     for ( ; ; )

TEMA 7.2.

Los bucles "for" se pueden anidar (incluir uno dentro de otro), de modo que podríamos escribir las tablas de multiplicar del 1 al 5 con:

  /*-------------------------*/
/*  Ejemplo en C nº 25:    */
/*  EJ25.C
*/
/*
*/
/*  Escribe las tablas de  */
/*  multiplicar del 1 al   */
/*  5, con "for".
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int tabla, numero;

  main()
{
for (tabla=1; tabla<=5; tabla++)
for (numero=1; numero<=10;
numero++)
printf("%d
por %d es %d\n", tabla, numero, tabla*numero);
}

  /*-------------------------*/

TEMA 7.3.

En estos ejemplos que hemos visto, después de "for" había un única sentencia. Si queremos que se hagan varias cosas, basta definirlas como un bloque (una sentencia compuesta) encerrándolas entre llaves. Por ejemplo, si queremos mejorar el ejemplo anterior haciendo que deje una línea en blanco entre tabla y tabla, sería:

  /*-------------------------*/
/*  Ejemplo en C nº 26:    */
/*  EJ26.C
*/
/*
*/
/*  Escribe las tablas de  */
/*  multiplicar del 1 al   */
/*  5, con "for". Deja     */
/*  espacios intermedios.  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int tabla, numero;

  main()
{
for (tabla=1; tabla<=5; tabla++)
{
for (numero=1; numero<=10;
numero++)
printf("%d
por %d es %d\n", tabla, numero, tabla*numero);
printf("\n");
}
}

  /*-------------------------*/

TEMA 7.4.

Al igual que en Pascal, para "contar" no necesariamente hay que usar números. Por ejemplo, podemos contar con letras así:

  /*-------------------------*/
/*  Ejemplo en C nº 27:    */
/*  EJ27.C
*/
/*
*/
/*  Escribe las letras de  */
/*  la 'a' a la 'z', con   */
/*  "for".
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  char letra;

  main()
  {
     for (letra='a'; letra<='z'; letra++)
        printf("%c ", letra);
  }

  /*-------------------------*/

Creo que este programa se explica por sí solo: empezamos en la "a" y terminamos en la "z", aumentando de uno en uno.

TEMA 7.5.

Si queremos contar de forma decreciente, o de dos en dos, o como nos interese, basta indicarlo en la condición de finalización del "for" y en la parte que lo incrementa:

  /*-------------------------*/
/*  Ejemplo en C nº 28:    */
/*  EJ28.C
*/
/*
*/
/*  Escribe las letras de  */
/*  la 'z' a la 'a', (una  */
/*  sí y otra no), con
*/
/*  "for".
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  char letra;

  main()
  {
     for (letra='z'; letra>='a'; letra-=2)
        printf("%c ", letra);
  }

  /*-------------------------*/

Estos son los casos sencillos de la orden "for", pero también podemos rizar el rizo, y usar dos variables (o más) para controlarlo, o salir de un bucle "for" con otras órdenes, como "break", "continue" y "goto", como veremos a continuación...

TEMA 7.5b.

Un bucle "for" puede estar controlado por dos variables:

  /*-------------------------*/
/*  Ejemplo en C nº 28b:   */
/*  EJ28B.C
*/
/*
*/
/*  For controlado con dos */
/*  variables.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int i, j;

  main()
{
for (i=0,j=1; i<=5, j<=30; i++,
j+=2)
printf("i vale %d y j vale
%d.\n", i, j);
}

  /*-------------------------*/

Vamos a ver qué hace este "for":

  • Los valores iniciales son i=0, j=1.
  • Se repetirá mientras que i <= 5, j <= 30.
  • Al final de cada paso, i aumenta en una unidad, y j en dos unidades.

El único problema está en saber cúando terminará el bucle: si se parará en cuanto se cumpla una de las dos condiciones o si tendrán que cumplirse las dos.

El resultado de este programa servirá como respuesta:

  - - - - - - - - - - - - - - - - -
  i vale 0 y j vale 1.
  i vale 1 y j vale 3.
  i vale 2 y j vale 5.
  i vale 3 y j vale 7.
  i vale 4 y j vale 9.
  i vale 5 y j vale 11.
  i vale 6 y j vale 13.
  i vale 7 y j vale 15.
  i vale 8 y j vale 17.
  i vale 9 y j vale 19.
  i vale 10 y j vale 21.
  i vale 11 y j vale 23.
  i vale 12 y j vale 25.
  i vale 13 y j vale 27.
  i vale 14 y j vale 29.
  - - - - - - - - - - - - - - - - -

Como podemos observar, llega un momento en que deja de cumplise que i<=5, pero el programa sigue avanzando: no se sale del bucle "for" hasta que se cumplen las dos condiciones.

TEMA 7.5c.

Podemos saltar alguna repetición de un bucle "for" con la orden "continue":

  /*-------------------------*/
/*  Ejemplo en C nº 28c:   */
/*  EJ28C.C
*/
/*
*/
/*  Saltar en un "for" con */
/*  "continue".
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int i;

  main()
  {
     for (i=0; i<=10; i++)
       {
       if (i==5) continue;
       printf("%d ", i);
       }
  }

  /*-------------------------*/

El resultado de este programa es:

  - - - - - - - - - - - - - - - - -
  0 1 2 3 4 6 7 8 9 10
  - - - - - - - - - - - - - - - - -

En él podemos observar que no aparece el valor 5.

TEMA 7.5d.

Podemos salir de un bucle "for" con la orden "break":

  /*-------------------------*/
/*  Ejemplo en C nº 28d:   */
/*  EJ28D.C
*/
/*
*/
/*  Salir de un "for" con  */
/*  "break".
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int i;

  main()
  {
     for (i=0; i<=10; i++)
       {
       if (i==5) break;
       printf("%d ", i);
       }
  }

  /*-------------------------*/

El resultado de este programa es:

  - - - - - - - - - - - - - - - - -
  0 1 2 3 4
  - - - - - - - - - - - - - - - - -

En cuanto se llega al valor 5, se interrumpe el "for".

TEMA 7.5e.

El lenguaje C también permite la orden "goto", para hacer saltos incondicionales. Su uso indisciplinado está muy mal visto, porque puede ayudar a hacer programas llenos de saltos, difíciles de seguir. Pero en casos concretos puede ser muy útil, por ejemplo, para salir de un bucle muy anidado (porque en este caso, "break" sólo saldría del "for" más interno).

El formato de "goto" es

   goto donde;

y la posición de salto se indica con su nombre seguido de dos puntos (:)

   donde:

como en el siguiente ejemplo:

  /*-------------------------*/
/*  Ejemplo en C nº 28d:   */
/*  EJ28E.C
*/
/*
*/
/*  "for" y "goto"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int i, j;

  main()
{
for (i=0; i<=5; i++)
for (j=0; j<=20; j+=2)
{
if ((i==1) &&
(j>=7)) goto salida;
printf("i vale
%d y j vale %d.\n", i, j);
}
salida:
printf("Fin del programa\n");
}

  /*-------------------------*/

El resultado de este programa es:

  - - - - - - - - - - - - - - - - -
  i vale 0 y j vale 0.
  i vale 0 y j vale 2.
  i vale 0 y j vale 4.
  i vale 0 y j vale 6.
  i vale 0 y j vale 8.
  i vale 0 y j vale 10.
  i vale 0 y j vale 12.
  i vale 0 y j vale 14.
  i vale 0 y j vale 16.
  i vale 0 y j vale 18.
  i vale 0 y j vale 20.
  i vale 1 y j vale 0.
  i vale 1 y j vale 2.
  i vale 1 y j vale 4.
  i vale 1 y j vale 6.
  Fin del programa
  - - - - - - - - - - - - - - - - -

Vemos que cuando i=1 y j>=7 (cuando va a valer 8), se sale de los dos "for".

TEMA 7.6. While.

Habíamos comentado que "while" podía aparecer en dos tipos de construcciones, según comprobemos la condición al principio o al final.

En el primer caso, su sintaxis es

   while (condición)
     sentencia;

Es decir, la sentencia se repetirá mientras la condición se cierta. Si la condición es falsa ya desde un principio, la sentencia no se ejecuta nunca. Si queremos que se repita más de una sentencia, basta agruparlas entre { y }.

Un ejemplo que nos diga el triple de cada número que tecleemos, y que pare cuando tecleemos el número 0, podría ser:

  /*-------------------------*/
/*  Ejemplo en C nº 29:    */
/*  EJ29.C
*/
/*
*/
/*  Escribe el triple de   */
/*  los números tecleados, */
/*  con "while"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int numero;
  char frase[60];

  main()
{
printf("Teclea un número (0 para
salir): ");
scanf("%d", &numero);
while (numero)
{
printf("Su triple es
%d.\n", numero*3);
printf("Teclea otro
número (0 para salir): ");
scanf("%d", &numero);
}
}

  /*-------------------------*/

¿Y eso de "while (numero)"? Pues es lo mismo que "while (numero != 0)". En caso de duda, habrá que repasar el tema anterior.

En este ejemplo, si se introduce 0 la primera vez, la condición es falsa y ni siquiera se entra al bloque del "while", terminando el programa inmediatamente.

TEMA 7.7.

Como ejemplo de la otra aplicación (la condición se comprueba al final, con do..while), vamos a ver cómo sería la típica clave de acceso, pero con una pequeña diferencia: como todavía no sabemos manejar cadenas de texto con una cierta soltura, la clave será un número:

  /*-------------------------*/
/*  Ejemplo en C nº 30:    */
/*  EJ30.C
*/
/*
*/
/*  Clave de acceso numé-  */
/*  rica, con "do..while"  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  int valida = 711;
  int clave;

  main()
{
do
{
printf("Introduzca su
clave numérica: ");
scanf("%d", &clave);
if (clave != valida)
printf("No válida!\n");
}
while (clave != valida);
printf("Aceptada.\n");
}

  /*-------------------------*/

En este caso, se comprueba la condición al final, de modo que se nos preguntará la clave al menos una vez. Mientras que la respuesta que demos no sea la correcta, se nos vuelve a preguntar. Finalmente, cuando tecleamos la clave correcta, el ordenador escribe "Aceptada" y termina el programa.


Curso de C. Tema 8: Definición de constantes y tipos.

Tema 8.1. Definición de constantes.

Cuando desarrollamos un programa, nos podemos encontrar con que hay variables que realmente "no varían" a lo largo de la ejecución de un programa, sino que su valor es constante.

Hay una manera especial de definirlas, que es con el especificador "const", que tiene el formato

  const Nombre = Valor;

Así, en el ejemplo anterior (la clave de acceso numérica) habría sido más correcto hacer

  /*-------------------------*/
/*  Ejemplo en C nº 31:    */
/*  EJ31.C
*/
/*
*/
/*  Clave de acceso numé-  */
/*  rica, con "do..while"  */
/*  y "const"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  const valida = 711;
  int clave;

  main()
{
do
{
printf("Introduzca su
clave numérica: ");
scanf("%d", &clave);
if (clave != valida)
printf("No válida!\n");
}
while (clave != valida);
printf("Aceptada.\n");
}

  /*-------------------------*/

El ejemplo lo he dejado así para que resulte más familiar a quien venga de Pascal, pero realmente en C la palabra "const" es un modificador, algo que da información extra, de modo que su uso habitual sería:

   const int valida = 711;

que se leería algo parecido a "La variable 'válida' es un entero, de valor 711, y este valor deberá permanecer constante".

Cuando tenemos varias constantes, cuyos valores son números enteros, y especialmente si son números enteros consecutivos, tenemos una forma abreviada de definirlos. Se trata de enumerarlos:

    enum diasSemana { LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO,
      DOMINGO };

(Las he escrito en mayúsculas para que se sepa "de un vistazo" que son constantes, no variables. Más adelante comentaré algo más sobre este convenio.)

La primera constante valdrá 0, y las demás irán aumentando de una en una, de modo que en nuestro caso valen:

   LUNES = 0, MARTES = 1, MIERCOLES = 2, JUEVES = 3, VIERNES = 4,
   SABADO = 5, DOMINGO = 6

Si queremos que los valores no sean exactamente estos, podemos dar valor a cualquiera de las contantes, y las siguientes irán aumentando de uno en uno. Por ejemplo, si escribimos

    enum diasSemana { LUNES=1, MARTES, MIERCOLES, JUEVES=6, VIERNES,
      SABADO=10, DOMINGO };

Ahora sus valores son:

   LUNES = 1, MARTES = 2, MIERCOLES = 3, JUEVES = 6, VIERNES = 7,
   SABADO = 10, DOMINGO = 11

En C tenemos 3 tipos de variables, según si su valor se puede modificar o no, y de qué forma:

  • Las que habíamos visto hasta ahora, a las que podíamos asignar un valor.
  • Las que definimos y no permitiremos que se altere su valor, para lo que usamos el modificador "const".
  • Las que pueden cambiar en cualquier momento, y para ellas usaremos el modificador "volatile". Es para el caso (poco habitual) de que su valor pueda ser cambiado por algo que no sea nuestro programa principal, y así obligamos al compilador en este caso a que lea el valor de la variable en memoria, en vez de mirarlo en algún registro temporal en el que lo pudiera haber guardado para mayor velocidad.

TEMA 8.2.

Hay otra forma muy frecuente en C de definir constantes, que es con la directiva #define. Al tratarse de una directiva del preprocesador (ver tema 3), es una información que no llega al compilador cuando tiene que traducir a código máquina, sino que ya en una primera pasada se cambia su nombre por el valor correspondiente en cada punto en que aparezca.

Así, algo como

   #define Valida 711
   if (numero == Valida)  [...]

se convertiría inmediatamente a

   if (numero == 711)  [...]

que es lo que realmente el compilador traduciría a código máquina. Por eso es frecuente llamarlas "constantes simbólicas".

Así, este último ejemplo quedaría

  /*-------------------------*/
/*  Ejemplo en C nº 32:    */
/*  EJ32.C
*/
/*
*/
/*  Clave de acceso numé-  */
/*  rica, con "do..while"  */
/*  y "#define"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  #define kVALIDA 711

  int clave;

  main()
{
do
{
printf("Introduzca su
clave numérica: ");
scanf("%d", &clave);
if (clave != kVALIDA)
printf("No válida!\n");
}
while (clave != kVALIDA);
printf("Aceptada.\n");
}

  /*-------------------------*/

¿Y por qué lo he puesto en mayúsculas y precedido por una k?

Simplemente por legibilidad: en C es inmediato diferenciar una variable de una función, porque, como ya hemos mencionado y veremos de nuevo en el próximo tema, una función necesariamente acaba con paréntesis, incluso aunque no tenga parámetros; en cambio, no hay nada que distinga "a simple vista" una constante de una variable. Por eso, es frecuente "obligar" a que sea fácil distinguir las constantes de las variables sólo con ojear el listado, y las formas más habituales son escribir las variables en minúsculas y las constantes en mayúsculas, o bien comenzar las constantes con una "k", o bien ambas cosas.

TEMA 8.3. Definición de tipos.

El tipo de una variable nos indica el rango de valores que puede tomar. Tenemos creados para nosotros los tipos básicos, pero puede que nos interese crear nuestros propios tipos de variables, para lo que usamos "typedef".

El formato es "typedef tipo nombre;" y realmente lo que hacemos es darle un nuevo nombre, lo que nos puede resultar útil por ejemplo si venimos de Pascal (cómo no) y consideramos más legible tener un tipo "boolean". Vamos a verlo directamente con un ejemplo:

  /*-------------------------*/
/*  Ejemplo en C nº 33:    */
/*  EJ33.C
*/
/*
*/
/*  Clave de acceso numé-  */
/*  rica retocada, con     */
/*  typedef y #define
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  #define VALIDA 711          /* Clave correcta */

  #define TRUE 1
/* Nostalgia de los boolean */
#define FALSE 0

  typedef int boolean;
/* Definimos un par de tipos */
typedef int integer;

  integer clave;
/* Y dos variables */
boolean acertado;

  main()
{
do
{
printf("Introduzca su
clave numérica: ");
scanf("%d", &clave);
acertado = (clave ==
VALIDA);
if (acertado == FALSE)
printf("No válida!\n");
}
while (acertado != TRUE);
printf("Aceptada.\n");
}

  /*-------------------------*/

Todo esto se puede hacer más cortito, claro, pero es para que se vea un ejemplo de su uso.

TEMA 8.4.

También podemos usar "typedef" para dar un nombre corto a todo un struct:

  /*-------------------------*/
/*  Ejemplo en C nº 34:    */
/*  EJ34.C
*/
/*
*/
/*  Uso de "typedef" con   */
/*  "struct".
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  typedef struct {
/* Defino el tipo "datos" */
int valor;
float coste;
char ref;
} datos;

  datos ejemplo;           /* Y creo una variable de ese tipo */

  main()
  {
     ejemplo.valor = 34;
     ejemplo.coste = 1200.5;
     ejemplo.ref = 'A';
     printf("El valor es %d", ejemplo.valor);
  }

  /*-------------------------*/


Curso de C. Tema 9: Programación estructurada. Funciones.

La programación estructurada trata de dividir el programa el bloques más pequeños, buscando una mayor legibilidad, y más comodidad a la hora de corregir o ampliar.

La idea es que un programa que cumpla una cierta misión tendrá que seguir ciertos pasos. Por ejemplo: para resolver un sistema de ecuaciones, los pasos podrían ser:

   Pedir Número de Ecuaciones y de Incógnitas.
   Para cada ecuación:
     - Pedir coeficientes de x(i).
     - Pedir término independiente.
   Escribir el sistema como matriz.
   Hacer ceros bajo la diagonal principal.
   Aplicar sustitución regresiva.
   Mostrar resultados.

Esos son los pasos que uno piensa cuando se plantea cómo resolver el problema (el "algoritmo"). Nosotros pensamos en "cosas a hacer", no en líneas de programa, por eso la forma más "natural" de trabajar es descomponer el problema en partes, en vez de intentar resolverlo tecleando una línea tras otra.

Es la clásica idea del "divide y vencerás": un programa que hayamos dividido en bloques (con una cierta conexión, no a lo loco) será más fácil de crear y de mantener.

En muchos lenguajes de programación (como Pascal o Basic), estos bloques son de dos tipos: procedimientos ("procedure") y funciones ("function").

La diferencia entre ellos es que un procedimiento ejecuta una serie de acciones que están relacionadas entre sí, y no devuelve ningún valor, mientras que la función sí que va a devolver valores.

Por ejemplo, un procedimiento podría saludarnos: da unos ciertos pasos, muestra un mensaje en pantalla y ya está.

    Saluda;

Por el contrario una función podría ser la raíz cuadrada: da una serie de pasos, destinados a calcular cual es el valor, pero finalmente tiene que respondernos diciendo qué valor ha obtenido.

    x = Raiz(9);      (y el ordenador debe "devolverme" el número: 3)

En C sólo existen funciones, pero éstas pueden devolver unos valores de tipo "nulo", con lo cual salen a equivaler a un procedimiento (lo veremos algo más adelante).

Vamos a verlo mejor con un par de ejemplos.

Primero crearemos una función "potencia", que eleve un número entero a otro número entero, dando también como resultado un número entero. La forma de conseguir elevar un número a otro será a base de multiplicaciones, es decir:

   3 elevado a 5   =   3 · 3 · 3 · 3 · 3

(multiplicamos 5 veces el 3 por sí mismo). En general, como nos pueden pedir cosas como "6 elevado a 100", usaremos la orden "for" para multiplicar tantas veces como haga falta:

  /*-------------------------*/
/*  Ejemplo en C nº 35:    */
/*  EJ35.C
*/
/*
*/
/*  Función "potencia"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  int potencia(int base, int exponente)
{
int temporal = 1;
/* Valor que voy hallando */
int i;
/* Para bucles */

     for(i=1; i<=exponente; i++)
/* Multiplico "n" veces */
temporal *= base;
/* Y calculo el valor temporal */

     return temporal;
/* Al final de las multiplicaciones, */
}
/*    obtengo el valor que buscaba */

  main()
  {
     int num1, num2;

     printf("Introduzca la base: ");
scanf("%d", &num1);
printf("Introduzca el exponente: ");
scanf("%d", &num2);
printf("%d elevado a %d vale %d", num1,
num2, potencia(num1,num2));
}

  /*-------------------------*/

Vamos a comentar cosas sobre este programa:

  • base y exponente son dos valores que se "le pasan" a la función: son sus "parámetros", unos datos que la función necesita. En nuestro caso, queremos elevar un número a otro, luego deberemos indicarle cual es el número que queremos elevar (la base) y a qué número lo queremos elevar (el exponente).
  • El cuerpo de la función se indica entre llaves, como cualquier otro bloque de un programa.
  • i y temporal son variables locales a la función "potencia": no se puede acceder a ellas desde ninguna otra parte del programa. Igual ocurre con num1 y num2: sólo se pueden leer o modificar desde "main". Las variables que habíamos visto hasta ahora no estaban dentro de ninguna función, por lo que eran globales a todo el programa. Una detalle importante: las variables locales se deberán declarar al principio de la función, antes de que aparezca ninguna orden.
  • Con "return" salimos de la función e indicamos el valor que queremos que se devuelva. En este caso es lo que habíamos llamado "temporal", que era el valor que íbamos calculando para la potencia en cada paso.
  • Recordemos que los "int" en MsDos están limitados a 32767, luego si el resultado es mayor, obtendremos valores erróneos. Esto se puede evitar usando enteros largos o números reales.

TEMA 9.2.

Ahora vamos a ver un ejemplo de función que no devuelva ningún valor:

  /*-------------------------*/
/*  Ejemplo en C nº 36:    */
/*  EJ36.C
*/
/*
*/
/*  Función "hola"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  void saludo(char nombre[])
  {
     printf("Hola, %s, cómo estás?", nombre);
  }

  main()
  {
     saludo("Eva");
     printf("\n");
  }

  /*-------------------------*/

Y los comentarios de rigor:

  • Esta función es de tipo "void" (nulo), con lo que indicamos que no queremos que devuelva ningún valor.
  • Con eso de "char nombre[]" indicamos que le vamos a pasar una cadena de caracteres, pero no hace falta que digamos su tamaño, como hacíamos cuando las declarábamos. Así podremos pasar cadenas de caracteres tan grandes (o tan pequeñas) como queramos.
  • Esta función no termina en "return", porque no queremos que devuelva ningún valor. Aun así, sí que podríamos haber escrito

void saludo(char nombre[])
{
printf("Hola,
%s, cómo estás?", nombre);
return;
}

pero la diferencia con respecto a la función "potencia" está en que en este caso hemos dejado el "return" sólo para que se vea dónde termina la función, y no indicamos nada como "return valor", dado que no hay ningún valor que devolver.

Este tipo de funciones, que no devuelven ningún valor, son el equivalente de los "procedures" del lenguaje Pascal.

TEMA 9.3.

Recordemos en este punto que "main" es una función, de tipo entero (se considera así si no se indica el tipo, que es lo que estábamos haciendo hasta ahora), por lo que la forma más correcta de escribir el ejemplo de la potencia sería:

  /*-------------------------*/
/*  Ejemplo en C nº 35 (b) */
/*  EJ35B.C
*/
/*
*/
/*  Función "potencia";    */
/*  función "main"
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  int potencia(int base, int exponente)
{
int temporal = 1;
/* Valor que voy hallando */
int i;
/* Para bucles */

     for(i=1; i<=exponente; i++)
       temporal *= base;

     return temporal;
  }

  int main()
  {
     int num1, num2;

     printf("Introduzca la base: ");
scanf("%d", &num1);
printf("Introduzca el exponente: ");
scanf("%d", &num2);
printf("%d elevado a %d vale %d", num1,
num2, potencia(num1,num2));
return 0;
}

  /*-------------------------*/

Hasta ahora no habíamos puesto eso de "int main()" porque en cuanto el compilador de C ve eso de (), sabe que se trata de una función, y si no le indicamos cómo es el valor que va a devolver, considera que se trata de un número entero. Por eso, cuando creamos la función "potencia", también podríamos haber escrito:

  potencia(int base, int exponente)
{
int temporal = 1;
/* Valor que voy hallando */
int i;
/* Para bucles */

     for(i=1; i<=exponente; i++)
       temporal *= base;

     return temporal;
  }

TEMA 9.4.

Vamos a ver ahora cómo modificar el valor de un parámetro de una función. Vamos a empezar por un ejemplo típico:

  /*-------------------------*/
/*  Ejemplo en C nº 37:    */
/*  EJ37.C
*/
/*
*/
/*  Función "modif1"
*/
/*  Parámetros por valor.  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  void modif1(int v)
  {
     v ++;
     printf("Ahora vale: %d\n", v);
  }

  main()
  {
     int valor = 2;
     printf("Ahora vale: %d\n", valor);
     modif1(valor);
     printf("Ahora vale: %d\n", valor);
  }

  /*-------------------------*/

Sigamos cada paso que vamos dando

  • Incluimos "stdio.h" para poder usar "printf".
  • La función "modif1" no devuelve ningún valor. Se le pasa una variable de tipo entero, aumenta su valor en uno y lo escribe.
  • Empieza el cuerpo del programa: valor = 2.
  • Lo escribimos. Claramente, será 2.
  • Llamamos al procedimiento "modif1", que asigna el valor=3 y lo escribe.
  • Finalmente volvemos a escribir valor... 3?

¡Pues no! Escribe un 2. Las modificaciones que hagamos a "dato" dentro de la función "modif1" sólo son válidas mientras estemos dentro de esa función. Esto es debido a que realmente no modificamos "dato", sino una copia que se hace de su valor.

TEMA 9.5.

Eso es pasar un parámetro por valor. Pero, cómo lo hacemos si realmente queremos modificar el parámetro? Eso sería pasar un parámetro por referencia, y en C se hace pasando a la función la dirección de memoria en la que está el valor. Va un ejemplo, que comentaré después:

  /*-------------------------*/
/*  Ejemplo en C nº 38:    */
/*  EJ38.C
*/
/*
*/
/*  Función "modif2"
*/
/*  Parámetros por ref.    */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  void modif2(int *v)
  {
     (*v) ++;
     printf("Ahora vale: %d\n", *v);
  }

  main()
  {
     int valor = 2;
     printf("Ahora vale: %d\n", valor);
     modif2( &valor );
     printf("Ahora vale: %d\n", valor);
  }

  /*-------------------------*/

Este programa resulta bastante menos legible, pero al menos funciona, que es de lo que se trata. Vamos a ir comentando las cosas raras que aparecen:

  • El parámetro lo pasamos como "int *v". Eso quiere decir que v es un "puntero a entero", o, en un castellano más normal, "una dirección de memoria en la que habrá un dato que es un número entero" (veremos los punteros con más detalle en el próximo tema). De ahí viene eso de "paso por referencia": no le decimos a la función el valor de la variable, sino que le damos referencias sobre ella, le decimos dónde se encuentra.
  • Para modificarlo no hacemos "v++" sino "(*v) ++". Recordemos que ahora v es una dirección de memoria, que no es lo que queremos incrementar. Queremos aumentar el valor que hay en esa dirección. A ese valor se accede como "*v". Por tanto, lo aumentaremos con (*v)++
  • A la hora de llamar a la función también tenemos que llevar cuidado, porque ésta espera que le digamos una dirección de memoria de la que leerá el dato. Eso es lo que hace el operador "&", de modo que "&valor" se leería como "la dirección de memoria en la que se encuentra la variable valor".

Pues con este jaleo de pasar una dirección de memoria y modificar el contenido de esa dirección de memoria sí que podemos variar el valor de un parámetro desde dentro de una función.

¿Y para qué vamos a querer hacer eso? Pues el ejemplo más claro es cuando queremos que una función nos devuelva más de un valor. Otro uso de los pasos por referencia es cuando tratamos de optimizar velocidad al máximo, dado que cuando pasamos un dato por valor, se crea una copia suya (ver los comentarios al ejemplo 37), cosa que no ocurre al pasar ese parámetro por referencia.

TEMA 9.6.

En el próximo tema volveremos a los punteros con más detalle. Ahora vamos a comentar qué es la recursividad, para dar este tema por finalizado:

La idea es simplemente que una función recursiva es aquella que se llama a sí misma. El ejemplo clásico es el "factorial de un número":

Partimos de la definición de factorial: el factorial de un número es
  n! = n  (n-1)  (n-2)  ...  3  2  1

Por otra parte, el factorial n-1 es
  (n-1)! = (n-1)  (n-2)  (n-3)  ...  3  2  1

Luego podemos escribir cada factorial en función del factorial del siguiente número:
  n! = n  (n-1)!

Pues esta es la definición recursiva del factorial, ni más ni menos. Esto, programando, se haría:

  /*-------------------------*/
/*  Ejemplo en C nº 39:    */
/*  EJ39.C
*/
/*
*/
/*  "factorial" recursivo  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  long fact(int n)
{
if (n==1)
/* Aseguramos que termine */
return 1;
return n * fact (n-1);
/* Si no es 1, sigue la recursión */
}

  main()
{
int num;
printf("Introduzca un número entero:
");
scanf("%d", &num);
printf("Su factorial es: %ld\n", fact(num));
}

  /*-------------------------*/

Dos consideraciones importantes:

  • Atención a la primera parte de la función recursiva: es MUY IMPORTANTE comprobar que hay salida de la función, para que no se quede dando vueltas todo el tiempo y nos cuelgue el ordenador.
  • Los factoriales crecen rápidamente, así que no conviene poner números grandes: el factorial de 16 es 2004189184, luego a partir de 17 empezaremos a obtener resultados erróneos, a pesar de haber usado enteros largos.


Curso de C. Tema 10: Variables dinámicas.

(Nota antes de empezar: este es un tema denso, y que a mucha gente le asusta al principio. Puede ser necesario leerlo varias veces y experimentar bastante para poder sacarle todo el jugo).
Hasta ahora teníamos una serie de variables que declaramos al principio del programa o de cada función. Estas variables, que reciben el nombre de ESTATICAS, tienen un tamaño asignado desde el momento en que se crea el programa.

Este tipo de variables son sencillas de usar y rápidas... si sólo vamos a manejar estructuras de datos que no cambien, pero resultan poco eficientes si tenemos estructuras cuyo tamaño no sea siempre el mismo.

Es el caso de una agenda: tenemos una serie de fichas, e iremos añadiendo más. Si reservamos espacio para 10, no podremos llegar a añadir la número 11, estamos limitando el máximo. Una solución sería la de trabajar siempre en el disco: no tenemos límite en cuanto a número de fichas, pero es muchísimo más lento.

Lo ideal sería aprovechar mejor la memoria que tenemos en el ordenador, para guardar en ella todas las fichas o al menos todas aquellas que quepan en memoria.

Una solución "típica" (pero mala) es sobredimensionar: preparar una agenda contando con 1000 fichas, aunque supongamos que no vamos a pasar de 200. Esto tiene varios inconvenientes: se desperdicia memoria, obliga a conocer bien los datos con los que vamos a trabajar, sigue pudiendo verse sobrepasado, etc.

La solución suele ser crear estructuras DINAMICAS, que puedan ir creciendo o disminuyendo según nos interesen. Ejemplos de este tipo de estructuras son:

  • Las pilas. Justo como una pila de libros: vamos apilando cosas en la cima, o cogiendo de la cima.
  • Las colas. Como las del cine, por ejemplo (en teoría): la gente llega por un sitio (la cola) y sale por el opuesto (la cabeza).
  • Las listas, en las que se puede añadir elementos, consultarlos o borrarlos en cualquier posición.

Y la cosa se va complicando: en los árboles cada elemento puede tener varios sucesores, etc.

Todas estas estructuras tienen en común que, si se programan bien, pueden ir creciendo o decreciendo según haga falta, al contrario que un array, que tiene su tamaño prefijado.

En todas ellas, lo que vamos haciendo es reservar un poco de memoria para cada nuevo elemento que nos haga falta, y enlazarlo a los que ya teníamos. Cuando queramos borrar un elemento, enlazamos el anterior a él con el posterior a él (si hace falta, para que no "se rompa") y liberamos la memoria que estaba ocupando.

Así que para seguir, necesitamos saber cómo reservar memoria y cómo liberarla. Antes, vamos a ver algo que ya se comentó de pasada en el tema anterior: eso de los punteros.

Un puntero no es más que una dirección de memoria. Lo que tiene de especial es que normalmente un puntero tendrá un tipo asociado: por ejemplo, un "puntero a entero" será una dirección de memoria en la que habrá almacenado (o podremos almacenar) un número entero.

Ahora vamos a ver qué símbolos usamos en C para designar los punteros:

    int num;            "num" es un número entero
    int *pos;           "pos" es un "puntero a entero" (dirección de
                        memoria en la que podremos guardar un entero)

    num = 1;            ahora "num" vale 1
    pos = 1000;         "pos" ahora es la dirección 1000 (peligroso)
    *pos = 25;          en la posición "pos" guardamos un 25
    pos = &num;         "pos" ahora es la dirección de "num"

Por tanto, con el símbolo * indicamos que se trata de un puntero, y & nos devuelve la dirección de memoria en la que se encuentra una variable.

Lo de "pos=1000" es peligroso, porque no sabemos qué hay en esa dirección, de modo que si escribimos allí podemos provocar una catástrofe. Por ejemplo, si ponemos un valor al azar que coincide con la instrucción en código máquina de formatear el disco duro, no nos hará nada de gracia cuando nuestro programa llegue hasta esa instrucción. Normalmente las consecuencias no son tan graves, pero hay que llevar cuidado. La forma de trabajar será pedir al compilador que nos reserve un poco de memoria donde él crea adecuado.

Para eso usamos la orden "malloc" (necesitaremos incluir el fichero de cabecera <stdlib.h> o <malloc.h> -es preferible usar el primero, que es estándar-). Una vez que hemos terminado de usar esa memoria, suele ser conveniente liberarla, y para eso empleamos "free". Vamos a ver un ejemplo:

  /*-------------------------*/
/*  Ejemplo en C nº 40:    */
/*  EJ40.C
*/
/*
*/
/*  Introducción a los
*/
/*  punteros.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

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

  int num;
/* Un número entero */
int *pos;
/* Un "puntero a entero" */

  main()
  {
     printf("Num vale: %d (arbitrario)\n", num);
     printf("La dirección Pos es: %p (arbitrario)\n", pos);

     /* La siguiente línea está deshabilitada porque es "peligrosa"
          (en GCC dará error) */
     /* printf("El contenido de Pos es: %d (arbitrario)\n", *pos); */

     num = 1;
/* ahora "num" vale 1 */
printf("Num vale: %d (fijado)\n", num);

                        /* Reservamos espacio */
     pos = (int *) malloc (sizeof(int));

     printf("La dirección Pos es: %p (asignado)\n", pos);
     printf("El contenido de Pos es: %d (arbitrario)\n", *pos);

     *pos = 25;         /*  En la posición "pos" guardamos un 25 */
     printf("El contenido de Pos es: %d (fijado)\n", *pos);

     free(pos);         /* Liberamos lo reservado */

     pos = &num;        /*  "pos" ahora es la dirección de "num" */
     printf("Y ahora el contenido de Pos es: %d (valor de Num)\n", *pos);
  }

  /*-------------------------*/

El formato de "free" no tiene ninguna pega: "free(pos)" quiere decir "libera la memoria que ocupaba pos". El de "malloc" es más rebuscado:

    malloc (tamaño)

Como queremos reservar espacio para un entero, ese "tamaño" será lo que ocupe un entero, y eso nos lo dice "sizeof(int)".

¿Y eso de (int*)? Es porque "malloc" nos devuelve un puntero sin tipo (un puntero a void: void*). Cómo queremos guardar un dato entero, primero debemos hacer una conversión de tipos (typecast), de "puntero sin tipo" a "puntero a entero" (int *).

De principio puede asustar un poco, pero se le pierde el miedo en cuanto se usa un par de veces. Vamos a analizar ahora la salida de este programa.

Con Turbo C++ 1.01 obtenemos:

    Num vale: 0 (arbitrario)
    La dirección Pos es: 0000:0000 (arbitrario)
    El contenido de Pos es: 294 (arbitrario)
    Num vale: 1 (fijado)
    La dirección Pos es: 0000:0004 (asignado)
    El contenido de Pos es: 1780 (arbitrario)
    El contenido de Pos es: 25 (fijado)
    Y ahora el contenido de Pos es: 1 (valor de Num)

Y con Symantec C++ 6.0

    Num vale: 0 (arbitrario)
    La dirección Pos es: 0000 (arbitrario)
    El contenido de Pos es: 21061 (arbitrario)
    Num vale: 1 (fijado)
    La dirección Pos es: 2672 (asignado)
    El contenido de Pos es: -9856 (arbitrario)
    El contenido de Pos es: 25 (fijado)
    Y ahora el contenido de Pos es: 1 (valor de Num)

En ambos casos vemos que

  • Inicialmente "num" vale 0, pero como no es algo que hayamos obligado nosotros, no podemos fiarnos de que siempre vaya a ser así.
  • El puntero POS es 0 al principio (no apunta todavía a ninguna dirección). Esto es un "puntero nulo", para el que todavía no se ha asignado un espacio en memoria. De hecho, para lograr una mayor legibilidad, está definida (en "stdio.h" y en otras cabeceras) una constante llamada NULL y de valor 0, de modo que podemos hacer comparaciones como
      if (pos == NULL)  ...
Recordemos que if (pos != NULL) es igual que if (pos) ... (ya visto en el tema de condiciones).
  • Como esta dirección ("pos") no está reservada aún, su valor puede ser cualquier cosa. Y de hecho, puede seguirlo siendo después de reservarla: el compilador nos dice dónde tenemos memoria disponible, pero no "la vacía" para nosotros.
  • Una vez que hemos reservado memoria y hemos asignado el valor, ya sabemos con certeza que ese número 25 se guardará donde debe.
  • Finalmente, si queremos asignar a "pos" el valor de la dirección en la que se encuentra "num", como hemos hecho en la penúltima línea, no hace falta reservar memoria con "malloc" (de hecho, lo he usado a propósito después de liberar la memoria con "free"). Esto es debido a que "num" ya tenía reservado su espacio de memoria, al que nosotros ahora podemos acceder de dos formas: sabiendo que corresponde a la variable "num", o bien teniendo en cuenta que "pos" es la dirección en la que está ese dato.

Finalmente, con GCC la siguiente línea da problemas:

    printf("El contenido de Pos es: %d (arbitrario)\n", *pos);

Estamos intentando acceder a un espacio de memoria que no hemos reservado, lo cual no es posible bajo Linux. Esto hace que en el momento de ejecutar el programa, aparezca un error de "Segmentation Fault". En cambio, los dos compiladores que trabajan bajo DOS aceptan esta orden. Aun así, insisto en que no se debe modificar el contenido de zonas de memoria que no hemos reservado.

TEMA 10.2. Aritmética de punteros.

Aquí hay que hacer una observación: como un puntero tiene un valor numérico (la dirección en la que se nos ha reservado memoria), podemos aumentar este número haciendo cosas como

    pos ++;           o bien   pos += 3;

Pero también hay que recordar que normalmente un puntero va a estar asociado a un cierto tipo de datos. Por ejemplo, "int *" indica un puntero a entero.

¿A qué viene esto? Es sencillo: si un entero ocupa 2 bytes, al hacer "pos++" no deberíamos avanzar de 1 byte en 1 byte, sino de 2 en 2 bytes, para saltar al siguiente dato.

Vamos a ver un ejemplo que cree un array de enteros dinámicamente, y que lo recorra usando los índices del array (como habríamos hecho hasta ahora) y también usando punteros:

  /*-------------------------*/
/*  Ejemplo en C nº 41:    */
/*  EJ41.C
*/
/*
*/
/*  Punteros y arrays
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

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

  int *num;           /* Puntero a número(s) entero(s) */
  int *temporal;      /* Temporal, para recorrer el array */
  int i;              /* Para bucles */

  main()
  {
            /* Reservamos espacio para 10 números (array dinámico) */
     num = (int *) malloc (10 * sizeof(int));

     for (i=0; i<10; i++)
/* Recorremos el array */
num[i] = i*2;
/* Dando valores */

     printf("La dirección de comienzo del array es: %p\n", num);

     printf("Valores del array: ");
for (i=0; i<10; i++)
/* Recorremos el array */
printf("%d ",num[i]);
/* Mostrando los valores */

     printf("\nValores del array (como puntero):
");
temporal=num;
for (i=0; i<10; i++)
/* Recorremos como puntero */
printf("%d ",*temporal++);
/* Mostrando los valores y aumentando */

     free(num);                  /* Liberamos lo reservado */

  }

  /*-------------------------*/

Como se ve, en C hay muy poca diferencia entre arrays y punteros: hemos declarado "num" como un puntero, pero hemos reservado espacio para más de un dato, y hemos podido recorrerlo como si hubiésemos definido

   int num[10];

Esto es lo que da lugar a la gran diferencia que existe en el manejo de "strings" (cadenas de texto) en lenguajes como Pascal frente al C, por lo que he preferido dedicarles un tema entero (se verá más adelante).

Se puede profundizar mucho más en los punteros, especialmente tratando estructuras dinámicas como las pilas y colas, las listas simples y dobles, los árboles, etc. De momento, nos limitaremos a comentar en qué consisten las listas simples y los árboles.

TEMA 10.3. Listas.

Pues ahora vamos a ver dos tipos de estructuras totalmente dinámicas (que puedan aumentar o disminuir de tamaño durante la ejecución del programa). Primero veremos las listas, y más adelante los árboles binarios. Hay otras muchas estructuras, pero no son difíciles de desarrollar si se entienden bien estas dos.
(Nota: quien haya seguido mi curso de Pascal, verá que el texto de esta lección es casi idéntico al de la equivalente en Pascal, salvando la diferente sintaxis de ambos lenguajes; espero que eso ayude a comparar el manejo de punteros en ambos lenguajes).

Ahora "el truco" consistirá en que dentro de cada dato almacenaremos todo lo que nos interesa, pero también una referencia que nos dirá dónde tenemos que ir a buscar el siguiente.

Sería algo así como:

   (Posición: 1023).
   Nombre          : 'Nacho Cabanes'
   DireccionInet   : 'ncabanes@pobox.com'
   SiguienteDato   : 1430

Este dato está almacenado en la posición de memoria número 1023. En esa posición guardamos el nombre y la dirección (o lo que nos interese) de esta persona, pero también una información extra: la siguiente ficha se encuentra en la posición 1430.

Así, es muy cómodo recorrer la lista de forma secuencial, porque en todo momento sabemos dónde está almacenado el siguiente dato. Cuando lleguemos a uno para el que no esté definido cual es el siguiente, quiere decir que se ha acabado la lista.

Por tanto, en cada dato tenemos un enlace con el dato siguiente. Por eso este tipo de estructuras recibe el nombre de "listas simplemente enlazadas" o listas simples. Si tuvieramos enlaces hacia el dato siguiente y el posterior, se trataría de una "lista doblemente enlazada" o lista doble.

Hemos perdido la ventaja del acceso directo: ya no podemos saltar directamente a la ficha número 500. Pero, por contra, podemos tener tantas fichas como la memoria nos permita.

Para añadir un ficha, no tendríamos más que reservar la memoria para ella, y el compilador de C nos diría "le he encontrado sitio en la posición 4079". Así que nosotros iríamos a la última ficha y le diríamos "tu siguiente dato va a estar en la posición 4079".

Esa es la idea "intuitiva". Ahora vamos a concretar cosas en forma de programa en C.

Primero veamos cómo sería ahora cada una de nuestras fichas:

  typedef

    struct f
/* Estos son los datos que guardamos: */
{
char nombre[30];
/*   Nombre, hasta 30 letras */
char direccion[50];
/*   Direccion, hasta 50 */
int edad;
/*   Edad, un numero < 255 */
struct f* siguiente;
/*   Y dirección de la siguiente */
} Ficha;

La diferencia con un "struct" normal está en el campo "siguiente" de nuestro registro, que es el que indica donde se encuentra la ficha que va después de la actual, y por tanto será otro puntero a un registro como estos, un "struct f*".

Un puntero que "no apunta a ningún sitio" tiene el valor NULL (realmente este identificador es una constante de valor 0, como ya hemos comentado), que nos servirá después para comprobar si se trata del final de la lista: todas las fichas "apuntarán" a la siguiente, menos la última, que "no tiene siguiente", y apuntará a NULL.

Entonces la primera ficha definiríamos con

  Ficha* dato1;                /* Va a ser un puntero a ficha */

y la crearíamos con

  dato1 = (Ficha*) malloc (sizeof(Ficha)); /* Reservamos memoria
*/
strcpy(dato1->nombre, "Pepe");
/* Guardamos el nombre, */
strcpy(dato1->direccion, "Su casa");
/*   la dirección */
dato1->edad = 40;
/*   la edad */
dato1->siguiente = NULL;
/* y no hay ninguna más */

Eso de

  strcpy(dato1->nombre,"Pepe");

es algo que posiblemente tenderíamos a escribir así en un principio:

  *dato1.nombre = "Pepe";

Pero recordemos que en el lenguaje C no se puede dar valores así a las cadenas de texto, sino que debemos funciones como "strcpy", y hacer algo parecido a

  strcpy(*dato1.nombre,"Pepe");

Pero esa expresión todavía no es correcta del todo. La forma correcta de escribir que queremos acceder a los campos de un puntero a registro es usando el operador "->", como he indicado anteriormente:

  strcpy(dato1->nombre,"Pepe");

Ahora que ya tenemos una ficha, podríamos añadir otra ficha detrás de ella. Primero guardamos espacio para la nueva ficha, como antes:

  Ficha* dato2;                /* Va a ser otro puntero a ficha */

  dato2 = (Ficha*) malloc (sizeof(Ficha)); /* Reservamos memoria
*/
strcpy(dato2->nombre, "Juan");
/* Guardamos el nombre, */
strcpy(dato2->direccion, "No lo sé");
/*   la dirección */
dato2->edad = 35;
/*   la edad */
dato2->siguiente = NULL;
/* y no hay ninguna más */

y ahora enlazamos la anterior con ella:

  dato1->siguiente = dato2;

Si quisieramos introducir los datos ordenados alfabéticamente, basta con ir comparando cada nuevo dato con los de la lista, e insertarlo donde corresponda. Por ejemplo, para insertar un nuevo dato entre los dos anteriores, haríamos:

  Ficha* dato3;                /* Va a ser otro puntero a ficha */

  dato3 = (Ficha*) malloc (sizeof(Ficha));
strcpy(dato3->nombre, "Carlos");
strcpy(dato3->direccion, "Por ahí");
dato3->edad = 14;
dato3->siguiente = dato2;
/* enlazamos con la siguiente */

  dato1->siguiente = dato3;                /* y la anterior con ella */

La estructura que hemos obtenido es la siguiente

   Dato1 - Dato3 - Dato2 - NULL

Es decir: cada ficha está enlazada con la siguiente, salvo la última, que no está enlazada con ninguna (apunta a NULL).

Si ahora quisiéramos borrar Dato3, tendríamos que seguir dos pasos:

1.- Enlazar Dato1 con Dato2, para no perder información.
2.- Liberar la memoria ocupada por Dato3.

Esto, escrito en "C" sería:

      dato1->siguiente = dato2;
/* Enlaza Dato1 y Dato2 */
free(dato3);
/* Libera lo que ocupó Dato3 */

Hemos empleado tres variables para guardar tres datos. Si tenemos 20 datos, ¿necesitaremos 20 variables? ¿Y 3000 variables para 3000 datos?

Sería tremendamente ineficiente, y no tendría mucho sentido. Es de suponer que no sea así. En la práctica, basta con dos variables, que nos indicarán el principio de la lista y la posición actual, o incluso sólo una para el principio de la lista.

Por ejemplo, una rutina que muestre en pantalla toda la lista se podría hacer de forma recursiva así:

  void MuestraLista ( Ficha *inicial )
{
if (inicial)
/* Si realmente hay lista */
{
printf("Nombre: %s\n", inicial->nombre);
printf("Dirección: %s\n",
inicial->direccion);
printf("Edad: %s\n", inicial->edad);
MuestraLista ( inicial->siguiente
);    /* Y mira el siguiente */
}
}

Lo llamaríamos con "MuestraLista(Dato1)", y a partir de ahí el propio procedimiento se encarga de ir mirando y mostrando los siguientes elementos hasta llegar a NULL, que indica el final.

Antes de seguir, vamos a juntar todo esto en un programa, para comprobar que realmente funciona: añadimos los 3 datos "a lo bestia" y decimos que los muestre desde el primero; luego borramos el del medio y los volvemos a mostrar:

  /*-------------------------*/
/*  Ejemplo en C:
*/
/*  LISTA1.C
*/
/*
*/
/*  Lista enlazada simple  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*-------------------------*/

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

  typedef

    struct f
/* Estos son los datos que guardamos: */
{
char nombre[30];
/*   Nombre, hasta 30 letras */
char direccion[50];
/*   Direccion, hasta 50 */
int edad;
/*   Edad, un numero < 255 */
struct f* siguiente;
/*   Y dirección de la siguiente */
} Ficha;

  Ficha* dato1;
/* Va a ser un puntero a ficha */
Ficha* dato2;
/* Otro puntero a ficha */
Ficha* dato3;
/* Y otro más */

  void MuestraLista ( Ficha *inicial )
{
if (inicial)
/* Si realmente hay lista */
{
printf("Nombre: %s\n", inicial->nombre);
printf("Dirección: %s\n",
inicial->direccion);
printf("Edad: %d\n\n", inicial->edad);
MuestraLista ( inicial->siguiente
);    /* Y mira el siguiente */
}
}

  int main()
{
dato1 = (Ficha*) malloc (sizeof(Ficha)); /* Reservamos memoria
*/
strcpy(dato1->nombre, "Pepe");
/* Guardamos el nombre, */
strcpy(dato1->direccion, "Su casa");
/*   la dirección */
dato1->edad = 40;
/*   la edad */
dato1->siguiente = NULL;
/* y no hay ninguna más */

  dato2 = (Ficha*) malloc (sizeof(Ficha)); /* Reservamos memoria
*/
strcpy(dato2->nombre, "Juan");
/* Guardamos el nombre, */
strcpy(dato2->direccion, "No lo sé");
/*   la dirección */
dato2->edad = 35;
/*   la edad */
dato2->siguiente = NULL;
/* y no hay ninguna más */

  dato1->siguiente = dato2;                /* Enlazamos la anterior con ella */

  dato3 = (Ficha*) malloc (sizeof(Ficha)); /* La tercera */
strcpy(dato3->nombre, "Carlos");
strcpy(dato3->direccion, "Por ahí");
dato3->edad = 14;
dato3->siguiente = dato2;
/* enlazamos con la siguiente */

  dato1->siguiente = dato3;                /* y la anterior con ella */

  printf("La lista inicialmente es:\n");
  MuestraLista (dato1);

  dato1->siguiente = dato2;
/* Borrar dato3: Enlaza Dato1 y Dato2 */
free(dato3);
/* Libera lo que ocupó Dato3 */

  printf("Y tras borrar dato3:\n\n");
  MuestraLista (dato1);

  return 0;
  }

Aquí va otro programilla de ejemplo, que crea una lista de números, y va insertando en ella varios valores ordenados:

  /*-------------------------*/
/*  Ejemplo en C:
*/
/*  LISTA2.C
*/
/*
*/
/*  Lista simple ordenada  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*-------------------------*/

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

  typedef
struct lista
{
int numero;
struct lista*
sig;
} tipoDatos;

  typedef
      tipoDatos* puntero;

  puntero CrearLista(int valor)  /* Crea la lista, claro
*/
{
puntero r;
/* Variable auxiliar */

      r = (puntero) malloc (sizeof(tipoDatos));
/* Reserva memoria */
r->numero = valor;
/* Guarda el valor */
r->sig = NULL;
/* No hay siguiente */

       return r;
/* Crea el puntero */
};

  void MuestraLista ( puntero lista )
{
if (lista)
/* Si realmente hay lista */
{
printf("%d\n",
lista->numero);    /* Escribe el valor */
MuestraLista
(lista->sig );       /* Y mira el siguiente
*/
};
};

  void InsertaLista( puntero* lista, int valor)
{
puntero r;
/*      Variable auxiliar */
puntero actual;

         actual = *lista;

       if (actual)
/*  Si hay lista */
if (actual->numero < valor)       /*
y todavía no es su sitio */
/*    hace una llamada recursiva: */
InsertaLista(&actual->sig,valor);  /* mira la siguiente posición
*/
else
/* Caso contrario: si hay lista */
{
/* pero hay que insertar ya: */
r = (puntero) malloc (sizeof(tipoDatos));  /*Reserva espacio, */
r->numero = valor;
/* guarda el dato */
r->sig = actual;
/* pone la lista a continuac. */
*lista = r;
/* Y hace que comience en */
}
/* el nuevo dato: r */
else
/*      Si no hay lista */
{
/*  deberá crearla */
r = (puntero)
malloc (sizeof(tipoDatos));  /* Reserva espacio, */
r->numero
= valor;
/* guarda el dato */
r->sig =
NULL;
/* no hay nada detrás y */
*lista =
r;
/* hace que la lista comience */
/* en el dato: r */
}
}

  int main()
  {
          puntero l;                     /* La lista que crearemos */

          l = CrearLista(5);             /* Crea una lista e introduce un 5 */
          InsertaLista(&l, 3);           /* Inserta un 3 */
          InsertaLista(&l, 2);           /* Inserta un 2 */
          InsertaLista(&l, 6);           /* Inserta un 6 */
        MuestraLista(l);                 /* Muestra la lista resultante */
        return 0;                        /* Se acabó */
  }

Si alguien compara esto con el curso de Pascal, verá que el ejemplo es casi idéntico, las únicas diferencias se deben a la diferente sintaxis de ambos lenguajes.

Eso sí, la rutina de insertar elementos es algo menos legible en C, porque en este lenguaje es más engorroso pasar parámetros por referencia que en Pascal.

Hay varios casos particulares más sencillos que una lista "normal". Sólo comentaré algunos de ellos de pasada:

  • Una pila es un caso particular de lista, en la que los elementos siempre se introducen y se sacan por el mismo extremo (se apilan o se desapilan). Es como una pila de libros, en la que para coger el tercero deberemos apartar los dos primeros (excluyendo malabaristas, que los hay). Este tipo de estructura se llama LIFO (Last In, First Out: el último en entrar es el primero en salir).
  • Una cola es otro caso particular, en el que los elementos se introducen por un extremo y se sacan por el otro. Es como se supone que debería ser la cola del cine: los que llegan, se ponen al final, y se atiende primero a los que están al principio. Esta es una estructura FIFO (First In, First Out).

Finalmente, antes de pasar con los "árboles", comentaré una mejora a estas listas enlazadas que hemos visto. Tal y como las hemos tratado, tienen la ventaja de que no hay limitaciones tan rígidas en cuanto a tamaño como en las variables estáticas, ni hay por qué saber el número de elementos desde el principio. Pero siempre hay que recorrerlas desde DELANTE hacia ATRAS, lo que puede resultar lento. Una mejora relativamente evidente es lo que se llama una lista doble o lista doblemente enlazada: si guardamos punteros al dato anterior y al siguiente, en vez de sólo al siguiente, podremos avanzar y retroceder con comodidad. Queda como ejercicio para quien tenga inquietudes.

TEMA 10.4. Árboles.

Vamos allá. En primer lugar, veamos de donde viene el nombre. En las listas, después de cada elemento venía otro (o ninguno, si habíamos llegado al final). Pero también nos puede interesar tener varias posibilidades después de cada elemento, 3 por ejemplo. De cada uno de estos 3 saldrían otros 3, y así sucesivamente. Obtendríamos algo que recuerda a un árbol: un tronco del que nacen 3 ramas, que a su veces se subdividen en otras 3 de menor tamaño, y así sucesivamente hasta llegar a las hojas.

Pues eso será un árbol: una estructura dinámica en la que cada nodo (elemento) puede tener más de un "siguiente". Nos centraremos en los árboles binarios, en los que cada nodo puede tener un hijo izquierdo, un hijo derecho, ambos o ninguno (dos hijos como máximo).

Para puntualizar aun más, aviso que trataremos los árboles binarios de búsqueda, en los que tenemos prefijado un cierto orden, que nos ayudará a encontrar un cierto dato dentro de un árbol con mucha rapidez.

Y como es este "orden prefijado"? Sencillo: para cada nodo tendremos que:

  • la rama de la izquierda contendrá elementos menores.
  • la rama de la derecha contendrá elementos mayores.

¿Asusta? Con un ejemplo seguro que no: Vamos a introducir en un árbol binario de búsqueda los datos 5,3,7,2,4,8,9

 Primer número:  5 (directo)

          5

 Segundo número: 3 (menor que 5)

          5
         /
        3

 Tercer número: 7 (mayor que 5)

          5
         / \
        3   7

 Cuarto: 2 (menor que 5, menor que 3)

          5
         / \
        3   7
       /
      2

 Quinto: 4 (menor que 5, mayor que 3)

          5
         / \
        3   7
       / \
      2   4

 Sexto: 8 (mayor que 5, mayor que 7)

          5
         / \
        3   7
       / \   \
      2   4   8

 Séptimo: 9 (mayor que 5, mayor que 7, mayor que 8)

          5
         / \
        3   7
       / \   \
      2   4   8
               \
                9

¿Y qué ventajas tiene esto? Pues la rapidez: tenemos 7 elementos, lo que en una lista supone que si buscamos un dato que casualmente está al final, haremos 7 comparaciones; en este árbol, tenemos 4 alturas => 4 comparaciones como máximo.

Y si además hubiéramos "equilibrado" el árbol (irlo recolocando, de modo que siempre tenga la menor altura posible), serían 3 alturas.

Esto es lo que se hace en la práctica cuando en el árbol se va a hacer muchas más lecturas que escrituras: se reordena internamente después de añadir cada nuevo dato, de modo que la altura sea mínima en cada caso.

De este modo, el número máximo de comparaciones que tendríamos que hacer sería log2(n), lo que supone que si tenemos 1000 datos, en una lista podríamos llegar a tener que hacer 1000 comparaciones, y en un árbol binario, log2(1000) => 10 comparaciones como máximo. La ganancia en velocidad de búsqueda es clara.

No vamos a ver cómo se hace eso de los "equilibrados", que considero que sería propio de un curso de programación más avanzado, o incluso de uno de "Tipos Abstractos de Datos" o de "Algorítmica", y vamos a empezar a ver rutinas para manejar estos árboles binarios de búsqueda.

Recordemos que la idea importante es que todo dato menor estará a la izquierda del nodo que miramos, y los datos mayores estarán a su derecha.

Ahora la estructura de cada nodo (dato) será:

  typedef
        int TipoDato;                 /*  Vamos a guardar enteros */

  typedef
        struct t  {                   /*  El tipo base en sí: */
              TipoDato dato;          /*    - un dato */
              struct t* hijoIzq;      /*    - puntero a su hijo izquierdo */
              struct t* hijoDer;      /*    - puntero a su hijo derecho */
       }  TipoBase;

  typedef
       TipoBase* puntero;             /*  El puntero al tipo base */

Y las rutinas de inserción, búsqueda, escritura, borrado, etc., podrán ser recursivas. Como primer ejemplo, la de escritura de todo el árbol (la más sencilla) sería:

  void Escribir(puntero punt)
{
if (punt)
/*  Si no hemos llegado a una hoja */
{
Escribir(punt->hijoIzq);
/*  Mira la izqda recursivamente */
printf("%d ",punt->dato);
/*  Escribe el dato del nodo */
Escribir(punt->hijoDer);
/*  Y luego mira por la derecha */
};
};

Si alguien no se cree que funciona, que coja lápiz y papel y lo compruebe con el árbol que hemos puesto antes como ejemplo. Es MUY IMPORTANTE que este procedimiento quede claro antes de seguir leyendo, porque los demás serán muy parecidos.

La rutina de inserción sería parecida, aunque algo más "pesada" porque tenemos que pasar el puntero por referencia, para que se pueda modificar el puntero:

  void Insertar(puntero* punt, TipoDato valor)
  {
        puntero actual= *punt;

        if (actual == NULL)
/*  Si hemos llegado a una hoja */
{
*punt =
(puntero) malloc (sizeof(TipoBase));    /* Reserv. memoria
*/
actual=
*punt;
actual->dato
= valor;         /* Guardamos el
dato */
actual->hijoIzq
= NULL;       /* No tiene hijo izquierdo
*/
actual->hijoDer
= NULL;       /* Ni derecho */
}
else
/*      Si no es hoja
*/
if (actual->dato > valor)
/* Y encuentra un dato mayor */
Insertar(&actual->hijoIzq,
valor);  /* Mira por la izquierda */
else
/* En caso contrario (menor) */
Insertar(&actual->hijoDer,
valor);  /* Mira por la derecha */
};

Y finalmente, la de borrado de todo el árbol, casi igual que la de escritura, sólo que en vez de borrar la izquierda, luego el nodo y luego la derecha, borraremos primero las dos ramas y luego el nodo para evitar incongruencias (borrar el hijo de algo que ya no existe):

  void Borrar(puntero punt)
{
if (punt)
/*  Si no hemos llegado a una hoja */
{
Borrar(punt->hijoIzq);
/*  Va a la izqda recursivamente */
Borrar(punt->hijoDer);
/*  Y luego va por la derecha */
free(punt);
/*  Libera lo que ocupa el nodo */
};
} ;

Finalmente, vamos a juntar todo esto en un ejemplo "que funcione":

  /*-------------------------*/
/*  Ejemplo en C:
*/
/*  ARBOL.C
*/
/*
*/
/*  Arbol binario de busq. */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*-------------------------*/

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

  typedef
        int TipoDato;                 /*  Vamos a guardar enteros  */

  typedef
        struct t  {                   /*  El tipo base en sí: */
              TipoDato dato;          /*    - un dato */
              struct t* hijoIzq;      /*    - puntero a su hijo izquierdo */
              struct t* hijoDer;      /*    - puntero a su hijo derecho */
         } TipoBase;

  typedef
       TipoBase* puntero;             /*  El puntero al tipo base */

  void Escribir(puntero punt)
{
if (punt)
/*  Si no hemos llegado a una hoja */
{
Escribir(punt->hijoIzq);
/*  Mira la izqda recursivamente */
printf("%d ",punt->dato);
/*  Escribe el dato del nodo */
Escribir(punt->hijoDer);
/*  Y luego mira por la derecha */
};
};

 void Insertar(puntero* punt, TipoDato valor)
  {
        puntero actual= *punt;

        if (actual == NULL)
/*  Si hemos llegado a una hoja */
{
*punt =
(puntero) malloc (sizeof(TipoBase));    /* Reserv. memoria
*/
actual=
*punt;
actual->dato
= valor;         /* Guardamos el
dato */
actual->hijoIzq
= NULL;       /* No tiene hijo izquierdo
*/
actual->hijoDer
= NULL;       /* Ni derecho */
}
else
/*      Si no es hoja
*/
if (actual->dato > valor)
/* Y encuentra un dato mayor */
Insertar(&actual->hijoIzq,
valor);  /* Mira por la izquierda */
else
/* En caso contrario (menor) */
Insertar(&actual->hijoDer,
valor);  /* Mira por la derecha */
};

  /*  Cuerpo del programa  */

  int main()
  {
        puntero arbol = NULL;

        Insertar(&arbol, 5);
        Insertar(&arbol, 3);
        Insertar(&arbol, 7);
        Insertar(&arbol, 2);
        Insertar(&arbol, 4);
        Insertar(&arbol, 8);
        Insertar(&arbol, 9);
        Escribir(arbol);

        return 0;
  }


Curso de C. Tema 11: Manejo de ficheros.

Vamos a comenzar por ver cómo leer un fichero de texto. Primero voy a poner un ejemplo que lea y muestre el AUTOEXEC.BAT, y después lo iré comentando:

  /*-------------------------*/
/*  Ejemplo en C nº 42:    */
/*  EJ42.C
*/
/*
*/
/*  Ficheros de texto (1)  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  FILE* fichero;
  char texto[80];

  main()
{
fichero = fopen("c:\\autoexec.bat", "rt");
/* En Linux: fichero = fopen("/proc/cpuinfo",
"rt");  */
if (fichero == NULL)
{
printf("No existe el fichero!\n");
exit(1);
}
while (! feof(fichero)) {
fgets(texto, 80, fichero);
printf("%s", texto);
}
fclose(fichero);
}

  /*-------------------------*/

Aquí van los comentarios sobre este programa:

  • FILE es el tipo asociado a un fichero. Realmente se trata de un "puntero a fichero", por eso aparece el asterisco * a su derecha.
  • Para abrir el fichero usamos "fopen", que lleva dos parámetros: el nombre del fichero y el modo de lectura. En el nombre del fichero, la barra \ aparece repetida a propósito, porque (como vimos al hablar de "printf") es un código de control, de modo que \a sería la señal de alerta (un pitido), que no es lo que queremos leer. Por eso, ponemos \\, que se traduce como una sola barra. Lo de "rt" indica que el modo será de lectura (r) en un fichero de texto (t). Si usamos GCC bajo Linux, no existe "c:\autoexec.bat", así que podemos leer "/proc/cpuinfo", o "/proc/devices", o ".profile", por ejemplo.
  • Como "fichero" es un puntero (a fichero), para mirar si ha habido algún problema, comprobamos si ese puntero sigue siendo nulo después de intentar acceder al fichero.
  • Después repetimos una parte del programa hasta que se acabe el fichero, de lo que nos informa "feof" (EOF es la abreviatura de End Of File, fin de fichero).
  • Con "fgets" leemos una cadena de texto, que podemos limitar en longitud (en este caso, a 80 caracteres), desde el fichero. Esta cadena de texto conservará los caracteres de avance de línea.
  • Finalmente, cerramos el fichero con "fclose".

TEMA 11.2.

Si queremos crear un fichero, los pasos son muy parecidos, sólo que lo abriremos para escritura (w), y escribiremos con "fputs":

  /*-------------------------*/
/*  Ejemplo en C nº 43:    */
/*  EJ43.C
*/
/*
*/
/*  Ficheros de texto (2)  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*    - PCC 2.1c
*/
/*-------------------------*/

  #include <stdio.h>

  FILE* fichero;
  char texto[80];

  main()
{
fichero = fopen("basura.dat", "wt");
if (fichero == NULL)
{
printf("No se ha podido crear el
fichero!\n");
exit(1);
}
fputs("Esto es una línea\n", fichero);
fputs("Esto es otra", fichero);
fputs(" y esto es continuación de la
anterior\n", fichero);
fclose(fichero);
}

  /*-------------------------*/

TEMA 11.3.

Antes de seguir, vamos a ver las letras que pueden aparecer en el modo de apertura del fichero:

  Tipo   Significado

   r     Abrir sólo para lectura.
   w     Crear para escribir.  Sobreescribe si existiera ya.
   a     Añade al final si existe, o crea si no existe.
   +     Permite modificar.  Por ejemplo: r+
   t     Abrir en modo de texto.
   b     Abrir en modo binario.

Si queremos leer o escribir sólo una letra, tenemos las órdenes "fgetc" y "fputc", que se usan:

   letra = fgetc( fichero );
   fputc (letra, fichero);

Podemos querer crear un "fichero con tipo", en el que todos los componentes vayan a ser del mismo tipo. Por ejemplo, podría ser para un agenda, en la que guardemos los datos de cada persona con un "struct".

En casos como éste, la solución más cómoda puede ser usar "fprintf" y "fscanf", análogos a "printf" y "scanf", que se emplearían así:

   fprintf( fichero, "%40s%5d\n", persona.nombre, persona.numero);
   fscanf( fichero, "%40s%5d\n", &persona.nombre, &persona.numero);

TEMA 11.4.

Finalmente, podemos crear ficheros "sin tipo", es decir, que la información que contengan no necesariamente sea sólo texto ni tampoco datos siempre iguales.

En este caso, utilizamos "fread" y "fwrite" (análogos a BlockRead y BlockWrite, para quien venga de Pascal). Los datos que se leen se van guardando en un buffer (una zona intermedia de memoria). En el momento en que se lean menos bytes de los que hemos pedido, quiere decir que hemos llegado al final del fichero.

Vamos a ver un ejemplo, que comentaré después:

  /*-------------------------*/
/*  Ejemplo en C nº 44:    */
/*  EJ44.C
*/
/*
*/
/*  Ficheros sin tipo:     */
/*  Copiador elemental     */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

  #include <stdio.h>

  FILE *fichOrg, *fichDest;
/* Los dos ficheros */
char buffer[2048];
/* El buffer para guardar lo que leo */
char nombreOrg[80],
/* Los nombres de los ficheros */
nombreDest[80];
int cantidad;
/* El número de bytes leídos */

  main()
  {

    /* Accedo al fichero de origen */
printf("Introduzca el nombre del fichero Origen:
");
scanf("%s",nombreOrg);
if ((fichOrg = fopen(nombreOrg, "rb")) == NULL)
{
printf("No existe el fichero origen!\n");
exit(1);
}

    /* Y ahora al de destino */
printf("Introduzca el nombre del fichero Destino:
");
scanf("%s",nombreDest);
if ((fichDest = fopen(nombreDest, "wb")) ==
NULL)
{
printf("No se ha podido crear el
fichero destino!\n");
exit(1);
}

    /* Mientras quede algo que leer */
while (! feof(fichOrg) )
{
/* Leo datos: cada uno de 1 byte,
todos los que me caben */
cantidad = fread( buffer, 1, sizeof(buffer),
fichOrg);
/* Escribo tantos como haya leído
*/
fwrite(buffer, 1, cantidad, fichDest);
}
/* Cierro los ficheros */
fclose(fichOrg);
fclose(fichDest);
}

  /*-------------------------*/

Las novedades en este programa son:

  • Defino un buffer de 2048 bytes (2 K), en el que iré guardando lo que lea.
  • En la misma línea intento abrir el fichero y compruebo si todo ha sido correcto. Es menos legible, pero más compacto, y, sobre todo, muy frecuente encontrarlo en "fuentes ajenos" de esos que circulan por ahí, de modo que he considerado adecuado incluirlo.
  • A "fread" le digo que quiero leer 2048 datos de 1 byte cada uno, y él me devuelve la cantidad de bytes que ha leído realmente. Cuando sea menos de 2048 bytes, es que el fichero se ha acabado.
  • A "fwrite" le indico el número de bytes que quiero que escriba.
Cuando trabajamos con un fichero, es posible que necesitemos acceder directamente a una cierta posición del mismo. Para ello usamos "fseek", que tiene el formato:

    int fseek(FILE *fichero, long posic, int desde);

Como siempre, comentemos qué es cada cosa:

  • Es de tipo "int", lo que quiere decir que nos va a devolver un valor, para que comprobemos si realmente se ha podido saltar a la dirección que nosotros le hemos pedido: si el valor es 0, todo ha ido bien; si es otro, indicará un error (normalmente, que no hemos abiertos el fichero).
  • "fichero" indica el fichero dentro de el que queremos saltar. Este fichero debe estar abierto previamente (con fopen).
  • "posic" nos permite decir a qué posición queremos saltar (por ejemplo, a la 5010).
  • "desde" es para poder afinar más: la dirección que hemos indicado con posic puede estar referida al comienzo del fichero, a la posición en la que nos encontramos actualmente, o al final del fichero (entonces posic deberá ser negativo). Para no tener que recordar que un 0 quiere decir que nos referimos al principio, un 1 a la posición actual y un 2 a la final, tenemos definidas las constantes:

   SEEK_SET (0):  Principio
   SEEK_CUR (1):  Actual
   SEEK_END (2):  Final

Finalmente, si queremos saber en qué posición de un fichero nos encontramos, podemos usar "ftell(fichero)".

Pues esto es el manejo de ficheros en C. Ahora sólo queda elegir un proyecto en el que aplicarlos, y ponerse con ello.


Curso de C. Tema 12: Cadenas de texto.

El hecho de que este tema se encuentre al final no quiere decir que sea más difícil que los anteriores. De hecho, es más fácil que los ficheros, y desde luego mucho más fácil que dominar los punteros. Se trata simplemente de separarlo un poco del resto, porque he visto que mucha gente que viene de programar en lenguajes como Pascal tiende a hacer cosas como estas:

   texto = "Hola";

Es una forma muy legible de dar una valor a una cadena de texto... ¡en otros lenguajes! En C no se puede hacer así. Por eso he puesto este tema separado: sólo para recalcar que ciertas cosas no se pueden hacer.

Vamos a empezar por lo fundamental, que no se debe olvidar:

Una cadena de texto en C no es más que un array de caracteres. Como a todo array, se le puede reservar espacio estáticamente o dinámicamente. Ya lo vimos en el tema de punteros, pero insisto: estáticamente es haciendo cosas como

    char texto[80];

y dinámicamente es declarándola como puntero:

    char *texto;

y reservando memoria con "malloc" cuando nos haga falta.

En cualquier caso, una cadena de caracteres siempre terminará con un carácter nulo (\0). Por eso, si necesitamos 7 letras para un teléfono, deberemos hacer

   char telefono[8];

dado que hay que almacenar esas 7 y después un “\0”. Si sabemos lo que hacemos, podemos reservar sólo esas 7, pero tendremos que usar nuestras propias funciones, porque las que nos ofrece el lenguaje C se apoyan todas en que al final debe existir ese carácter nulo.

Ahora vamos ya con lo que es el manejo de las cadenas:

Para copiar el valor de una cadena de texto en otra, no podemos hacer texto1 = texto2; porque estaríamos igualando dos punteros. A partir de este momento, las dos cadenas se encontrarían en la misma posición de memoria, y los cambios que hiciéramos en una se reflejarían también en la otra.

En vez de eso, debemos usar una función de biblioteca, "strcpy" (string copy), que se encuentra, como todas las que veremos, en "string.h":

   strcpy (destino, origen);

Es nuestra responsabilidad que en la cadena de destino haya suficiente espacio reservado para copiar lo que queremos. Si no es así, estaremos sobreescribiendo direcciones de memoria en las que no sabemos qué hay.

Si queremos copiar sólo los primeros n bytes de origen, usamos

   strncpy (destino, origen, n);

Para añadir una cadena al final de otra (concatenarla), usamos

   strcat (origen, destino);

Para comparar dos cadenas alfabéticamente (para ver si son iguales o para poder ordenarlas, por ejemplo), usamos

   strcmp (cad1, cad2);

Esta función devuelve un número entero, que será: - 0 si ambas cadenas son iguales. - negativo, si cad1 < cad2. - positivo, si cad1 > cad2.

Según el compilador que usemos, tenemos incluso funciones ya preparadas para convertir una cadena a mayúsculas: strupr (esa línea del siguiente ejemplo funciona en TC y SC, pero no en GCC).

Estas son las principales posibilidades, aunque hay muchas más funciones (quien tenga curiosidad puede mirar la ayuda sobre "string.h" en su compilador favorito). Vamos a aplicarlas a un ejemplo:

  /*-------------------------*/
/*  Ejemplo en C nº 45:    */
/*  EJ45.C
*/
/*
*/
/*  Cadenas de texto
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*    - Symantec C++ 6.0   */
/*    - GCC 2.6.3
*/
/*-------------------------*/

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

  char texto1[80] = "Hola";
/* Cadena estática */
char *texto2;
/* Cadena dinámica */

  main()
  { /* Reservo espacio para la cadena dinámica */
    texto2 = (char *) malloc (70* sizeof(char));

    strcpy(texto2, "Adios");
/* Le doy un valor */
puts(texto1); puts(texto2);   /* Escribo
las dos */

    strncpy(texto1, texto2, 3);   /* Copio las 3 primeras letras */
    puts(texto1);                 /* Escribo la primera -> Adia */

    strcat(texto1, texto2);
/* Añado texto2 al final */
puts(texto1);
/* Escribo la primera -> AdiaAdios */

              /* Comparo alfabéticamente */
    printf("Si las comparamos obtenemos: %d",
      strcmp(texto1, texto2));
    printf(" (Número negativo: texto1 es menor)\n");

              /* Longitud */
    printf("La longitud de la primera es %d\n", strlen(texto1));

              /* Mayúsculas -No en GCC- */
    printf("En mayúsculas es %s.\n",
      strupr(texto1));

    free(texto2);
/* Libero lo reservado */
}

  /*-------------------------*/

Hay dos posibilidades más de las cadenas de texto que merece la pena comentar. Son las que nos ofrecen las funciones “sprintf” y “sscanf”:

La funcion “sprintf” crea una cadena de texto a partir de una especificación de formato y unos ciertos parámetros, al igual que hace “printf”, pero la diferencia está en que “printf” manda su salida a la pantalla, mientras que “sprintf” la deja guardada en una cadena de texto.

Por ejemplo, si escribimos

printf(”El número %d multiplicado por 2 vale %d\n”, 50, 50*2);

En pantalla aparecerá escrito

El número 50 multiplicado por 2 vale 100

Pues bien, si tenemos una cadena de texto que hayamos definido (por ejemplo) como char cadena[100] y escribimos

sprintf(cadena,”El número %d multiplicado por 2 vale %d\n”, 50, 50*2);

Esta vez en pantalla no aparece nada escrito, sino que “cadena” pasa a contener el texto que antes habíamos mostrado. Ahora ya podríamos escribir este texto con cualquiera de estas dos órdenes:

puts(cadena);
printf(”%s”, cadena);

Por otra parte “sscanf” es similar a “scanf”, con la diferencia de que los valores para las variables no se leen desde el teclado, sino desde una cadena de texto

strcpy(cadena, ”20 30”);
sscanf(cadena, ”%d %d”, &primerNum, &segundoNum);

Nota: sscanf devuelve el número de valores que realmente se han detectado, de modo que podemos comprobar si ha tomado todos los que esperábamos o alguno menos (porque el usuario haya tecleado menos de los que esperábamos o porque alguno esté tecleado incorrectamente).


Curso de C. Ampliaciones.

  • Pantalla en modo texto en Turbo C.
  • Pantalla en modo texto en Symantec C++.
  • Pantalla en modo texto en Linux.
  • Pantalla en modo gráfico en Turbo C usando las funciones estándar.
  • Pantalla en modo gráfico en Turbo C accediendo a la memoria de pantalla.
  • Pantalla en modo gráfico en Watcom C++.
  • Pantalla en modo gráfico en DJGPP usando GRX.
  • Pantalla en modo gráfico en DJGPP usando Allegro.
  • Funciones del DOS desde Turbo C.
  • Acceso a la impresora desde C.
  • Control del ratón en MsDos.
  • Programas a partir de varios fuentes.
  • Modelos de memoria.
  • Incluir ensamblador en C.
  • Leer la línea de comandos.
  • Funciones matemáticas en C.
  • Código ASCII.
  • Resumen rápido C -> Pascal.


Curso de C. Ampliaciones - Pantalla de texto en Turbo C.

Las funciones añadidas para manejo de la pantalla en Turbo C y superiores (Turbo C++ y Borland C++, en sus versiones para MsDos) se encuentran accesibles a través del fichero de cabecera "conio.h".

(Este mismo fichero puede estar disponible también para otros compiladores; por ejemplo, también viene con DJGPP)

En él encontramos funciones como:

  • gotoxy - Desplaza el cursor a ciertas coordenadas.
  • textcolor - Cambia el color del texto (el de primer plano).
  • textbackground - Cambia el color del texto (el de fondo).
  • textattr - Cambia el color (fondo y primer plano) del texto.
  • cprintf - Escribe un mensaje en color.
  • cputs - Escribe una cadena de texto en color.
  • clrscr - Borra la pantalla.
  • clreol - Borra hasta el final de la línea actual.
  • textmode - Cambia el modo de pantalla.
  • getche - Espera hasta que se pulse una tecla, y la muestra en pantalla.
  • getch - Espera hasta que se pulse una tecla, pero no la muestra en pantalla.
  • kbhit - Comprueba si se ha pulsado alguna tecla (pero no espera).
  • wherex - Devuelve la coordenada “x” (columna) en que se encuentra el cursor.
  • wherey - Devuelve la coordenada “y” (fila) en que se encuentra el cursor.
  • window - Permite crear una ventana dentro de la pantalla. A partir de entonces, las órdenes como "gotoxy" se referirán sólo a esa ventana, no al total de la pantalla.
  • ... (y otras muchas)

Los colores de la pantalla se indican por números. Por ejemplo: 0 es el negro, 1 es el azul oscuro, 2 el verde, 3 el azul claro, 4 el rojo, etc. Aun así, para no tener que recordarlos, tenemos definidas constantes con su nombre en inglés:

     BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY,
     DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA,
         YELLOW, WHITE.

Una última consideración: en MsDos sólo se pueden usar como colores de fondo los 7 primeros: desde BLACK hasta LIGHTGRAY. Esto se puede evitar con las pantallas VGA, a cambio de perder la posibilidad de que el texto parpadee, pero no voy a entrar en esos detalles. El caso es que "normalmente" si hacemos algo como

   textbackground(LIGHTBLUE);

no obtendremos los resultados esperados, sino que será como si hubiésemos utilizado el equivalente en el rango de 0 a 7:

   textbackground(BLUE);

Vamos a ver un ejemplo sencillo que use las funciones más elementales para cambiar colores, escribir usando esos colores y que sitúe el cursor en distintas posiciones de la pantalla.

  /*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo texto con TC++ */
/*  TXTC.C
*/
/*
*/
/*  Escribe una cadena de  */
/*  texto repetida en dos  */
/*  columnas, con distin-  */
/*  tos colores.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*-------------------------*/

  #include <conio.h>      /* Para funciones de pantalla */

  int i,j;                /* Para los bucles "for" */

  main()
{
textbackground(BLUE);
/* Fondo de la pantalla en azul */
clrscr();
/* Borro la pantalla */
for(i=0; i<=1; i++)
/* Dos columnas */
for(j=0; j<=15; j++)
/* Los 16 colores */
{
gotoxy(10+ 40*i , 3+j);
/* Coloco el cursor */
textcolor(j);
/* Elijo el color */
if (j == 0)
/* Si el color es 0 (negro) */
textbackground(LIGHTGRAY);
/*   dejo fondo gris */
else
/* Si es otro color */
textbackground(BLACK);
/*    dejo fondo negro */
cprintf(" Este es el
color %d ",j);    /* Escribo en color */
}
getch();
/* Final: espero a que se pulse una tecla, sin mostrarla */
}

  /*-------------------------*/

Esto es lo fundamental. Para ver cómo usar las otras órdenes, o conocer las que incluye y yo no he mencionado, basta recurrir a la ayuda en línea de Turbo C/C++/Borland C++: escribir la palabra que nos interese y pulsar Alt+F1 sobre ella.


Curso de C. Ampliaciones - Pantalla de texto en Symantec C++.

En el caso de Symantec C++, no existe el fichero de cabecera "conio.h", sino que en su lugar tenemos "disp.h", en la que encontramos casi las mismas funciones, pero con otros nombres. Por ejemplo:
  • disp_move - Desplaza el cursor a ciertas coordenadas.
  • disp_setattr - Cambia el color (fondo y primer plano) del texto.
  • disp_printf - Escribe un mensaje en color.
  • disp_puts - Escribe una cadena de texto en color.
  • disp_eeop - Borra hasta el final de la pantalla.
  • disp_eeol - Borra hasta el final de la línea actual.
  • disp_setmode - Cambia el modo de pantalla.
  • cursorrow - Nos indica la fila en que está el cursor.
  • cursorcol - Devuelve la columna en que está el cursor.
... (y otras muchas también)

Una nota importante: se debe inicializar la pantalla de texto antes de comenzar a trabajar con ella usando colores. Para eso es la orden:

   disp_open();

y dar por finalizado el uso de la pantalla de texto antes de abandonar nuestro programa, con:

   disp_close();

Otra consideración: las coordenadas X e Y en Turbo C y superiores comienzan en 1, mientras que en Symantec C++ empiezan en 0.

Así, un ejemplo sencillo usando las propias funciones de Symantec C++ sería:

  /*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo texto con SC++ */
/*  TXSC.C
*/
/*
*/
/*  Escribe un texto sen-  */
/*  cillo en color en el   */
/*  centro de la pantalla. */
/*
*/
/*  Comprobado con:
*/
/*    - Symantec C++ 6.0   */
/*-------------------------*/

  #include <disp.h>
  #include <stdlib.h>

  int main()
{
disp_open();
/* Paso a modo texto */
disp_move(0,0);
/* Voy al comienzo de la pantalla */
disp_eeop();
/* Y la borro */
disp_move(35,12);
/* Voy al centro de la pantalla */
disp_setattr(2 + 16);
/* Color verde con fondo azul */
disp_printf("Texto en SC++\n");
disp_close();
/* Se acabó */
}

  /*-------------------------*/

En mi caso, comencé a usar Symantec C++ porque podía crear programas en modo protegido del DOS (sin la limitación de 640K de memoria), pero la mayoría de mis fuentes en C estaban creados para Turbo C/C++. Me encontré con el problema de que tenía que cambiar mis fuentes si quería compilarlos con Symantec C++, así que opté por crearme una "imitación de conio.h" para Symantec C++.

La idea es de cómo copiar "conio.h" es sencilla:

  • "disp_printf" es idéntico a "cprintf"
  • "disp_move" es análogo a "gotoxy" con la única diferencia de que hay que restar 1 a las coordenadas (en SC++ se empieza a contar desde 0).
  • "disp_eeop" borra hasta el final de la pantalla, de modo que actuará como "clrscr" si previamente nos hemos desplazado a las coordenadas (0,0)
  • ...

Así que se puede hacer algo así:

  /*-------------------------*/
/*  NCONIO.H
*/
/*
*/
/*  Convierte las funcio-  */
/*  nes básicas de disp.h  */
/*  para que se parezcan   */
/*  más al conio.h de TC   */
/*
*/
/*  Comprobado con:
*/
/*    - Symantec C++ 6.0   */
/*-------------------------*/

  #include <disp.h>

  /*  Definición de constantes */

  enum COLORS {
      BLACK,          /* Colores oscuros -fondo- */
      BLUE,
      GREEN,
      CYAN,
      RED,
      MAGENTA,
      BROWN,
      LIGHTGRAY,
      DARKGRAY,       /* Colores claros -fondo y primer plano- */
      LIGHTBLUE,
      LIGHTGREEN,
      LIGHTCYAN,
      LIGHTRED,
      LIGHTMAGENTA,
      YELLOW,
      WHITE
  };

  #define BLINK       128 /* Bit de parpadeo */

  enum text_modes { LASTMODE=-1, BW40=0, C40, BW80, C80, MONO=7, C4350=64 };

  /* Definición de funciones */

  #define cprintf disp_printf
/* Las sencillas, como macros */
#define clreol  disp_eeol

  void gotoxy( int x, int y);
/* Las menos sencillas, detalladas */
void textattr( int attr );
void clrscr();
void textcolor( int pluma );
void textbackground( int fondo );

  void clrscr()
  {
    disp_move(0,0);
    disp_eeop();
  }

  void gotoxy( int x, int y)
  {
    disp_move( y-1, x-1 );
  }

  void textattr( int attr )
  {
    disp_setattr( attr );
  }

  void textcolor( int pluma )
  {
    int fondo = disp_getattr() / 16;
    disp_setattr( pluma + fondo*16 );
  }

  void textbackground( int fondo )
  {
    int pluma = disp_getattr() % 16;
    disp_setattr( pluma + fondo*16 );
  }

  /*-------------------------*/

He guardado todo en un fichero de cabecera (.H). Lo he hecho así por simplicidad y comodidad mía, pero si somos un poco puristas, esto no sería lo más correcto: en el fichero .H deberían estar sólo los prototipos ("las cabeceras") de las funciones, y los detalles en concreto de cada función deberían estar en otro fichero .C, como comentaremos en el tema que habla de programas creados a partir de varios fuentes

Y el programa análogo al visto para Turbo C, que probase este fichero de cabecera, sería:

  /*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo texto con SC++ */
/*  TXSC2.C
*/
/*
*/
/*  Escribe una cadena de  */
/*  texto repetida en dos  */
/*  columnas, con distin-  */
/*  tos colores.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Symantec C++ 6.0   */
/*-------------------------*/

  #include <nconio.h>     /* Para funciones de pantalla, tipo conio.h */

  int i,j;                /* Para los bucles "for" */

  main()
{
disp_open();
/* Inicializo la pantalla */
textbackground(BLUE);
/* Fondo de la pantalla en azul */
clrscr();
/* Borro la pantalla */
for(i=0; i<=1; i++)
/* Dos columnas */
for(j=0; j<=15; j++)
/* Los 16 colores */
{
gotoxy(10+ 40*i , 3+j);
/* Coloco el cursor */
textcolor(j);
/* Elijo el color */
if (j == 0)
/* Si el color es 0 (negro) */
textbackground(LIGHTGRAY);
/*   dejo fondo gris */
else
/* Si es otro color */
textbackground(BLACK);
/*    dejo fondo negro */
cprintf(" Este es el
color %d ",j);    /* Escribo en color */
}
getch();
/* Final: espero a que se pulse una tecla, sin mostrarla */
disp_close();  /* y indico que he terminado
de usar la pantalla */
}

  /*-------------------------*/


Curso de C. Ampliaciones - Pantalla de texto en GCC/Linux.

El caso de Linux se parece más al de Symantec que al de Turbo C. En Linux, hay una biblioteca llamada "curses", que es la que deberemos incluir si queremos usar algo más que el simple "printf": nos permitirá borrar la pantalla, escribir en unas ciertas coordenadas, dibujar recuadros, etc.

Al igual que en Symantec, debemos inicializar la pantalla antes de empezar a usarla, y liberarla al terminar, pero además, deberemos usar "refresh" cuando queramos actualizar la presentación en pantalla (hay que recordar que en Linux -y Unix en general- nos podemos encontrar con terminales lentos conectados a nuestro ordenador, a los que mandamos toda la información de golpe, y después les decimos que actualicen el contenido de la pantalla).

Veamos sólo un ejemplo sencillo:

  /*-------------------------*/
  /*  Ejemplo en C: pantalla */
  /*  en modo texto, Linux   */
  /*  TXLIN.C                */
  /*                         */
  /*  Escribe un texto sen-  */
  /*  cillo en ciertas coor- */
  /*  denadas de la pantalla */
  /*                         */
  /*  Comprobado con:        */
  /*    - GCC 2.6.3          */
  /*-------------------------*/

  #include <curses.h>

  main()
  {
    initscr();
    clear();
    mvaddstr(10,10,"hola");
    refresh();
    getch();
    endwin();
  }

  /*-------------------------*/

Creo que está claro, pero por si acaso:

- initscr Inicializa la pantalla.
- clear Borra la pantalla.
- mvaddstr Escribe una cadena de texto en ciertas coordenadas.
- refresh Actualiza el contenido de la pantalla.
- getch Espera a que se pulse una tecla.
- endwin Finaliza el uso directo de la pantalla.

Un detalle importante: a la hora de compilar hay que usar las siguientes opciones, para indicar al compilador que incluya las bibliotecas que hacen falta para acceder a la pantalla en modo texto mejorado (algo que no es necesario hacer en TC++ ni SC++):

  cc nombreFich.c -I/usr/include/ncurses -L/usr/lib -lncurses

Otro detalle importante: esta línea hay que escribirla tal cual, convervando las mayúsculas y minúsculas como están. Estos se debe a que Linux (y todos los sitemas Unix en general) sí distingue entre mayúsculas y minúculas, de modo que que un fichero llamado “nombreFich.c” y uno llamado “nombrefich.c” son distintos (y pueden existir los dos a la vez, mientras que en MsDos y Windows esto no es posible, ambos nombres se referirían al mismo fichero), y las opciones –I y –i se consideran opciones distintas (en los compiladores para MsDos y Windows también se suele distinguir entre mayúsculas y minúsculas a la hora de analizar las opciones que hemos tecleado... pero normalmente no “nos damos cuenta” porque los solemos usar desde un entrono integrado, que se encarga de todo, en vez de teclear nosotros toda esta serie de opciones “a mano”).

Esto es apenas lo fundamental. Como la mayoría de la gente no programa en Linux (Unix), no profundizo más. Con Linux vienen algunos ejemplos más sofisticados, y siempre se puede recurrir al "manual" (man) para saber más sobre alguna orden en concreto.


Curso de C. Ampliaciones - Pantalla gráfica en Turbo C.

Para la pantalla gráfica en Turbo C (o superiores) tenemos definidas también varias funciones, todas ellas en "graphics.h". Algunas son:

   move      - Se desplaza a un cierto punto.
   putpixel  - Dibuja un punto con un color dado.

   setcolor  - Elige el color en que se dibujará en pantalla.
   line      - Dibuja una línea entre dos puntos.
   circle    - Traza un círculo que tenga un cierto centro y radio.
   arc       - Crea un arco de círculo.

   initgraph - Pasa a modo gráfico.
   closegraph- Da por terminado el trabajo en modo gráfico.
   ...         (y otras muchas también)

En general, el manejo de la pantalla en modo gráfico es casi idéntico a como se hace en Turbo Pascal, así que vamos a ver directamente un ejemplo sencillo:

  /*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  Turbo C++
*/
/*  GRAFTC.C
*/
/*
*/
/*  Dibuja una línea de    */
/*  esquina a esquina de   */
/*  la pantalla.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*-------------------------*/

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

  void inicio(void)
{
int gdriver = DETECT,
/* Que detecte qué pantalla tengo */
gmode, codError;
/* Modo gráfico, código de error */

     initgraph(&gdriver, &gmode, "");
/* Intento pasar a modo gráfico */
codError = graphresult();
/* Miro si hay error */
if (codError != grOk)
/* Si lo hay... */
{
printf("Error de gráficos:
%s\n",         /* Aviso */
grapherrormsg(codError));
/* Muestro el error */
printf("Pulse una tecla
para salir...");
getch();
/* Espero */
exit(1);
/* y abandono el programa */
}
}

  int main(void)
/* Cuerpo del programa */
{
int xmax, ymax;
/* Variables locales: aquí guardaré
las coordenadas máximas de la pantalla */

     inicio();                  /* Paso a modo gráfico */

     setcolor(getmaxcolor());   /*
Elijo el último color (blanco) */
xmax = getmaxx();
/* Miro cuales son las coordenadas */
ymax = getmaxy();
/* máximas de la pantalla */

     line(0, 0, xmax, ymax);    /* Dibujo una diagonal */

     getch();
/* Espero a que se pulse una tecla */
closegraph();
/* Y termino con el modo gráfico */
return 0;
}

Una consideración: las funciones gráficas de Turbo C (y superiores) emplean la información que leen de unos "drivers" llamados BGI. Esto quiere decir que para que este programa funcione, el fichero EGAVGA.BGI (o el que corresponda, si nuestra pantalla no es EGA o VGA) deberá encontrarse en el mismo directorio que nuestro programa.

Si se encuentran en otro directorio, deberemos indicarle cual es, en la orden "initgraph":

     initgraph(&gdriver, &gmode, "c:\\tc\\bgi");

Recordemos que debemos usar dos barras (\\) en vez de una (\), porque la barra invertida servía para indicar ciertos códigos de control: en nuestro caso, si escribiésemos "c:\tc\bgi", el ordenador creería que nos referimos a: "c:" seguido de un tabulador (\t), de "c", de un retroceso (\b, que borraría la "c"), y finalmente de "gi": buscaría en un directorio llamado

    c:    gi

Nota: En la referencia hay más detalles sobre initgraph, sobre los drivers gráficos que se pueden emplear, y sobre los modos gráficos posibles.

Otra alternativa es no usar los BGI, sino crear gráficos escribiendo nosotros mismos en la memoria gráfica.

Gráficos en Turbo C (2) - Un programa de dibujo rudimentario.

Antes de seguir vamos a ver cómo sería un ejemplo de un programa de dibujo sencillo, que dibujara puntos en la pantalla, y que nosotros pudiéramos mover estos puntos con ciertas teclas, por ejemplo con 2, 4, 6 y 8 del teclado numérico.

  /*-------------------------*/
/*  Ejemplo en C: programa */
/*  rudimentario de dibujo */
/*  con Turbo C++
*/
/*  DIBTC.C
*/
/*
*/
/*  Dibuja puntos a medida */
/*  que nos desplazamos    */
/*  por la pantalla con    */
/*  las teclas 2,4,6,8     */
/*  Para finalizar, se usa */
/*  la 'f'.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*-------------------------*/

  #include <graphics.h>
  #include <conio.h>

  int driver = DETECT, modo, tecla, x, y;

  main()
  {
        initgraph(&driver, &modo, "");    /* Paso a modo gráfico */

        x = 320;
/* Comienzo en el centro */
y = 240;

        do {
           putpixel(x,y, 15);             /* Dibujo un punto en blanco */
           tecla = getch();               /* Miro la tecla pulsada */
           switch(tecla) {
              case '8': y = y-1; break;   /* Hacia arriba */
              case '4': x = x-1; break;   /* Izquierda */
              case '6': x = x+1; break;   /* Derecha */
              case '2': y = y+1; break;   /* Abajo */
           };

        }
while (tecla != 'f');
/* Hasta que se pulse la "f" */

        closegraph();
/* Y entonces termino */
return 0;
}

Gráficos en Turbo C (3) - Círculos.

Vamos a ver otro ejemplo sencillo, que dibuje una serie de círculos, cada uno ligeramente mayor que el anteiror y ligeramente desplazado, de forma que resulte un dibujo parecido a un cono.

El resultado que buscamos es algo parecido a esto:

Y lo podríamos conseguir así:

 /*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo grafico con    */
/*  Turbo C++
*/
/*  GRAFTC2.C
*/
/*
*/
/*  Cono formado por
*/
/*  varios circulos.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*-------------------------*/

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

 void inicio(void)
{
int gdriver = DETECT,
/* Que detecte que pantalla tengo */
gmode, codError;
/* Modo grafico, codigo de error */

    initgraph(&gdriver, &gmode, "");
/* Intento pasar a modo grafico */
codError = graphresult();
/* Miro si hay error */
if (codError != grOk)
/* Si lo hay... */
{
printf("Error de gr ficos:
%s\n",         /* Aviso */
grapherrormsg(codError));
/* Muestro el error */
printf("Pulse una tecla para
salir...");
getch();
/* Espero */
exit(1);
/* y abandono el programa */
}

 }

 int main(void)
/* Cuerpo del programa */
{
int i;
/* Para dibujar varios circulos */

    inicio();                  /* Paso a modo gr fico */

    setcolor(LIGHTRED);
/* Color rojo */
for( i=1; i<=40; i++)
/* Dibujaremos 40 circulos */
circle ( i*5, i*5, i*5 );

    getch();
/* Espero a que se pulse una tecla */
closegraph();
/* Y termino con el modo grafico */
return 0;
}

Gráficos en Turbo C (4) - Un efecto curioso.

Hemos visto cómo dibujar líneas, y sabemos (espero) qué es la operacion XOR entre dos dígitos binarios (la suma exclusiva: 0 xor 0 = 0, 0 xor 1 = 1, 1 xor 0 =1, 1 xor 1 = 0).

Pues bien, podemos conseguir un efecto curioso simplemente dibujando una serie de líneas desde el origen de coordenadas, pero diciendo a Turbo C que no sean líneas opacas, sino que vaya realizando una operación XOR con los puntos que ya había en pantalla.

Esta vez, el resultado que obtendremos será este:

Y lo conseguiríamos así:

 /*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  Turbo C++
*/
/*  GRAFTC3.C
*/
/*
*/
/*  Lineas dibujadas con   */
/*  XOR
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*-------------------------*/

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

 void inicio(void)
{
int gdriver = VGA,
/* Pantalla:  VGA */
gmode = VGAHI,
/* Modo gráfico: 640x480, 16c */
codError;
/* Código de error */

    initgraph(&gdriver, &gmode, "");
/* Intento pasar a modo gráfico */
codError = graphresult();
/* Miro si hay error */
if (codError != grOk)
/* Si lo hay... */
{
printf("Error de gráficos:
%s\n",         /* Aviso */
grapherrormsg(codError));
/* Muestro el error */
printf("Pulse una tecla para
salir...");
getch();
/* Espero */
exit(1);
/* y abandono el programa */
}

 }

 int main(void)
/* Cuerpo del programa */
{
int i;
/* Para bucles */

    inicio();                  /* Paso a modo gráfico */

    setcolor(LIGHTRED);
/* Color: rojo claro */
setwritemode(XOR_PUT);
/*-- Este es el "truco" */
for (i=0; i<639; i++)
/* Dibujaremos lineas */
line (0,0,i,479);
for (i=0; i<479; i++)
/* y mas lineas */
line (0,0,639,i);

    getch();
/* Espero a que se pulse una tecla */
closegraph();
/* Y termino con el modo gráfico */
return 0;
}

CURSO DE C. FUNCIONES GRAFICAS EN TURBO C.

Aquí están todas (o casi) las funciones gráficas de Turbo C++ 2nd Edition, ordenadas alfabéticamente. Estas funciones deberían poder utilizarse en cualquier versión de Turbo C, Turbo C++ o Borland C++ que cree programas para MsDos.

Para más detalles sobre cualquiera de ellas, no tiene más que teclearla en el editor de Turbo C y pulsar las teclas Ctr+F1. Aparecerá un descripción detallada... en inglés, eso sí.

  • arc - Dibuja un arco.
  • bar - Dibuja una barra (rectángulo relleno).
  • bar3d - Dibuja una barra en 3D (con profundidad).
  • circle - Dibuja un círculo.
  • cleardevice - Borra la pantalla gráfica (la rellena con el color actual de fondo).
  • clearviewport - Borra la ventana gráfica actual.
  • closegraph - Termina el modo gráfico: libera la memoria que hubiese reservado, y deja la pantalla en el modo en que se encontraba antes de pasar a modo gráfico.
  • detectgraph - Detecta la tarjeta gráfica que tenemos instalada.
  • drawpoly - Dibuja un polígono formado por varios puntos.
  • ellipse - Dibuja una elipse o un arco de elipse.
  • fillellipse - Dibuja una elipse rellena.
  • fillpoly - Dibuja un polígono relleno.
  • floodfill - Rellena una zona de la pantalla que tenga un borde.
  • getarccoords - Devuelve información sobre el último arco realizado con "arc."
  • getaspectratio - Devuelve la relación de aspecto del modo gráfico actual
  • getbkcolor - Indica el color de fondo actual.
  • getcolor - Devuelve el color de primer plano actual.
  • getdefaultpalette - Devuelve la paleta de colores por defecto.
  • getdrivername - Devuelve el nombre del "driver" gráfico actual.
  • getfillpattern - Devuelve el patrón de relleno actual.
  • getfillsettings - Devuelve el estilo de relleno actual.
  • getgraphmode - Indica el modo gráfico actual.
  • getimage - Captura una imagen de la pantalla y la guarda en memoria.
  • getlinesettings - Devuelve las características del tipo de línea actual: color, grosor y patrón.
  • getmaxcolor - Devuelve el número de color más alto que podemos emplear.
  • getmaxmode - Devuelve el número del modo gráfico más alto que podemos emplear con un cierto "driver".
  • getmaxx - Devuelve la máxima coordenada "x" (el número de columnas de la pantalla gráfica).
  • getmaxy - Devuelve la máxima coordenada "y" (el número de filas de la pantalla gráfica).
  • getmodename - Devuelve el nombre del modo gráfico actual.
  • getmoderange - Devuelve el rango de modos gráficos que podemos emplear con un cierto "driver": el más alto y el más bajo.
  • getpalette - Devuelve la paleta actual.
  • getpalettesize - Indica el número de entradas (colores) que existen en la paleta actual.
  • getpixel - Indica el color de un punto de la pantalla.
  • gettextsettings - Devuelve información sobre el tipo de letra actual.
  • getviewsettings - Devuelve información sobre la ventana gráfica actual.
  • getx - Devuelve la coordenada x de la posición actual.
  • gety - Devuelve la coordenada x de la posición actual.
  • graphdefaults - Deja toda la configuración en sus valores por defecto (ventana, paleta, tipo de letra, patrón y color).
  • grapherrormsg - Devuelve la cadena de texto que corresponde a un cierto número de mensaje de error.
  • _graphfreemem - Libera memoria que se haya reservado con "_graphgetmem".
  • _graphgetmem - Otras funciones la usan internamente para reservar memoria. Se puede redefinir para que esas funciones accedan a nuestra propia rutina de reserva de memoria, en vez de la que TC incluye por defecto.
  • graphresult - Indica el código de error de la última operación de gráficos.
  • imagesize - Indica la cantidad de memoria que es necesaria para almacenar una imagen con "getimage".
  • initgraph - Comienza el modo gráfico.
  • installuserdriver - Permite añadir otro driver BGI a los que trae Turbo C++.
  • installuserfont - Permite añadir otro tipo de letra a los que incorpora Turbo C++.
  • line - Dibuja una línea entre dos puntos dados.
  • linerel - Dibuja una línea hasta una cierta distancia contada a partir de la posición anterior.
  • lineto - Dibuja una línea desde la posición actual hasta un punto dado.
  • moverel - Se desplaza una cierta distancia, contada desde la posición actual.
  • moveto - Se desplaza a una cierta posición de la pantalla.
  • outtext - Escribe un texto en la posición actual.
  • outtextxy - Escribe un texto en una cierta posición de la pantalla.
  • pieslice
  • putimage - Copia a la pantalla una imagen que se había guardado con "getimage".
  • putpixel - Dibuja un punto en cierta posición de la pantalla y con un color dado.
  • rectangle - Dibuja un rectángulo entre dos puntos dados.
  • registerbgidriver - Permite acceder a drivers BGI que se hayan incluido dentro del propio fichero EXE.
  • registerbgifont - Permite acceder a tipos de letra que se hayan incluido dentro del propio fichero EXE.
  • restorecrtmode - Vuelve al modo texto que existía cuando se realizó "initgraph". Está pensado para cambiar de modo texto a modo gráfico, en conjunción con "setgraphmode".
  • sector - Dibuja un sector de elipse.
  • setactivepage - Cambia la página gráfica en que se escribe (sólo para los modos en que tengamos más de una página de pantalla).
  • setallpalette - Cambia todos los colores de la paleta por otros que se indiquen.
  • setaspectratio - Cambia la relación de aspecto actual, para conseguir que los círculos queden totalmente redondos.
  • setbkcolor - Fija el color de fondo.
  • setcolor - Fija el color actual.
  • setfillpattern - Elige un patrón de relleno.
  • setfillstyle - Elige el patrón y el color de relleno.
  • setgraphbufsize - Permite cambiar el tamaño de un buffer (memoria intermedia) que utilizan ciertas funciones (como floodfill), para que podamos hacerlo más grande (si obtenemos algún error de falta de memoria) o más pequeño (si consideramos que nos sobra).
  • setgraphmode - Cambia a un cierto modo gráfico, sin cambiar de "driver".
  • setlinestyle - Escoge el estilo con que se dibujarán las líneas.
  • setpalette - Cambia un color de la paleta.
  • setrgbpalette - Cambia las componentes R, G, B (rojo, verde y azul) de un color de la paleta.
  • settextjustify - Cambia la alineación del texto: derecha, izquierda o centrado (en horizontal), o bien inferior, superior o centrado (en vertical).
  • settextstyle - Cambia las características del texto: tipo de letra, dirección y tamaño.
  • setusercharsize - Cambia el tamaño en horizontal y/o vertical de un tipo de letra (multiplicando o dividiendo por un cierto número).
  • setviewport - Define una ventana gráfica dentro de la pantalla.
  • setvisualpage - Dice cual de las páginas disponibles (en los modos gráficos que tienen más de una página) es la que debe mostrarse.
  • setwritemode - Permite poner los puntos de forma "normal" o haciendo un XOR con los que ya hay en la pantalla.
  • textheight - Devuelve la altura en puntos de una frase, dependiendo de la frase, del tipo de letra y el tamaño escogidos.
  • textwidth - Devuelve la anchura en puntos de una frase, dependiendo de la frase, del tipo de letra y el tamaño escogidos.

También puede ver un ejemplo de cómo representar gráficamente funciones matemáticas y un apartado sobre cómo utilizar la pantalla en modo gráfico con Watcom C++.


Curso de C. Ampliaciones - Pantalla gráfica accediendo a memoria.

Este método tiene al menos tres ventajas:
  • No necesitamos distribuir los ficheros BGI con nuestro programa.
  • Es más rápido que los BGI (si sabemos manejarlo bien, claro).
  • Podemos usar otros modos de pantalla para los cuales no dispongamos de los BGI.

Pero tiene también algún inconveniente claro
  • Hay que saber cómo se maneja la pantalla en modo gráfico, lo cual depende mucho de la tarjeta gráfica que tengamos y del modo en concreto que queramos usar.
  • No tenemos ninguna orden predefinida para dibujar círculo, elipses, arcos, rellenar zonas, ni siquiera para dibujar líneas. Tendremos que aprender a pintar un punto en pantalla y avanzar nosotros mismos a partir de ahí.

Por eso, nos centraremos en el modo más vistoso de una tarjeta VGA "normal", que además es el más fácil de programar: el modo de 320x200 puntos con 256 colores (modo 13h).

En este modo, tenemos 200 líneas, cada una de las cuales tiene 320 puntos. A su vez, cada punto puede tener 256 colores, luego necesitamos un byte por cada punto de la pantalla. Por tanto, una pantalla ocupa:

   320 x 200 x 1 = 64.000 bytes.

Estos 64.000 bytes se encuentran en el segmento A000h, comenzando justo en la posición 0.

Por tanto, para acceder a la pantalla, podríamos hacer algo como

   char pantalla[64000];

pero diciendo que esos 64.000 bytes los queremos en la posición A000:0000.

En la práctica, como no necesitamos "reservar" esos 64.000 bytes, nos basta con

   char *pantalla;

(Un puntero a caracteres: una posición de memoria que contendrá caracteres -bytes-) y luego decirle que queremos que esa posición de memoria sea la A000:0000, lo que conseguimos con

    MK_FP (0xa000, 0)

MK_FP es la abreviatura de "Make Far Pointer", crear puntero lejano, para indicar que queremos mirar en otro segmento de la memoria.

Esto nos obliga a tener que compilar el programa en un modelo
de memoria que nos permita más de 64K de datos, como es el caso
del modelo "Compact" y superiores.  Este programa no funcionará
en los modelos "Tiny", "Small" ni "Medium".

Para compilarlo con Turbo C++ en modelo Compact desde fuera del editor,
tendríamos que teclear:

tcc -mc grafmem.c

Veamos el programa y después comentamos los pasos que va dando:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico acce-  */
/*  diendo a memoria.
*/
/*  GRAFMEM.C
*/
/*
*/
/*  Dibuja una línea de    */
/*  diagonal en la pan-    */
/*  talla.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*-------------------------*/

#include <dos.h>
/* Usaremos las funciones del DOS */

#define ALTO  200
/* La pantalla tiene 200 filas */
#define ANCHO 320
/* y 320 columnas */

main()
{
char *pantalla;
/* Para acceder a la pantalla */
int i;
/* Para repetir con un "for" */
union REGS r;
/* Para usar los registros */

/* Digo donde comienza la pantalla */
pantalla = (char *) MK_FP (0xa000, 0);

r.x.ax = 0x13;
/* En AX pongo 13h, el modo de pantalla */
int86( 0x10, &r, &r);
/* Cambio a ese modo con la int 10h */

for(i=0; i<ALTO; i++)    /*
Escribo 200 puntos */
pantalla[i*ANCHO +i]=i;
return 0;
/* Y se acabó */
}

Creo que sólo hay dos líneas que merezcan ser explicadas:

pantalla = (char *) MK_FP (0xa000, 0);
pantalla[i*ANCHO +i]=i;

La primera línea ya se ha comentado antes: es la que nos permite
decir en qué posición de la memoria física  de
nuestro PC queremos situar esa variable llamada "pantalla":  MK_FP
es la abreviatura de "Make Far Pointer": crear un puntero lejano (lejano
se refiere a que puede estar en cualquier segmento
de la memoria, no necesariamente en el segmento actual de datos).
Hay que indicarle el segmento y el desplazamiento,
que en este caso son A000:0000 (el comienzo de la pantalla gráfica
en una tarjeta VGA).

Pero hay una cosas "rara": eso de (char *).  La idea es
que MK_FP nos devuelve "pantalla" como un "void *", un puntero a un dato
de tipo "void", pero nosotros queríamos que fuera un puntero a tipo
"char" (char *), para acceder a la pantalla byte a byte, como queríamos.
Por eso hacemos una conversión de tipos (en inglés typecast),
para que realmente lo interprete como un valor del tipo que nos interesa.

La segunda línea es la que dibuja puntos en una línea diagonal hacia abajo. La idea es que para dibujar un punto en cualquier posición de la pantalla usaremos:

  pantalla [ fila*ANCHO + columna ] = color;

(tendremos que avanzar saltar varias filas, de un cierto ancho, y luego varias casillas -columnas- de la última fila).

De igual modo, podríamos leer el color de cualquier punto de la pantalla gráfica con:

  color = pantalla [ fila*ANCHO + columna ];

A continuación veremos como este ejemplo, con algunos cambios, (tampoco demasiados) se puede hacer funcionar bajo DJGPP.

(Nota: Puede ver también un ejemplo mucho más avanzado, de Figuras que se mueven por la pantalla).

Esta es la versión para DJGPP del fuente que dibuja una línea
accediendo directamente a la memoria de pantalla

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico acce-  */
/*  diendo a memoria.
*/
/*  (Versión para DJGPP)   */
/*  GRAFMDJ.C
*/
/*
*/
/*  Dibuja una línea de    */
/*  diagonal en la pan-    */
/*  talla.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Djgpp 2.01
*/
/*-------------------------*/

#include <dos.h>
/* Usaremos las funciones del DOS */
#include <go32.h>
/* y del extensor del DOS */

#define ALTO  200
/* La pantalla tiene 200 filas */
#define ANCHO 320
/* y 320 columnas */

main()
{
char *pantalla;
/* Para acceder a la pantalla */
int i;
/* Para repetir con un "for" */
union REGS r;
/* Para usar los registros */

r.x.ax = 0x13;
/* En AX pongo 13h, el modo de pantalla */
int86( 0x10, &r, &r);
/* Cambio a ese modo con la int 10h */

for(i=0; i<ALTO; i++)    /*
Escribo 200 puntos */
_farpokeb(_dos_ds, 0xa0000 + i*ANCHO
+i, i);

return 0;
/* Y se acabó */
}

La diferencia fundamental es que ahora no hemos creado ningún
array llamado “pantalla” que represente a la zona de memoria ocupada por
la pantalla VGA, sino que nos hemos limitado a modificar bytes directamente
en esa zona de memoria empleando la orden “_farpokeb” que incorpora DJGPP.

Puede ver también un ejemplo mucho más avanzado (realizado
con Turbo C++), de Figuras que se mueven por la pantalla.


Curso de C. Ampliaciones - Pantalla gráfica en Watcom C++.

En el caso de Watcom C++, las funciones para utilizar la pantalla en modo gráfico se encuentran definidas en "graph.h". Al igual que ocurre con otros compiladores, su nombre comienza con un símbolo de subrayado, para indicar que se trata de funciones que no son estándar. Las primeras que veremos son:

   _moveto       - Se desplaza a un cierto punto.
   _lineto       - Dibuja una línea desde la posición actual hasta un cierto punto.
   _setvideomode - Cambia a un cierto modo de pantalla.

Vamos a verlos con un ejemplo:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  Watcom C++
*/
/*  GRAFWC1.C
*/
/*
*/
/*  Dibuja una línea
*/
/*  diagonal en la pan-    */
/*  talla.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Watcom C++ 11.0c   */
/*-------------------------*/

#include <graph.h>

main()
{
  _setvideomode( _MRES256COLOR );
  _moveto( 20, 10);
  _lineto ( 310, 175);
}

Tenemos muchísimos modos de pantalla disponibles, muchos más que en Turbo C++ (incluso para tarjetas SuperVGA, siguiendo el estándar VESA), y además no necesitaremos distribuir ficheros BGI, al contrario de lo que ocurre con Turbo C. Los modos de pantalla que podemos usar son:

 Constante    Valor     Resolución y cantidad de colores
-----------   -----    -------------------------------------
_TEXTBW40       0       (40 x 25 -texto-, 16 tonos de gris)
_TEXTC40        1       (40 x 25 -texto-, 16/8 colores)
_TEXTBW80       2       (80 x 25 -texto-, 16 tonos de gris)
_TEXTC80        3       (80 x 25 -texto-, 16/8 colores)
_MRES4COLOR     4       (320 x 200, 4 colores)
_MRESNOCOLOR    5       (320 x 200, 4 tonos de gris)
_HRESBW         6       (640 x 200, B/N)
_TEXTMONO       7       (80 x 25 -texto-, B/N)
_HERCMONO       11      (720 x 350, B/N)
_MRES16COLOR    13      (320 x 200, 16 colores)
_HRES16COLOR    14      (640 x 200, 16 colores)
_ERESNOCOLOR    15      (640 x 350, B/N)
_ERESCOLOR      16      (640 x 350, 4 o 16 colores)
_VRES2COLOR     17      (640 x 480, B/N)
_VRES16COLOR    18      (640 x 480, 16 colores)
_MRES256COLOR   19      (320 x 200, 256 colores)
_URES256COLOR   0x100   (640 x 400, 256 colores)
_VRES256COLOR   0x101   (640 x 480, 256 colores)
_SVRES16COLOR   0x102   (800 x 600, 16 colores)
_SVRES256COLOR  0x103   (800 x 600, 256 colores)
_XRES16COLOR    0x104   (1024 x 768, 16 colores)
_XRES256COLOR   0x105   (1024 x 768, 256 colores)
_YRES16COLOR    0x106   (1280 x 1024, 16 colores)
_YRES256COLOR   0x107   (1280 x 1024, 256 colores)
_SVTEXTC80X60   0x108   (80 x 60 -texto-)
_SVTEXTC132X25  0x109   (132 x 25 -texto-)
_SVTEXTC132X43  0x10A   (132 x 43 -texto-)
_SVTEXTC132X50  0x10B   (132 x 50 -texto-)
_SVTEXTC132X60  0x10C   (132 x 60 -texto-)
_MRES32KCOLOR   0x10D   (320 x 200, 32K colores)
_MRES64KCOLOR   0x10E   (320 x 200, 64K colores)
_MRESTRUECOLOR  0x10F   (320 x 200, color real -true color-)
_VRES32KCOLOR   0x110   (640 x 480, 32K colores)
_VRES64KCOLOR   0x111   (640 x 480, 64K colores)
_VRESTRUECOLOR  0x112   (640 x 480, 16.8M colores)
_SVRES32KCOLOR  0x113   (800 x 600, 32K colores)
_SVRES64KCOLOR  0x114   (800 x 600, 64K colores)
_SVRESTRUECOLOR 0x115   (800 x 600, 16.8M colores)
_XRES32KCOLOR   0x116   (1024 x 768, 32K colores)
_XRES64KCOLOR   0x117   (1024 x 768, 64K colores)
_XRESTRUECOLOR  0x118   (1024 x 768, 16.8M colores)
_YRES32KCOLOR   0x119   (1280 x 1024, 32K colores)
_YRES64KCOLOR   0x11A   (1280 x 1024, 64K colores)
_YRESTRUECOLOR  0x11B   (1280 x 1024, 16.8M colores)
_ZRES256COLOR   0x11C   (1600 x 1200, 256 colores)
_ZRES32KCOLOR   0x11D   (1600 x 1200, 32K colores)
_ZRES64KCOLOR   0x11E   (1600 x 1200, 64K colores)
_ZRESTRUECOLOR  0x11F   (1600 x 1200, 16.8M colores)

Y otras de las órdenes que podemos emplear son:

   _setcolor     - Escoge el color con el que se dibujará
   _getcolor     - Lee el color en uso
   _setpixel     - Dibuja un punto con el color actual
   _ellipse      - Dibuja una elipse
   _rectangle    - Dibuja un rectángulo dadas dos esquinas
   _floodfill    - Rellena una zona de la pantalla
   _arc          - Dibuja un arco de círculo
   _setlinestyle - Indica el patrón que se usará para dibujar líneas no contínuas

Además, existe la posibilidad de definir una cierta ventana, y de usar órdenes referidas a esta ventana: _arc_w en vez de _arc, _lineto_w en lugar de _lineto, y así sucesivamente.

Por otra parte, las órdenes como _rectangle y _ellipse tienen un primer parámetro, para indicar si queremos que la figura que se dibuja esté rellena (_GFILLINTERIOR) o si será sólo el contorno (_GBORDER).

Así, un ejemplo algo más completo sería:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  Watcom C++
*/
/*  GRAFWC2.C
*/
/*
*/
/*  Dibuja varias figuras  */
/*  en la pantalla
*/
/*
*/
/*  Comprobado con:
*/
/*    - Watcom C++ 11.0c   */
/*-------------------------*/

#include <graph.h>
#include <conio.h>

main()
{
  _setvideomode( _MRES256COLOR );
  _moveto( 20, 10);
  _lineto ( 310, 175);

  _setcolor( 14 );
  _moveto( 639, 0);
  _lineto( 0, 479);

  _setcolor( 3 );
  _rectangle( _GFILLINTERIOR, 30, 30, 300, 200);

  _setcolor( 11 );
  _ellipse (_GBORDER, 320, 200, 50, 100);

  getch();
  _setvideomode( _TEXTC80 );
}


Curso de C. Ampliaciones - Pantalla gráfica en DJGPP usando GRX.

El compilador DJGPP no incluye rutinas gráficas "de base", pero se ha extendido tanto que existen muchos "toolkits" adicionales, varios de los cuales son para añadir la posibilidad de crear gráficos. Los más extendidos son GRX y Allegro, que tienen orientaciones ligeramente distintas: GRX está pensado para crear y manipular gráficos sencillos en dos dimensiones, mientras que Allegro es una biblioteca mucho más completa, pensada para la creación de juegos, e incluye rutinas de 3D, de sonido, etc.

Ambas bibliotecas de funciones se pueden descargar desde la misma página Web que DJGPP (www.delorie.com/djgpp) y se distribuyen como código fuente, que deberemos compilar nosotros mismos (y que, de hecho, se puede compilar también usando MinGW si queremos crear programas para Windows, o con GCC bajo Linux si queremos crear programas para este sistema operativo.

Yo comentaré el caso de la última versión de GRX disponible en Enero de 2003: la 2.4.5.

Compilar esta biblioteca con DJGPP no tiene mayor dificultad: lo descomprimimos (conservando la estructura de directorios) dentro de la carpeta en la que hemos instalado DJGPP, y se creará una carpeta CONTRIB, y dentro de ella otra llamada GRX245. Pues bien, entramos a la carpeta SRC que hay dentro de esta última, que es donde se encuentran los fuentes, y tecleamos:

MAKE -F MAKEFILE.DJ2

y ya está. Al cabo de un rato, tendremos la librería creada. Para copiar los ficheros a las correspondientes carpetas de nuestro compilador, podemos hacerlo a mano, o simplemente teclear

MAKE -F MAKEFILE.DJ2 INSTALL

Nota: en mi versión de DJGPP (2.03, basada en GCC 3.2), no compilaba correctamente la biblioteca GRX, diciendo que había una incompatibilidad de tipos entre STDIO.H y STDARG.H, y buscaba este último fichero en una carpeta un poco "extraña". El problema se solucionó simplemente añadiendo al principio de STDIO.H estas dos líneas:

#include <stdarg.h>
#include <stddef.h>

Ya debería estar listo para usar. Vamos a ver un ejemplo de cómo cambiar a modo gráfico y dibujar una diagonal:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  DJGPP 2.03 y GRX 2.4.5 */
/*  GRAFDJG1.C
*/
/*
*/
/*  Dibuja una línea
*/
/*  diagonal en la pan-    */
/*  talla.
*/
/*
*/
/*  Comprobado con:
*/
/*  - Djgpp 2.03 (gcc 3.2) */
/*-------------------------*/

#include <grx20.h>
#include <grxkeys.h>

main()
{
  GrSetMode(GR_320_200_graphics);
  GrLine( 20, 10,  310, 175,  15);

  GrKeyRead();
}

Y para compilarlo teclearíamos:

gcc grafdjg1.c -o grafdjg1.exe -lgrx20

Nota: si estuvieramos compilando para Windows con MinGW, el cuerpo del programa debería llamarse "GRXMain" el vez de "main"; para DJGPP podemos usar cualquiera de ambos nombres indistintamente.

Demos un repaso rápido a las posibilidades más básicas de esta biblioteca.

  • Dibujar un punto en un cierto color: void GrPlot(int x,int y,GrColor c);
  • Dibujar una línea: void GrLine(int x1,int y1,int x2,int y2,GrColor c);
  • Línea horizontal: void GrHLine(int x1,int x2,int y,GrColor c);
  • Línea vertical: void GrVLine(int x,int y1,int y2,GrColor c);
  • Recuadro: void GrBox(int x1,int y1,int x2,int y2,GrColor c);
  • Círculo: void GrCircle(int xc,int yc,int r,GrColor c);
  • Elipse: void GrEllipse(int xc,int yc,int xa,int ya,GrColor c);
  • Arco de elipse: void GrEllipseArc(int xc,int yc,int xa,int ya, int start,int end,int style,GrColor c);
  • Polígono: void GrPolygon(int numpts,int points[][2],GrColor c);

y en cuanto a figuras rellenas:

  • Recuadro relleno: void GrFilledBox(int x1,int y1,int x2,int y2,GrColor c);
  • Círculo relleno: void GrFilledCircle(int xc,int yc,int r,GrColor c);
  • Elipse rellena: void GrFilledEllipse(int xc,int yc,int xa,int ya,GrColor c);
  • Sector circular: void GrFilledCircleArc(int xc,int yc,int r, int start,int end,int style,GrColor c);
  • Sector de elipse: void GrFilledEllipseArc(int xc,int yc,int xa,int ya, int start,int end,int style,GrColor c);
  • Polígono relleno: void GrFilledPolygon(int numpts,int points[][2],GrColor c);
  • Polígono convexo relleno: void GrFilledConvexPolygon(int numpts,int points[][2],GrColor c);

También podemos rellenar una cierta zona de la pantalla con

  • void GrFloodFill(int x, int y, GrColor border, GrColor c);

O leer el color de un punto con

  • GrColor GrPixel(int x,int y);

Y borrar pantalla con:

  • void GrClearScreen(GrColor bg);

Para elegir modo de pantalla se usa la rutina que ya hemos visto:

  • int GrSetMode(int which,...);

El parámetro "which" (qué modo) puede ser uno de los siguientes:

typedef enum _GR_graphicsModes {
  GR_80_25_text,
  GR_default_text,
  GR_width_height_text,
  GR_biggest_text,
  GR_320_200_graphics,
  GR_default_graphics,
  GR_width_height_graphics,
  GR_biggest_noninterlaced_graphics,
  GR_biggest_graphics,
  GR_width_height_color_graphics,
  GR_width_height_color_text,
  GR_custom_graphics,
  GR_width_height_bpp_graphics,
  GR_width_height_bpp_text,
  GR_custom_bpp_graphics,
  GR_NC_80_25_text,
  GR_NC_default_text,
  GR_NC_width_height_text,
  GR_NC_biggest_text,
  GR_NC_320_200_graphics,
  GR_NC_default_graphics,
  GR_NC_width_height_graphics,
  GR_NC_biggest_noninterlaced_graphics,
  GR_NC_biggest_graphics,
  GR_NC_width_height_color_graphics,
  GR_NC_width_height_color_text,
  GR_NC_custom_graphics,
  GR_NC_width_height_bpp_graphics,
  GR_NC_width_height_bpp_text,
  GR_NC_custom_bpp_graphics,
} GrGraphicsMode;

Una forma "normal" de usarlo sería la que hemos visto (forzar el modo de 320x200 puntos), y otra podría ser indicando la anchura, la altura y el número de bits por pixel (bpp: 8 para 256 colores, 16 para 65.536 colores, 32 para color real). Así, podríamos pasar al modo de 800x600 puntos con 256 colores haciendo

GrSetMode(GR_width_height_bpp_graphics, 800, 600, 8);

Un ejemplo algo más completo sería:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  DJGPP 2.03 y GRX 2.4.5 */
/*  GRAFDJG2.C
*/
/*
*/
/*  Dibuja varias figuras  */
/*  en la pantalla
*/
/*
*/
/*  Comprobado con:
*/
/*  - Djgpp 2.03 (gcc 3.2) */
/*-------------------------*/

#include <grx20.h>
#include <grxkeys.h>

main()
{
  GrSetMode(GR_width_height_bpp_graphics, 640, 480, 8);

  GrLine( 20, 10,  310, 175,  15);
  GrLine( 639, 0,    0, 479,  14);

  GrFilledBox(30, 30, 300, 200, 3);

  GrEllipse (320, 200, 50, 100, 11);

  GrKeyRead();
  GrSetMode(GR_default_text);
}

Hay muchas más posibilidades: limitar la escritura a ciertas zonas de la pantalla, controlar el ratón, capturar partes de la pantalla, utilizar distintos tipos de letra, mostrar ficheros JPG y/o PNG, etc. Pero la mayoría de ellas son bastante más complejas de manejar.

Quien quiera profundizar más, tiene en la carpeta DOC de GRX245 el manual de usuario, en formato HTML (en inglés, eso sí).


Curso de C. Ampliaciones - Pantalla gráfica en DJGPP usando Allegro.

Allegro, al igual que GRX, un "toolkit" adicional, que permite crear gráficos (y muchas más cosas) utilizando DJGPP. Realmente Allegro va mucho más allá que GRX: es una biblioteca mucho más completa, pensada para la creación de juegos, e incluye rutinas de 3D, de sonido, etc.

Como GRX, se puede descargar desde la misma página Web que DJGPP (www.delorie.com/djgpp) y se distribuye como código fuente, que deberemos compilar nosotros mismos (y que también se puede compilar también usando MinGW si queremos crear programas para Windows, o con GCC bajo Linux si queremos crear programas para este sistema operativo.

Yo comentaré el caso de la última versión de Allegro disponible en Enero de 2003: la 4.02.

Compilar esta biblioteca con DJGPP no es difícil si cumplimos los requisitos básicos (DJGPP 2.01 o superior, basado en el compilador GCC 2.9.1 o superior). Descomprimimos Allegro (conservando la estructura de directorios) dentro de la carpeta en la que hemos instalado DJGPP, y se creará una carpeta CONTRIB, y dentro de ella otra llamada ALLEGRO. Pues bien, entramos a esta carpeta ALLEGRO y tecleamos:

MAKE

Ni más ni menos. Al cabo de un buen rato, tendremos la librería creada. Si obtuvieramos algún mensaje de error sería normalmente porque falta alguna parte adicional de DJGPP que no hemos instalado (Make, binutils o cualquier otra). Hay una lista más detallada del proceso de instalación en la carpeta DOCS\BUILD\DJGPP.TXT.

Nota: en mi versión de DJGPP (2.03, basada en GCC 3.2), no compilaba correctamente la biblioteca ALLEGRO, diciendo que había una incompatibilidad de tipos entre STDIO.H y STDARG.H, y buscaba este último fichero en una carpeta un poco "extraña". El problema se solucionó simplemente añadiendo al principio de STDIO.H estas dos líneas:

#include <stdarg.h>
#include <stddef.h>

Al igual que ocurre con GRX, para copiar los ficheros a las correspondientes carpetas de nuestro compilador, no es necesario hacerlo a mano, basta con teclear

MAKE INSTALL

Ya debería estar listo para usar. Vamos a ver un ejemplo de cómo cambiar a modo gráfico y dibujar una diagonal:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  DJGPP 2.03 y
*/
/*  Allegro 4.02:
*/
/*    GRAFDJA1.C
*/
/*
*/
/*  Dibuja una línea
*/
/*  diagonal en la pan-    */
/*  talla.
*/
/*
*/
/*  Comprobado con:
*/
/*  - Djgpp 2.03 (gcc 3.2) */
/*-------------------------*/

#include <allegro.h>
#include <conio.h>

main()
{

  allegro_init();

  if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0) {
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
allegro_message("Incapaz de entrar a modo grafico\n%s\n",
allegro_error);
return 1;
}

  line(screen, 20, 10,  310, 175, palette_color[15]);
  getch();
}

END_OF_MAIN();

Y para compilarlo teclearíamos:

gcc grafdja1.c -o grafdja1.exe -lalleg

Este ejemplo hace algo más que el ejemplo equivalente de GRX: comprobamos si realmente se puede cambiar a ese modo gráfico, y avisamos en caso de que no sea posible. De igual modo, la orden de dibujar líneas tiene un parámetro adicional en el caso de Allegro: dónde queremos dibujar esa línea (en este caso, en la pantalla: "screen").

Por lo demás, también se ve la forma de inicializar la librería Allegro: con "allegro:init( )" al comienzo, y la forma en que debe terminar el programa: con la macro "END_OF_MAIN();" al final.

Demos un repaso rápido a las posibilidades más básicas de esta biblioteca, en lo que se refiere a modo gráfico.

  • Dibujar un punto en un cierto color: void putpixel(BITMAP *bmp, int x, int y, int color);
  • Dibujar una línea: void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
  • Línea horizontal: void hline(BITMAP *bmp, int x1, int y, int x2, int color);
  • Línea vertical: void vline(BITMAP *bmp, int x, int y1, int y2, int color);
  • Recuadro: void rect(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
  • Recuadro relleno: void rectfill(BITMAP *bmp, int x1, int y1, int x2, int y2, int color);
  • Círculo: void circle(BITMAP *bmp, int x, int y, int radius, int color);
  • Círculo relleno: void circlefill(BITMAP *bmp, int x, int y, int radius, int color);
  • Elipse: void ellipse(BITMAP *bmp, int x, int y, int rx, int ry, int color);
  • Elipse rellena: void ellipsefill(BITMAP *bmp, int x, int y, int rx, int ry, int color);
  • Arco circular: void arc(BITMAP *bmp, int x, y, fixed ang1, ang2, int r, int color);
(Como hemos comentado antes, el primer parámetro "BITMAP *bmp" especifica dónde se dibujará cada una de esas figuras; para nosotros lo habitual será indicar "screen" -la pantalla-).

También podemos rellenar una cierta zona de la pantalla con

  • void floodfill(BITMAP *bmp, int x, int y, int color);

O leer el color de un punto con

  • int getpixel(BITMAP *bmp, int x, int y);

Para elegir modo de pantalla se usa la rutina que ya hemos visto:

  • int set_gfx_mode(int card, int w, int h, int v_w, int v_h);

El parámetro "card" (tarjeta) normalmente debería GFX_AUTODETECT (autodetección); w y h son la anchura (width) y altura (height) en puntos, y v_w y v_h son la anchura y altura de la pantalla "virtual", más grande que la visible, que podríamos utilizar para hacer algún tipo de scroll, y que nosotros no usaremos por ahora.

Pero... ¿y el número de colores? Se indica con

  • void set_color_depth(int depth);
Donde "depth" es la "profundidad" de color en bits. El valor por defecto es 8 (8 bits = 256 colores), y otros valores posibles son 15, 16, 24 y 32 bits.

Ahora vamos a ver un ejemplo algo más completo:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  DJGPP 2.03 y
*/
/*  Allegro 4.02:
*/
/*    GRAFDJA2.C
*/
/*
*/
/*  Dibuja varias figuras  */
/*  en la pantalla
*/
/*
*/
/*  Comprobado con:
*/
/*  - Djgpp 2.03 (gcc 3.2) */
/*-------------------------*/

#include <allegro.h>
#include <conio.h>

main()
{

  allegro_init();

  set_color_depth(16);
if (set_gfx_mode(GFX_SAFE, 640, 480, 0, 0) != 0) {
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
allegro_message("Incapaz de entrar a modo grafico\n%s\n",
allegro_error);
return 1;
}

  line(screen, 20, 10,  310, 175, palette_color[15]);
  line(screen, 639, 0,    0, 479, palette_color[14]);

  rectfill(screen, 30, 30, 300, 200, palette_color[3]);

  ellipse (screen, 320, 200, 50, 100, palette_color[11]);

  getch();
}

END_OF_MAIN();

Hay muchas más posibilidades: limitar la escritura a ciertas zonas de la pantalla, controlar el ratón, capturar partes de la pantalla, desplazar la pantalla (scroll), mostrar ficheros JPG y/o PNG, reproducir animaciones FLI, reproducir sonidos, controlar el Joystick, etc. Pero mi intención no es profundizar tanto. Al menos no en este curso....


Curso de C. Ampliaciones - Funciones del DOS con Turbo C.

En Turbo C (y superiores) tenemos acceso también a distintos servicios del DOS. Entre ellos tenemos: control de la fecha y la hora del sistema, leer el contenido de un directorio, leer o cambiar los atributos de un fichero, hacer pausas durante un cierto tiempo, llamar a las interrupciones del DOS, etc., e incluso otras posibilidades mucho más avanzadas, como leer ciertos sectores del disco, leer la información de la FAT (la tabla de situación de ficheros), redefinir los vectores de interrupciones, etc.

La mayoría de estas funciones se encuentran en "dos.h", y aquellas que se refieren en concreto a directorios (por ejemplo, para ver el contenido de un cierto directorio) están en "dir.h".

Como primer ejemplo, vamos a ver cómo obtener la fecha, la hora y el espacio que nos queda libre en el disco C:

  /*-------------------------*/
  /*  Ejemplo en C: acceder  */
  /*  a los servicios del    */
  /*  Dos desde Turbo C++    */
  /*  DOSTC.C                */
  /*                         */
  /*  Muestra fecha, hora    */
  /*  y espacio libre en     */
  /*  el disco duro.         */
  /*                         */
  /*  Comprobado con:        */
  /*    - Turbo C++ 1.01     */
  /*    - Djgpp 2.01         */
  /*-------------------------*/

  #include <dos.h>

  main()
  {
     struct time  hora;          /* Los datos que voy a leer: hora */
     struct date  fecha;                                  /* Fecha */
     struct dfree libreC;   /* Espacio libre en C, como estructura */

     long espacioLibre;                /* Espacio libre, en kBytes */
     long espacioTotal;                /* Espacio total, en kBytes */

     gettime(&hora);                              /* Leo los datos */
     getdate(&fecha);
     getdfree(3, &libreC);

     espacioLibre = (long) libreC.df_avail * (long) libreC.df_sclus *
       (long) libreC.df_bsec / 1024;
     espacioTotal = (long) libreC.df_total * (long) libreC.df_sclus *
       (long) libreC.df_bsec / 1024;

     printf("Son las %d horas y %d minutos",      /* Muestro los datos */
       hora.ti_hour, hora.ti_min);
     printf(" del día %d/%d/%d.\n",
       fecha.da_day, fecha.da_mon, fecha.da_year);
     printf("Espacio libre en el disco C: %ld de %ldK\n",
       espacioLibre, espacioTotal);

     delay(2000);                                 /* Espero 2 segundos */
  }

Comentarios a este programa:

  • Para leer la hora, debemos usar un tipo especial de datos: una estructura llamada "struct time". Por eso, declaramos la variable "hora" con
       struct time hora;
Esta estructura tiene los campos: ti_hour (hora), ti_min (minutos), ti_sec (segundos), ti_hund (centésimas de segundo). De estos campos, sólo he usado el el ejemplo dos ti_hour y "ti_min"

En la llamada a la función "gettime" también hay algo raro:

      gettime(&hora);
Ese símbolo de & antes de la variable "hora" aparece porque "gettime" espera que le digamos la dirección en que queremos guardar los datos. Esto no es nuevo para nosotros, porque ya lo habíamos usado con "scanf" cuando vimos la entrada/salida básica.
  • Para leer la fecha, es similar: debemos usar el tipo de datos llamado "struct date", por lo que declaramos la variable "fecha" con

      struct date fecha;
Esta estructura tiene los campos: da_year (año), da_mon (mes), da_day (segundos). Igual que en "gettime", hay que indicarle la dirección en que queremos guardar los datos, y por eso usamos "getdate(&fecha)".
  • Finalmente, dfree también es una estructura, que tiene los siguientes campos:
      df_avail: número de clusters disponibles.
      df_total: número total de clusters.
      df_sclus: número de sectores por cluster en la unidad.
      df_bsec:  número de bytes en cada sector.
Por tanto, para hallar los bytes libres, usaremos:
     df_avail * df_sclus * df_bsec
y para los KBytes libres, dividimos los bytes libres entre 1024.

De igual modo, el espacio total se halla con:
      df_total * df_sclus * df_bsec
Añadimos "(long)" para que trate esos valores como números "enteros largos" antes de multiplicar, y así asegurarnos que no haya ningún desbordamiento (por ejemplo: los "enteros" sólo llegan hasta 32.767 en muchos compiladores, de modo que la operación 1000 x 1000 desborda

Ahora vamos a ver cómo acceder a las interrupciones de MsDos. Esto nos permitiría hacer casi cualquier cosa, pero vamos a limitarnos a un ejemplo sencillo: la interrupción 10h es la encargada de manejar la pantalla. Usaremos dos de sus servicios: la función 00 para cambiar el modo de pantalla y la función 02 para colocarnos en una cierta posición de la pantalla:

  /*-------------------------*/
  /*  Ejemplo en C: acceder  */
  /*  a las interrupc. del   */
  /*  Dos desde Turbo C++    */
  /*  INTTC.C                */
  /*                         */
  /*  Cambia a modo de 40    */
  /*  columnas, se coloca    */
  /*  en el centro de la     */
  /*  pantalla y escribe un  */
  /*  mensaje.               */
  /*                         */
  /*  Comprobado con:        */
  /*    - Turbo C++ 1.01     */
  /*-------------------------*/

  #include <dos.h>

  #define VIDEO 0x10              /* La interrupción de video es la 10h */

  modoPantalla(int m)
  {
     union REGS regs;

     regs.h.ah = 0;               /* Función 0: modo de pantalla */
     regs.h.al = m;               /* En AL: el modo que queremos */
     int86(VIDEO, &regs, &regs);  /* Llamo a la interrupción de vídeo */
  }

  posicion(int x, int y)          /* Me coloco en un punto de la pantalla */
  {
     union REGS regs;

     regs.h.ah = 2;               /* Función 2: posición del cursor */
     regs.h.dh = y;               /* Fila */
     regs.h.dl = x;               /* Columna */
     regs.h.bh = 0;               /* Página de pantalla: 0 (primera) */
     int86(VIDEO, &regs, &regs);  /* Llamo a la interrupción de vídeo */
  }

  main()
  {
     modoPantalla(1);             /* Modo de pantalla 1: 40x25 color */
     posicion(17, 12);            /* Cerca del centro de la pantalla */
     printf("Hola!");            /* Escribo el mensaje */
     getch();                     /* Espero una tecla */
     modoPantalla(3);             /* Vuelvo a modo 3: 80x25 color */
  }

El ratón en MsDos también se controla mediante interrupciones.

Eso sí: no es sencillo manejar las interrupciones. Una recopilación muy interesante es la "Lista de interrupciones de Ralf Brown".

Vamos a ver cómo leer la lista de ficheros que hay en un directorio, usando las funciones de "dir.h":

  /*-------------------------*/
  /*  Ejemplo en C: acceder  */
  /*  al contenido de un     */
  /*  directorio en Turbo C  */
  /*  DIRTC.C                */
  /*                         */
  /*  Comprobado con:        */
  /*    - Turbo C++ 1.01     */
  /*    - Djgpp 2.01         */
  /*-------------------------*/

  #include <dir.h>

  main()
  {
     struct ffblk  datos;                       /* Cada dato que leo */
     int terminado;                             /* Para ver si he acabado */

     printf("Contenido del directorio (*.*)\n");
     terminado = findfirst("*.*",&datos,0);     /* Busco el primero */
     while (!terminado)                         /* Mientras queden */
     {
        printf("%s\n", datos.ff_name);          /* Escribo el nombre */
        terminado = findnext(&datos);           /* y busco el siguiente */
     }
  }

Esta vez hemos usado otro tipo de datos: la estructura "ffblk", que almacena los datos de los ficheros que encuentren "findfirst" y "findnext". Esta estructura tiene campos como ff_name (nombre del fichero), ff_fdate (fecha de creación/modificación del fichero), ff_fattribs (atributos) y ff_fsize (tamaño del fichero).


Curso de C. Ampliaciones - Manejo de la impresora.

En C, la impresora se maneja como un fichero en el que vamos escribiendo. La única diferencia es que este "fichero" tiene un nombre especial reservado en MsDos: PRN (en general) o bien LPT1 (la primera conexión para impresora) o LPT2 (la segunda conexión de impresora).

  /*-------------------------*/
  /*  Ejemplo en C: mandar   */
  /*  un texto sencillo a la */
  /*  impresora              */
  /*  IMPRC.C                */
  /*                         */
  /*  Comprobado con:        */
  /*    - Turbo C++ 1.01     */
  /*    - Djgpp 2.01         */
  /*    - Symantec C++ 6.0   */
  /*-------------------------*/

  #include <stdio.h>

  main()
  {
     FILE* impresora;

     impresora = fopen("PRN","w");     /* Abro impresora como fichero */

     fprintf(impresora,"Un texto");    /* Imprimo algo */
     fprintf(impresora," y otro\n");   /* y más en la misma línea */
     fprintf(impresora,"Y otro más.\n");    /* y en otra línea */
     fprintf(impresora,"\f");          /* Expulso la hoja */

     fclose(impresora);                /* Y termino con la impresora */
  }

Este programa debe ser bastante fácil de entender. Quizás el único problema se encuentre en ese "\f" que se usa para expulsar la hoja. Es una de las secuencias de escape que vimos en el tema 4.

En algunos compiladores (como Turbo C) existe también un identificador reservado para la impresora: "stdprn".


Curso de C. Ampliaciones - Ratón en MsDos.

En Turbo C, no tenemos funciones que nos permitan acceder al ratón directamente. Esto debemos conseguirlo mediante interrupciones. En concreto, la interrupción del ratón es la 33h, que nos ofrece muchos servicios. Los más importantes, agrupados por categorías, son:

Control del ratón.
INT 33,0 Reinicializa el ratón / Comprueba si hay ratón instalado

Control del cursor del ratón.
INT 33,1 Muestra el cursor del ratón
INT 33,2 Oculta el cursor del ratón
INT 33,4 Fija la posición del cursor.
INT 33,7 Fija la posición máxima y mínima en horizontal
INT 33,8 Fija la posición máxima y mínima en vertical

Posición del ratón y estado de los botones.
INT 33,3 Información sobre la posición y el estado de los botones.
INT 33,5 Información sobre si un botón se ha pulsado.
INT 33,6 Información sobre si un botón se ha dejado de pulsar.

También hay otros muchos servicios para cambiar la forma del cursor en modo texto y en modo gráfico, para cambiar la "velocidad" del ratón (la cantidad de espacio de mesa que es necesario para moverlo), para cambiar el lenguaje en que saldrán los mensajes (si el driver lo permite), etc.

Para más detalles, lo adecuado sería consultar una guía más detallada, como puede ser la lista de interrupciones de Ralf Brown, el HelpPC, o quizás incluso el propio manual del ratón.

Un programita sencillo de ejemplo podría ser el siguiente: (aunque compilado con Djgpp no detecta correctamente el número de botones).

  /*-------------------------*/
  /*  Ejemplo en C: acceder  */
  /*  al ratón en MsDos      */
  /*  RATON.C                */
  /*                         */
  /*  Comprobado con:        */
  /*    - Turbo C++ 1.01     */
  /*    - Djgpp 2.01         */
  /*-------------------------*/
  #include <dos.h>
  #include <conio.h>

  #define RATON 0x33      /* El número de la interrupcion del ratón */

  int HayRaton(int *botones)
  {
      union REGS reg;

      reg.x.ax = 0;                    /* Función 0 */
      int86(RATON,&reg,&reg);        /* Llamamos a la interrupción */
      *botones = reg.x.bx;             /* BX dice si tiene 2 ó 3 botones */
      return(reg.x.ax);                /* -1 en AX si hay ratón; 0 si no */
  }

  void MuestraRaton()
  {
      union REGS reg;

      reg.x.ax = 1;
      int86(RATON,&reg,&reg);
  }

  void OcultaRaton()
  {
      union REGS reg;

      reg.x.ax = 2;
      int86(RATON,&reg,&reg);
  }

  int EstadoRaton(int *PosH, int *PosV)
  {
      union REGS reg;

      reg.x.ax = 3;
      int86(RATON,&reg,&reg);
      *PosH = reg.x.cx;            /* Posición horizontal */
      *PosV  = reg.x.dx;           /* Posición vertical */
      return(reg.x.bx);            /* Estado de los botones: si el bit
                  0 está a 1 indica que se ha pulsado
                  el botón izquierdo; el bit 2 indica
                  el botón derecho, y el bit 3 el
                  botón central, si existe */
  }

  void SituaRaton(int PosH, int PosV)
  {
        union REGS reg;

        reg.x.ax = 4;
        reg.x.cx = PosH;
        reg.x.dx = PosV;
        int86(RATON,&reg,&reg);
  }

  void RestringeRatonH(int min, int max)
  {
        union REGS reg;

        reg.x.ax = 7;
        reg.x.cx = min;
        reg.x.dx = max;
        int86(RATON,&reg,&reg);
  }

  void RestringeRatonV(int min, int max)
  {
        union REGS reg;

        reg.x.ax = 8;
        reg.x.cx = min;
        reg.x.dx = max;
        int86(RATON,&reg,&reg);
  }

  main()         /* Programa que prueba estas funciones */
  {
      int botones, estado, posH, posV;

      if (HayRaton(&botones))
      {
        clrscr();
        printf("Se ha encontrado un ratón con %d botones.\n", botones);
        printf("Pulse el botón derecho para salir.");
        SituaRaton(320,200);
        RestringeRatonH(100,540);
        RestringeRatonV(50,150);
        MuestraRaton();
        do
        {
           estado = EstadoRaton(&posH, &posV);
           if (estado & 1)
           {
              OcultaRaton();
              gotoxy(10,10);
              cprintf("Se ha pulsado el botón izquierdo en %d,%d.  ",
               posH, posV);
              MuestraRaton();
           }
        }
        while ((estado & 2) == 0);
      }
      return(0);
  }

Es obligado hacer algún comentario extra sobre este programa:
  • Las coordenadas inicialmente suelen basarse en una pantalla de 640x200, como se verá al probar el programa. Si se quiere trabajar en modo texto y que las coordenadas sean "un poco más normales", se deberá dividir entre 8 los valores obtenidos.
  • Conviene ocultar el cursor antes de escribir (o dibujar) algo en la pantalla y volverlo a mostrar después, porque si escribimos algo en la posición del ratón sin ocultarlo previamente, se quedará el "hueco" cuando el ratón se mueva.
  • Si se oculta el ratón dos veces seguidas, no aparecerá hasta que se llame a MuestraRaton otras dos veces.
  • Este programa debería funcionar con muy pocos cambios en otros compiladores para MsDos: bastaría cambiar las órdenes de manejo de pantalla (clrscr, gotoxy, cprintf) por sus equivalentes.


Curso de C. Ampliaciones - Programas a partir de varios fuentes.

CURSO DE C. PROGRAMAS A PARTIR DE VARIOS FUENTES.

Vamos a empezar por el caso más sencillo: supongamos que tenemos un programa principal, y otros módulos auxiliares en los que tenemos algunas funciones que se usarán desde el programa principal.

Por ejemplo, el primer módulo (UNO.C) podría ser simplemente:

  void uno()
  {
     printf("Función uno\n");
  }

y el segundo módulo (DOS.C):

  void dos(int numero)
  {
     printf("Función dos, con el parámetro %d\n", numero);
  }

Un programa principal simple, que los utilizase, sería (TRES.C):

  #include <stdio.h>

  int main()
  {
     printf("Estamos en el cuerpo del programa.\n");
     uno();
     dos(3);
     return 0;
  }

Para compilar los 3 programas desde Turbo C++ 1.01, bastaría con escribir desde la línea de comandos del MsDos:

   TCC UNO DOS TRES

y entonces el compilador convierte los tres ficheros fuente (.C) a ficheros objeto (.OBJ), los enlaza y crea un único ejecutable, que se llamaría UNO.EXE (porque UNO es el nombre del primer fuente que hemos indicado al compilador), y cuya salida en pantalla sería:

   Estamos en el cuerpo del programa.
   Función uno
   Función dos, con el parámetro 3

Lo mismo ocurre si usamos Symantec C++, también desde la línea de órdenes de MsDos, con:

   SCC UNO DOS TRES

En el caso de GCC, tendremos que indicarle el nombre de los ficheros de entrada (con extensión) y el nombre del fichero de salida:

   gcc uno.c dos.c tres.c  -o resultado

Pero puede haber compiladores en los que la situación no sea tan sencilla. Puede ocurrir que al compilar el programa principal, que era:

  #include <stdio.h>

  int main()
  {
     printf("Estamos en el cuerpo del programa.\n");
     uno();
     dos(3);
     return 0;
  }

el compilador nos dé un mensaje de error, diciendo que no conoce las funciones "uno()" y "dos()".

La forma de evitarlo sería indicándole que esas funciones existen, y que ya le llegarán más tarde los detalles en concreto sobre cómo funcionan.

Para decirle que existen, lo que haríamos sería incluir en el programa principal los prototipos (las cabeceras, sin el desarrollo) de las funciones que se encuentran en otros módulos, así:

  #include <stdio.h>

  void uno();              /* Prototipos de las funciones externas */
  void dos(int numero);

  int main()               /* Cuerpo del programa */
  {
     printf("Estamos en el cuerpo del programa.\n");
     uno();
     dos(3);
     return 0;
  }

Esta misma solución de poner los prototipos al principio del programa nos puede servir para casos en los que, teniendo un único fuente, queramos declarar el cuerpo del programa antes que las funciones auxiliares:

  /*-------------------------*/
  /*  Ejemplo en C: prototi- */
  /*  pos de funciones, para */
  /*  poder declararlas des- */
  /*  pués del cuerpo del    */
  /*  programa.              */
  /*  PROTO.C                */
  /*                         */
  /*  Comprobado con:        */
  /*    - Turbo C++ 1.01     */
  /*    - Djgpp 2.01         */
  /*    - Symantec C++ 6.0   */
  /*-------------------------*/

  #include <stdio.h>

  void uno();              /* Prototipos de las funciones */
  void dos(int numero);

  int main()               /* Cuerpo del programa */
  {
     printf("Estamos en el cuerpo del programa.\n");
     uno();
     dos(3);
     return 0;
  }

  void uno()
  {
     printf("Función uno\n");
  }

  void dos(int numero)
  {
     printf("Función dos, con el parámetro %d\n", numero);
  }

En ciertos compiladores puede que tengamos problemas con este programa si no incluimos los prototipos al principio, porque en "main()" se encuentra la llamada a "uno()", que no hemos declarado. Al poner los prototipos antes, el compilador ya sabe qué tipo de función es "uno()" (sin parámetros, no devuelve ningún valor, etc.), y que los datos concretos los encontrará más adelante.

De hecho, si quitamos esas dos líneas, este programa no compila en Turbo C++ 1.01 ni en Symantec C++ 6.0, porque cuando se encuentra en "main()" la llamada a "uno()", da por supuesto que va a ser una función de tipo "int". Como después le decimos que es "void", protesta. (En cambio, GCC, que suele ser más exigente, en este caso se limita a avisarnos, pero compila el programa sin problemas).

La solución habitual en estos casos en que hay que declarar prototipos de funciones (especialmente cuando se trata de funciones compartidas por varios fuentes) suele ser agrupar estos fuentes en "ficheros de cabecera". Por ejemplo, podríamos crear un fichero llamado EJEMPLO.H que contuviese:

  /*  EJEMPLO.H */

  void uno();              /* Prototipos de las funciones */
  void dos(int numero);

y en el fuente principal escribiríamos:

  #include <stdio.h>
  #include "ejemplo.h"

  int main()
  {
     printf("Estamos en el cuerpo del programa.\n");
     uno();
     dos(3);
     return 0;
  }

Aquí es importante la diferencia en la forma de indicar los dos ficheros de cabecera, como ya vimos en el tema 3:

   <stdio.h>      Se indica entre corchetes angulares porque el fichero de
                  cabecera es propio del compilador (el ordenador lo buscará
                  en los directorios del compilador).

   "ejemplo.h"    Se indica entre comillas porque el fichero H es nuestro
                  (el ordenador lo buscará en el mismo directorio que están
                  nuestros fuentes).

Finalmente, conviene hacer una consideración: si varios fuentes distintos necesitaran acceder a EJEMPLO.H, deberíamos evitar que este fichero se incluyese varias veces. Esto se suele conseguir definiendo una variable simbólica la primera vez que se enlaza, de modo que podamos comprobar a partir de entonces si dicha variable está definida. Puede sonar difícil, pero no lo es. Se haría así:

  /* EJEMPLO.H mejorado */

  #ifndef _EJEMPLO_H
  #define _EJEMPLO_H

  void uno();              /* Prototipos de las funciones */
  void dos(int numero);

  #endif

Introducción al uso de la herramienta Make.

Make es una herramienta que muchos compiladores incorporan y que nos puede ser de utilidad cuando se trata de proyectos de un cierto tamaño, o al menos creados a partir de bastantes fuentes.

Su uso normal consiste simplemente en teclear MAKE. Entonces la herramienta Make buscará su fichero de configuración, un fichero de texto que deberá llamarse "makefile" (podemos darle otro nombre; ya veremos cómo). Este fichero de configuración le indica las dependencias entre ficheros, es decir, qué ficheros es necesario utilizar para crear un determinado "objetivo". Esto permite que no se recompile todos y cada uno de los fuentes si no es estrictamente necesario, sino sólo aquellos que se han modificado desde la última compilación.

En general, el contenido del fichero "makefile" será algo parecido a esto:

objetivo: dependencias
    órdenes

(En la primera línea se escribe el objetivo, seguido por dos puntos y por la lista de dependencias; en la segunda línea se escribe la orden que hay que dar en caso de que sea necesario recompilar, precedida por algún espacio en blanco o algún tabulador). Si queremos añadir algún comentario, basta con precederlos con el símbolo #.

Vamos a crear un ejecutable llamado PRUEBA.EXE a partir de cuatro ficheros fuente llamados UNO.C, DOS.C, TRES.C, CUATRO.C

Así, nuestro primer "makefile" podría ser un fichero que contuviese el siguiente texto:

PRUEBA.EXE: UNO.C DOS.C TRES.C CUATRO.C
    TCC -ePRUEBA.EXE UNO.C DOS.C TRES.C CUATRO.C

Es decir nuestro objetivo es conseguir un fichero llamado PRUEBA.EXE, que queremos crear a partir de varios ficheros llamados UNO.C, DOS.C, TRES.C y CUATRO.C. La orden que queremos dar es la que aparece en la segunda línea, y que permite, mediante el compilador TCC, crear un ejecutable llamado PRUEBA.EXE a partir de cuatro fuentes con los nombres anteriores. (La opción "-e" de Turbo C++ permite indicar el nombre que queremos que tenga el ejecutable; si no, se llamaría UNO.EXE, porque tomaría su nombre del primero de los fuentes).

¿Para qué nos sirve esto? De momento, nos permite ahorrar tiempo: cada vez que tecleamos MAKE, se lee el fichero MAKEFILE y se compara la fecha (y hora) del objetivo con la de las dependencias; si el fichero objetivo no existe o si es más antiguo que alguna de las dependencias, se realiza la orden que aparece en la segunda línea (de modo que evitamos escribirla completa cada vez).

En nuestro caso, cada vez que tecleemos MAKE, ocurrirá una de estas tres posibilidades

  • Si no existe el fichero PRUEBA.EXE, se crea uno nuevo utilizando la orden de la segunda línea.
  • Si ya existe y es más reciente que los cuatro fuentes, no se recompila ni se hace nada, todo queda como está.
  • Si ya existe, pero se ha modificado alguno de los fuentes, se recompilará de nuevo para obtener un fichero PRUEBA.EXE actualizado.

Eso sí, estoy dando por supuesto varias cosas "casi evidentes":

  • Que tenemos la herramienta MAKE y está accesible (en el directorio actual o en el PATH).
  • Que hemos creado el fichero MAKEFILE.
  • Que existen los cuatro ficheros fuente UNO.C, DOS.C, TRES.C y CUATRO.C.
  • Que existe el compilador TCC y está accesible (en el directorio actual o en el PATH).

Vayamos mejorando este MAKEFILE rudimentario. La primera mejora es que si la lista de dependencias no cabe en una única linea, podemos partirla en dos, empleando la barra invertida \

PRUEBA.EXE: UNO.C DOS.C \       #Objetivo y dependencias
    TRES.C CUATRO.C                      # Mas dependencias
    TCC -ePRUEBA.EXE UNO.C DOS.C TRES.C CUATRO.C    #Orden a dar

Al crear el MAKEFILE habíamos ganado en "velocidad de tecleo" y en que no se recompilase todo nuevamente si no se ha modificado nada. Pero en cuanto un fuente se modifica, nuestro MAKEFILE recompila todos otra vez, aunque los demás no hayan cambiado. Esto podemos mejorarlo añadiendo un paso intermedio (la creación cada fichero objeto OBJ) y más objetivos (cada fichero OBJ, a partir de cada fichero fuente), así:

# Creacion del fichero ejecutable

prueba.exe: uno.obj dos.obj tres.obj
    tcc -eprueba.exe uno.obj dos.obj tres.obj

# Creacion de los ficheros objeto

uno.obj: uno.c
    tcc -c uno.c

dos.obj: dos.c
    tcc -c dos.c

tres.obj: tres.c
    tcc -c tres.c

Estamos detallando los pasos que normalmente se dan al compilar, y que muchos compiladores realizan en una única etapa, sin que nosotros nos demos cuenta: primero se convierten los ficheros fuente (ficheros con extensión C) a código máquina (código objeto, ficheros con extensión OBJ) y finalmente los ficheros objeto se enlazan entre sí (y con las bibliotecas propias del lenguaje) para dar lugar al programa ejecutable (en MsDos y Windows normalmente serán ficheros con extensión EXE).

Así conseguimos que cuando modifiquemos un único fuente, se recompile sólo este (y no todos los demás, que pueden ser muchos) y después se pase directamente al proceso de enlazado, con lo que se puede ganar mucho en velocidad si los cambios que hemos hecho al fuente son pequeños.

(Nota: la opción "-c" de Turbo C++ es la que le indica que sólo compile los ficheros de C a OBJ, pero sin enlazarlos después).

Si tenemos varios MAKEFILE distintos (por ejemplo, cada uno para un compilador diferente, o para versiones ligeramente distintas de nuestro programa), nos interesará poder utilizar nombres distintos.

Esto se consigue con la opción "-f" de la orden MAKE, por ejemplo si tecleamos

MAKE -fPRUEBA

la herramienta MAKE buscaría un fichero de configuración llamado PRUEBA o bien PRUEBA.MAK.

Podemos mejorar más aún estos ficheros de configuración. Por ejemplo, si precedemos la orden por @, dicha orden no aparecerá escrita en pantalla

PRUEBA.EXE: UNO.C DOS.C TRES.C CUATRO.C
    @TCC -ePRUEBA.EXE UNO.C DOS.C TRES.C CUATRO.C

Y si precedemos la orden por & , se repetirá para los ficheros indicados como "dependencias". Hay que usarlo en conjunción con la macro $**, que hace referencia a todos los ficheros dependientes, o $?, que se refiere a los ficheros que se hayan modificado después que el objetivo.

copiaSeguridad: uno.c dos.c tres.c
    &copy $** a:\fuentes

Una última consideración: podemos crear nuestras propias macros, con la intención de que nuestro MAKEFILE resulte más fácil de leer y de mantener, de modo que una versión más legible de nuestro primer fichero sería:

FUENTES = uno.c dos.c tres.c
COMPIL = tcc

prueba.exe: $(FUENTES)
$(COMPIL) -eprueba.exe $(FUENTES)

Es decir, las macros se definen poniendo su nombre, el signo igual y su definición, y se emplean precediéndolas de $ y encerrándolas entre paréntesis.

Pero todavía hay más que no hemos visto. Las herramientas MAKE suelen permitir otras posibilidades, como la comprobación de condiciones (con las directivas "!if", "!else" y similares) o la realización de operaciones (con los operadores estándar de C: +, *, %, >>, etc). Quien quiera profundizar en estos y otros detalles, puede recurrir al manual de la herramienta MAKE que incorpore su compilador.

Introducción a los “proyectos”.

En la gran mayoría de los compiladores que incorporan un “entorno de desarrollo”, existe la posibilidad de conseguir algo parecido a lo que hace la herramienta MAKE, pero desde el propio entorno. Es lo que se conoce como “crear un proyecto”.

Para ello, se accede al menú “Projects”, donde existirá una opción “Open Project” o “New Project”:

Desde aquí le damos el nombre al proyecto (será el nombre que tendrá el fichero ejecutable), y también desde aquí podemos añadir los fuentes que formarán parte del proyecto (“Add Item”).

En una cierta ventana de la pantalla tendremos información sobre los fuentes que componen nuestro proyecto (en el ejemplo, tomado de Turbo C++ Second Edition´, esta ventana aparece en la parte inferior de la pantalla).

Las ventajas que obtenemos al usar “proyectos” son:

  • La posibilidad de manipular varios fuentes a la vez y de recompilar un programa que esté formado por todos ellos, sin necesidad de salir del entorno de desarrollo.
  • La ventaja añadida de que el gestor de proyectos sólo recompilará aquellos fuentes que realmente se han modificado, como comentábamos que hacía la herramienta MAKE.


Curso de C. Ampliaciones - Modelos de memoria.

En los compiladores de C que funcionan bajo MsDos en "modo real" (el modo de funcionamento original de los primeros PC, los 8086), existe el problema de la segmentación: la memoria está dividida en bloques (segmentos) de 64Kb de tamaño. Una dirección cualquiera dentro de estos segmentos recibe el nombre de desplazamiento.

Así, en un ordenador con un procesador 8086 no es posible acceder "directamente" a la posición de memoria 100.000, sino que tendríamos que decir que queremos trabajar con un segmento que empieza en la dirección 70.000 (por ejemplo, porque la posición inicial de los segmentos no está prefijada), y dentro de ese segmento queremos leer la posición (o desplazamiento) 30.000.

En la práctica, se puede trabajar con varios segmentos a la vez, de uno de los cuales se leen las órdenes (o "código", el segmento CS), de otro se leen los datos (DS), otro se usa como "pila" (donde se almacenan ciertos datos auxiliares, como la dirección a la que hay que volver al salir de una función, el segmento SS), otro extra que se puede usar para mover bloques grandes de memoria (ES). No voy a entrar en más detalles, porque no pretendo que este sea un curso de arquitectura de los PCs, ni siquiera querría extenderme demasiado con el "modo real", que cada vez se usa menos (se tiende a MsDos en modo protegido -ver más abajo- y a entornos gráficos como Windows).

La mayoría de estos compiladores nos permiten elegir entre usar un mismo segmento para datos y código, un segmento para datos y otro para código, o uno para datos y varios para código, o uno para código y varios para datos... cuantos más segmentos ocupemos, más lento será el programa (más veces habrá que cambiar la dirección de comienzo del segmento), así que normalmente podremos elegir entre los siguentes modelos:

  • Tiny
  • Small
  • Medium
  • Compact
  • Large
  • Huge

Vamos a comentar las diferencias:

Tiny (diminuto).
Los 4 registros de segmento (CS, DS, ES, SS) se sitúan en la misma dirección, de modo que sólo tenemos un total de 64K para código (programa en sí), datos y pila.
Es el modo de memoria que se usa para crear programas .COM.

Small (pequeño).
El segmento de código y el de datos son distintos (y no se solapan), de modo que tenemos 64K para código y 64K para datos (y pila).
Suele ser el modo por defecto de la mayoría de los compiladores que trabajan bajo DOS en modo real.

Medium (medio).
Los datos (y la pila) están limitados a 64K, pero el código puede ocupar más de un segmento (hasta 1 Mb).
Se usa para programas grandes pero que no guardan demasiados datos en memoria.

Compact (compacto).
Es el caso contrario del anterior: el código está limitado a 1 segmento (64K), pero los datos puede ocupar hasta 1 Mb.
Lo usaremos cuando el programa en sí sea pequeño, pero necesite manejar muchos datos en memoria.

Large (grande).
Tanto los datos como el código pueden ocupar más de un segmento, en teoría hasta llegar a 1 Mb.
Sólo lo usaremos para aplicaciones muy grandes (es más lento que los anteriores).

Huge (enorme).
Al igual que el caso anterior, podemos tener datos y código de hasta 1 Mb, pero este modelo permite además que un dato en concreto ocupe más de 64K. Es el modelo más lento de todos.

En el caso de Symantec C++, de Watcom C++, de Digital Mars y algún otro, si queremos crear programas para MsDos, podemos usar estos modos de memoria, o bien compilar para "modo protegido".

Este "modo protegido" es un modo de trabajo específico de los ordenadores 386 y superiores, en el que ya no existen segmentos, y podemos aprovechar sin problemas toda la memoria que tenga nuestro ordenador, crear bloques de datos (arrays, por ejemplo) de más de 64 K, etc.

Se suele designar a este modo de trabajo bajo MsDos por las siglas DPMI (del inglés DOS Protected Mode Interface - Interfaz para modo protegido en Dos-).


Curso de C. Ampliaciones - Ensamblador desde C.

El lenguaje C nos permite un control muy alto del ordenador, pero en ciertas ocasiones nos puede interesar llegar hasta un nivel más bajo todavía, o bien optimizar la velocidad de una cierta rutina al límite.

En esos casos (y muy pocos más) podemos incluir líneas en lenguaje ensamblador dentro de un programa normal en lenguaje C.

Aun así, conviene recordar que si lo hacemos, estamos perdiendo una de las bazas más importantes del lenguaje C: su portabilidad. Al incluir órdenes en lenguaje ensamblador, que es exclusivo de cada tipo de ordenador, e incluso en ciertos casos de cada sistema operativo, estamos haciendo que nuestro programa no pueda funcionar en otros tipos de ordenadores ni quizá en un ordenador idéntico pero con un sistema operativo distinto.

No pretendo enseñar desde aquí a programar en lenguaje ensamblador, pero sí pondré un ejemplo bajo MsDos para quien ya tenga unos ciertos conocimientos. La forma de incluir ensamblador dentro de C es sencilla: tecleamos la palabra "asm" y luego, entre llaves, las distintas órdenes:

  /*-------------------------*/
  /*  Ejemplo en C: incluir  */
  /*  órdenes en ensamblador */
  /*  ASMC.C                 */
  /*                         */
  /*  Comprobado con:        */
  /*    - Turbo C++ 1.01     */
  /*-------------------------*/

  #include <stdio.h>

  int i;

  int main()
  {
     asm {
      mov ax, 1      /* Cambio a modo 1: texto 40x25*/
      int 0x10
     }
     printf("Hola"); /* Saludo */
     asm {
      mov ax, 1      /* Espero a que se pulse una tecla */
      int 0x16
      mov ax, 3      /* Y vuelvo a modo 3: texto 80x25 */
      int 0x10
     }
  }

(DJGPP emplea un formato diferente de éste).

Puede ver un ejemplo de programa más desarrollado que utiliza estas posibilidades entre los fuentes de ejemplos, y que muestra varias figuras que rebotan por la pantalla.


Curso de C. Ampliaciones - Línea de comandos.

Es frecuente ver programas de MsDos y de Unix que permiten una serie de opciones, como

    DIR *.EXE /P
    ls n*

Estas opciones se dan en la misma línea que la propia orden. Son las llamadas "opciones de la línea de comandos". Vamos a ver cómo acceder a ellas desde un programa escrito en C:

En primer lugar, debemos indicar a la función "main" que nos va a interesar las opciones que se le pasen al programa. Esto lo conseguimos escribiendo "main" con dos parámetros:

En vez de escribir

   int main()

escribiremos

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

El primer parámetro (argc) es un número entero, que indica la cantidad de argumentos (o parámetros, u opciones) que se han tecleado al escribir el nombre del programa. Este número incluye el propio nombre del programa, de modo que en DIR *.EXE /P argc valdría 3 (DIR, *.EXE, /P) y en ls n* argc valdría 2 (ls, n*).

El segundo parámetro (argv) es un array de cadenas de texto, que contiene cada una de las opciones que hemos tecleado (también incluyendo el propio nombre del programa):

- argv[0] es el nombre del programa (DIR, en el primer ejemplo).
- argv[1] es el primer argumento (*.EXE).
- argv[2] es el segundo argumento (/P).

Puede ver un ejemplo de programa completo que utiliza estas posibilidades entre los fuentes de ejemplo, y que muestra por pantalla el contenido de un fichero de texto, y otro que compara dos ficheros.


Curso de C. Ampliaciones - Funciones matemáticas.

Dentro del fichero de cabecera “math.h” tenemos acceso a muchas funciones matemáticas predefinidas en C, como:

  • acos(x): Arco coseno
  • asin(x): Arco seno
  • atan(x): Arco tangente
  • atan2(y,x): Arco tangente de y/x (por si x o y son 0)
  • ceil(x): El valor entero superior a x y más cercano a él
  • cos(x): Coseno
  • cosh(x): Coseno hiperbólico
  • exp(x): Exponencial de x (e elevado a x)
  • fabs(x): Valor absoluto
  • floor(x): El valor mayor entero que es menor que x
  • fmod(x,y): Resto de la división x/y
  • log(x): Logaritmo natural (o neperiano, en base e)
  • log10(x): Logaritmo en base 10
  • pow(x,y): x elevado a y
  • sin(x): Seno
  • sinh(x): Seno hiperbólico
  • sqrt(x): Raíz cuadrada
  • tan(x): Tangente
  • tanh(x): Tangente hiperbólica

(todos ellos usan parámetros X de tipo “double”)

y una serie de constantes como

M_E, el número “e”, con un valor de 2.71828...
M_PI, el número “Pi”, 3.14159...

Si estamos en C++, tendremos también posiblemente definida una clase “complex”, para números complejos (con dos componentes, x e y).


Curso C. Código ASCII.

El nombre del código ASCII viene de "American Standard Code for Information Interchange", que podríamos traducir como Código Estándar Americano para Intercambio de Información.

La idea de este código es que se pueda compartir información entre distintos ordenadores o sistemas informáticos. Para ello, se hace corresponder una letra o carácter a cada secuencia de varios bits.

El código ASCII estándar es de 7 bits, lo que hace que cada grupo de bits desde el 0000000 hasta el 1111111 (0 a 127 en decimal) corresponda siempre a la misma letra.

Por ejemplo, en cualquier ordenador que use código ASCII, la secuencia de bits 1000001 (65 en decimal) corresponderá a la letra "A" (a, en mayúsculas).

Los códigos estándar "visibles" van del 32 al 127. Los códigos del 0 al 31 son códigos de control: por ejemplo, el caracter 7 (BEL) hace sonar un pitido, el caracter 13 (CR) avanza de línea, el carácter 12 (FF) expulsa una página en la impresora (y borra la pantalla en algunos ordenadores), etc.

Estos códigos ASCII estándar son:

|        0    1    2    3    4    5    6    7    8    9  |
| 000   NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL  BS   HT  |
| 010   LF   VT   FF   CR   SO   SI   DLE  DC1  DC2  DC3 |
| 020   DC4  NAK  SYN  ETB  CAN  EM   SUB  ESC  FS   GS  |
| 030   RS   US   SP    !    "    #    $    %    &    '  |
| 040    (    )    *    +    ,    -    .    /    0    1  |
| 050    2    3    4    5    6    7    8    9    :    ;  |
| 060    <    =    >    ?    @    A    B    C    D    E  |
| 070    F    G    H    I    J    K    L    M    N    O  |
| 080    P    Q    R    S    T    U    V    W    X    Y  |
| 090    Z    [    \    ]    ^    _    `    a    b    c  |
| 100    d    e    f    g    h    i    j    k    l    m  |
| 110    n    o    p    q    r    s    t    u    v    w  |
| 120    x    y    z    {    |    }    ~                |

Hoy en día, la mayoría de los ordenadores incluyen un código ASCII extendido de 8 bits, que permite 256 símbolos (del 0 al 255). Esto permite que se puedan usar también vocales acentuadas, eñes, y otros símbolos para dibujar recuadros, por ejemplo. Pero hay que recordar que estos símbolos que van del número 128 al 255 no son estándar, de modo que puede que otro ordenador no los interprete correctamente. De hecho, si miramos desde el Bloc de Notas de Windows un texto que tenga acentos y que hayamos creado con el editor (Edit) de MsDos, dichos acentos se verán como otros símbolos extraños... a pesar de que usamos el mismo ordenador.

Incluso en dos ordenadores distintos, equipados ambos con MsDos, pueden ser distintos parte de los caracteres "extendidos", según la "página de códigos" que se emplee. Por ejemplo, la siguiente imagen muestra la página de códigos 437, la que incluían las primeras versiones de MsDos:

Un mini-programa que nos muestre los códigos ASCII del 32 al 255 (en decimal y como letra) puede ser:

  /*-------------------------*/
/*  Ejemplo en C: ver la   */
/*  tabla de códigos ASCII */
/*  de un IBM PC.
*/
/*  ASCII.C
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*-------------------------*/

  #include <stdio.h>

  int i;

  int main()
{
for (i=32; i<=255; i++)
printf("%3d %c
", i, i);
}

También existen otros códigos distintos del ASCII, como el EBCDIC de IBM, que está cada vez más en desuso.


Curso de C. Fuentes de ejemplo.

En este apartado intentaré incluir programas algo más completos que los fuentes de ejemplo que se han ido viendo en los temas anteriores. Serán "programitas" capaces de funcionar por sí mismos, y que hagan algo que tenga una cierta utilidad.

Por ahora, puede acceder a los siguientes:

- Adivinar un número.
- Mostrar un fichero de texto.
- Comparar dos ficheros.
- Figuras que se mueven por la pantalla.
- Agenda de direcciones.
- Sistema de menús para MsDos.
- Representar funciones matemáticas.


Curso de C. Fuentes de ejemplo: Adivinar un número.

Este programa es un juego sencillo, que está pensado para dos jugadores: uno decide un número, que el otro deberá adivinar, con un número limitado de intentos. Para hacerlo un poco más fácil, cada vez que el segundo jugador diga un número incorrecto, le avisaremos de si se ha pasado o si se ha quedado corto.

La estructura del programa es muy sencilla: se repite hasta que queden 0 intentos, y en cada repetición comprueba si se ha acertado, o si nos hemos quedado por encima o por debajo.

Hay una parte que depende del compilador: cuando el primer jugador teclea el número que se debe acertar, debemos borrar la pantalla para que el segundo jugador no lo pueda ver. Esta versión está realizada pensando en Turbo C++. En las ampliaciones del curso puede ver cómo adaptarlo para otros compiladores.

  /*-------------------------*/
  /*  Fuente completo en C:  */
  /*  Adivinar un número que */
  /*  proponga otro jugador. */
  /*  ADIV.C                 */
  /*                         */
  /*  Curso de C,            */
  /*          Nacho Cabanes  */
  /*                         */
  /*  Comprobado con:        */
  /*    - Turbo C++ 1.01     */
  /*    - Djgpp 2.01         */
  /*-------------------------*/

  #include <stdio.h>
  #include <conio.h>          /* Para borrar la pantalla, con Turbo C */

  int numero,                 /* El número que se debe encontrar */
    maxIntentos,              /* Intentos permitidos */
    intento,                  /* El número que se prueba cada vez */
    i;                        /* Para repetir, con "for" */

  void borraPantalla()        /* Esta es la parte que habrá que modificar */
  {                           /* si se usan otros compiladores */
    clrscr();
  }

  int main()                      /* -- Cuerpo del programa -- */
  {
     borraPantalla();
                                            /* Pido datos iniciales */
     puts("Introduce el número a adivinar (entre 1 y 100)");
     scanf("%d", &numero);
     puts("Introduce el número de intentos");
     scanf("%d", &maxIntentos);

     borraPantalla();

     for (i=maxIntentos-1; i>=0; i--)            /* La parte que se repite; */
     {
        puts("Introduce un número");             /* Pido el dato */
        scanf("%d", &intento);
        if (intento == numero)                   /* Comparo con el correcto */
        {
           printf("Acertaste!\n");               /* Si lo es, se acabó */
           exit(0);
        }
        if (intento < numero )                   /* Aviso si se queda corto */
           printf("Te has quedado corto\n");     /* o si se pasa */
        else
           printf("Te has pasado\n");

        if ( i>0 )                               /* Si quedan intentos */
          printf("\nQuedan %d intentos.\n",      /* le recuerdo cuantos */
            i);
     }
     puts("Se te acabaron los intentos");    /* Si se acaban los intentos: */
     return 0;                               /* aviso y salgo */
  }

Otra opción de este juego sería que el ordenador fuera el que "pensara" un número y el jugador quien tuviera que adivinarlo. Para ello tenemos que ver cómo generar números aleatorios...

En esta variante del juego de adivinar números, será el
ordenador el que decida el número inicial.  Para ello debemos
generar números aleatorios (al azar), lo que hace la función
"random", cuyo prototipo se encuentra en "stdlib.h".

Esta función se usa:

numero = random(n);

Esto hace que en "numero" se almacene un valor entre 0 y n-1.

Normalmente antes deberíamos llamar a "randomize()",
que tomará a partir del reloj interno el valor inicial para los
números que devuelva "random".  Esto hace que sean valores
más fiables, que no se repitan cada vez que pongamos el programa
a funcionar.

Estas dos funciones son las equivalentes a las que emplea Turbo
Pascal. Las estándares en ANSI C no son estas, sino "rand()"
para obtener un número y "srand(semilla)" para comenzar la secuencia.

El número de intentos está fijado en el programa.
Se podría hacer que variase en función del grado de dificultad
elegido, o que también fuese al azar.

/*-------------------------*/
/*  Fuente completo en C:  */
/*  Adivinar un número que */
/*  proponga el ordenador. */
/*  ADIV2.C
*/
/*
*/
/*  Curso de C,
*/
/*
Nacho Cabanes  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*-------------------------*/

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

int numero,
/* El número que se debe encontrar */
maxIntentos,
/* Intentos permitidos */
intento,
/* El número que se prueba cada vez */
i;
/* Para repetir, con "for" */

int main()
/* -- Cuerpo del programa -- */
{
/* Datos iniciales */
randomize();
numero = random(100)+1;
maxIntentos = 7;

for (i=maxIntentos-1; i>=0; i--)
/* La parte que se repite; */
{
puts("Introduce un número");
/* Pido el dato */
scanf("%d", &intento);
if (intento == numero)
/* Comparo con el correcto */
{
printf("Acertaste!\n");
/* Si lo es, se acabó */
exit(0);
}
if (intento < numero
)
/* Aviso si se queda corto */
printf("Te
has quedado corto\n");     /* o si se pasa */
else
printf("Te
has pasado\n");

if ( i>0 )
/* Si quedan intentos */
printf("\nQuedan
%d intentos.\n",      /* le recuerdo cuantos */
i);
}
puts("Se te acabaron los intentos");
/* Si se acaban los intentos: */
return 0;
/* aviso y salgo */
}


Curso de C. Fuentes de ejemplo: Mostrar un fichero de texto.

Este programa es una de las cosas "útiles" más sencillas que se pueden hacer con ficheros: mostrar en pantalla un fichero de texto.

Para complicar un poco la cosa, vamos a poner ciertas condiciones:
  • Debe poder leer líneas de más de 80 letras, pero truncarlas, mostrando sólo las primeras 79 letras, de forma que en pantalla todo quede "colocadito".
  • Cada 24 líneas deberá hacer una pausa.
  • El nombre del fichero a mostrar se debe indicar en la línea de comandos, por ejemplo escribiendo NTYPE AUTOEXEC.BAT

El hecho de que haga una pausa cada 24 líneas nos obliga a incluir la orden "getch()", que se encuentra en distinto fichero de cabecera según cada compilador. Para el caso de Turbo C, está en "conio.h".

/*-------------------------*/
/*  Fuente completo en C:  */
/*  Mostrar un fichero de  */
/*  texto.
*/
/*  NTYPE.C
*/
/*
*/
/*  Curso de C,
*/
/*
Nacho Cabanes  */
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*-------------------------*/

#include <stdio.h>
#include <conio.h>
/* Para usar "getch" */

int main(int argc, char *argv[])   /* Los parámetros
de "main" son
los que se pasen al programa */
{
char c;
/* El carácter que se lee */
int cc = 0,
/* Contador de caracteres */
lc = 0,
/* Contador de líneas */
i;
/* Para bucles */
char linea[90];  /* La línea
que se muestra en pantalla */
FILE *f;
/* El fichero que se abre */

/**** Si no se dan parámetros,
aviso y salgo ****/
if (argc < 2)
{
puts("Empleo:
NTYPE fichero");
return 1;
}

/**** Intento abrir el fichero ****/
if ((f=fopen(argv[1],"r")) == NULL)
{
puts("Ese
fichero no existe.");
return 1;
}
/* Primero vacío la línea */
for (i=0; i<=78; i++) linea[i]='
'; linea[79]='\0';

/**** El cuerpo del programa ****/
while ((c = fgetc(f)) != EOF)
/* Leo mientras queden letras */
{
if (c !=
'\n')
/* Si no es final de línea: */
if (cc < 79)          /*
Y queda sitio */
linea[cc] = c;    /* Guardo la letra */
else
/* Si no queda sitio, no la guardo */
linea[79] = '\0'; /* Y marco como si acabase la línea */
else
{
/* Si es final de línea */
lc++;
/* Cuento una línea más */
puts(linea);          /* La
escribo */
for (i=0; i<=78; i++) /* Y la vacío */
linea[i]=' '; linea[79]='\0';
cc = -1;
/* Ninguna letra en la prox línea */
if (lc % 24 == 0)     /* Si llevo 24 líneas
*/
{
printf("Pulse una tecla para seguir...");
getch();          /* Aviso
y espero una tecla */
printf("\n");
}
}
cc++;
/* Aumento el contador y paso a otra letra */
}
return 0;
}
/*** Fin del programa ***/


Curso de C. Fuentes de ejemplo: Comparar dos ficheros.

Este programa compara dos ficheros. La idea en sí en sencilla: se abren los dos ficheros. Si los tamaños son diferentes, no hay más que hablar: los ficheros son distintos. Si no lo son, se va leyendo un bloque de cada uno, y comparando el contenido de los dos bloques.

Para ello, abrimos los dos ficheros en modo binario (con "rb"), y vamos leyendo datos a un buffer (memoria intermedia) de 16K de tamaño (16384 bytes).

Hay una función ("fcompare") que se encarga de todo esto, y que devuelve:

  • -1 si hay algún error (no se encuentra algún fichero).
  • 0 si los ficheros son iguales.
  • 1 si los ficheros son distintos.

Esta función es el auténtico corazón del programa, mientras que "main" se limita a comprobar que demos el número correcto de parámetros y a llamar a "fcompare".

  /*-------------------------*/
/*  Fuente completo en C:  */
/*  Comparar dos ficheros. */
/*  NFC.C
*/
/*
*/
/*  Curso de C,
*/
/*
Nacho Cabanes  */
/*
*/
/*  Basado en un fuente de */
/*  Bob Stout (dominio pú- */
/*  blico).  Comentado,    */
/*  traducido (y poco más) */
/*  por Nacho Cabanes
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01
*/
/*    - Djgpp 2.01
*/
/*-------------------------*/

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

  enum {kERROR = -1, kIGUALES, kDISTINTOS;   /* Los valores de respuesta */

  #define kTAMBUF 16384
/* Tamaño del buffer */
static char buf[2][kTAMBUF];
/* El buffer en sí */

  /**** La parte importante es esta función ****/
int fcompare(const char *nomFich1, const char *nomFich2)
{
FILE *f1, *f2;
/* Los ficheros a comparar */
size_t tam1, tam2;
/* Los tamaños leídos */
int resultado = kIGUALES;
/* Presupongo que son iguales */

      if ((f1 = fopen(nomFich1, "rb")) == NULL)  /* Intento abrir */
          return kERROR;
      if ((f2 = fopen(nomFich2, "rb")) == NULL)
          return kERROR;

      do
      {
          tam1 = fread(buf[0], 1, kTAMBUF, f1);   /* Leo de un fichero */
          tam2 = fread(buf[1], 1, kTAMBUF, f2);   /* y del otro */

          if ((tam1
== 0) && (tam2 == 0))
/* Si no queda nada */
break;
/* por leer, salgo */

          if ((tam1
!= tam2)
/* Si los tamaños */
|| memcmp(buf[0], buf[1], tam1))    /* o los datos */
{
/* son distintos: salgo */
resultado = kDISTINTOS;
break;
}
} while (tam1 && tam2);
/* Repito mientras se */
/* pueda seguir leyendo */

      fclose(f1);
/* Finalmente, cierro los ficheros */
fclose(f2);
return resultado;
/* Y salgo de la función */
}

  int main(int argc, char *argv[])
{
if (argc < 3)
{
puts("Empleo: NFC fichero1 fichero2");
return 1;
}
printf("fcompare(%s,
%s) ha devuelto el valor %d\n",
argv[1], argv[2],
fcompare(argv[1], argv[2]));
return 0;
}


Curso de C. Fuentes de ejemplo: Sprites (imágenes que se mueven).

Este programa dibujará una serie de figuras iguales que se moverán por la pantalla sin borrar el fondo. Estas figuras son transparentes: si hay algún hueco, el fondo se verá a través suyo. Cuando yo empecé a cotillear cosas de informática, hace ya más de 10 años, a este tipo de figuras se les solía llamar sprites.

La idea de dibujar una figura transparente es sencilla: cuando definimos la figura, marcamos de cierta forma los puntos que no queramos que se dibujen (en mi caso, usando el color 0), y a la hora de dibujar cada punto nos fijamos antes en si su color es uno que deba aparecer o no.

Lo de que se muevan sin borrar el fondo es algo más difícil. Normalmente se puede solucionar guardando el fondo antes de dibujar la figura, y restaurándolo cuando ésta se va a mover. Podemos guardar toda la pantalla o sólo la parte que tapará la figura. Por mi parte, como se moverán varias figuras, he optado por guardar todo el fondo de una vez.

Este fuente incluye partes en lenguaje ensamblador. He usado poco este lenguaje, de modo que el programa no es tan rápido como podría llegar a ser, pero espero que así sea más legible. Las rutinas de ensamblador son para cambiar el modo de pantalla, para cambiar un color de la paleta (indicando la cantidad de rojo, verde y azul) y para sincronizar con el barrido de la pantalla y evitar así parpadeos.

Por lo demás, el listado debería explicarse por sí mismo. De todas formas, voy a detallar la secuencia básica que se repite

   Calcular valores iniciales
   Repetir hasta que se pulse una tecla:
     Copiar la pantalla de fondo en la temporal
     Para cada Sprite:
       - Dibujarlo en la pantalla temporal
       - Calcular su nueva posición
       - Si se acerca al extremo de la pantalla, cambiar el sentido
     Copiar la pantalla temporal en la visible.

El programa que hace todo eso es éste:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico acce-  */
/*  diendo a memoria.      */
/*  SPRITES.C              */
/*                         */
/*  Dibuja unas figuras    */
/*  que se mueven por la   */
/*  pantalla.              */
/*                         */
/*  Curso de C,            */
/*          Nacho Cabanes  */
/*                         */
/*  Comprobado con:        */
/*    - Turbo C++ 1.01     */
/*-------------------------*/

#include            /* Usaremos las funciones del DOS */
#include          /* Para "printf", por si algo va mal */
#include         /* En STDLIB está "random" y otras */
#include          /* Para kbhit() */
#include

#define ALTO  200          /* La pantalla tiene 200 filas */
#define ANCHO 320          /* y 320 columnas */

#define PANTALLA 0xA000    /* Posición de comienzo de la pantalla */

#define NUMSPRITES  10     /* Número de sprites */
#define XSIZE 30           /* Tamaño de cada sprite */
#define YSIZE 30

typedef
  char tipoSprite[XSIZE][YSIZE];
                         /* El sprite en sí: matriz de 30x30 bytes */

typedef                    /* Datos de cada sprite */
  struct {
    int x, y;              /* Coordenadas de cada sprite */
    char vx, vy;           /* Velocidad (incremento) según cada eje */
  } sprDat;

  sprDat D[NUMSPRITES];   /* Datos de cada sprite */

char *pantalla;           /* Para acceder a la pantalla física */
char *fondo;              /* El fondo de la pantalla */
char *pantVirt;           /* Una pantalla virtual auxiliar */

tipoSprite sprite =
   {{0,0,0,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0},
    {0,0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,0,0},
    {0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,0},
    {2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3},
    {2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3},
    {2,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,1,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,0,0,0,2,1,1,3,0,0,0,0,0,0,0,0,2,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,0,0,0,2,1,1,3,0,0,0,0,0,0,0,0,2,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,0,0,0,2,1,1,3,0,0,0,0,0,0,0,0,2,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,2,3,0,2,1,1,3,0,0,0,0,0,2,3,0,2,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,3,3,0,2,1,1,3,0,0,0,0,0,3,3,0,2,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,0,0,0,2,1,1,3,0,0,0,0,0,0,0,0,2,1,1,1,3},
    {2,1,1,1,1,3,3,3,3,3,3,3,3,1,1,1,1,3,3,3,3,3,3,3,3,1,1,1,1,3},
    {2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3},
    {2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3},
    {2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3},
    {2,1,1,1,1,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,3},
    {2,1,1,1,3,0,0,0,2,1,1,1,1,1,1,1,1,1,1,1,1,2,0,0,0,0,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,2,1,1,1,1,1,1,1,1,2,0,0,0,0,0,0,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,0,0,2,1,1,1,1,2,0,0,0,0,0,0,0,0,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0,0,0,0,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,3},
    {2,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,3},
    {2,1,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,1,1,1,3},
    {2,1,1,1,1,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,1,1,1,1,1,1,3},
    {2,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,2},
    {0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0},
    {0,0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,0},
    {0,0,0,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,0,0,0},
    {0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0}
   };

  sprDat D[NUMSPRITES];   /* Datos de cada sprite */

/* --- Dibuja un sprite transparente --- */
void dibujaSprite(int x, int y)
{
  int i,j;                    /* Para repetir con un "for" */

  for(i=0; i (195-XSIZE)))
        D[i].vx = -D[i].vx;
      D[i].y += D[i].vy;
      if ((D[i].y < 5) || (D[i].y > (315-YSIZE)))
        D[i].vy = -D[i].vy;
      }

    retrace();              /* Sincronizo con el barrido */
                            /* Y copio la pantalla virtual en la visible */
    if (memmove(pantalla, pantVirt, 64000)
      ==NULL)
      muestraError("Error al copiar la pantalla virtual.\n");

  }
  while (!kbhit());

  getch();

  free(fondo);             /* Libero la memoria reservada */
  free(pantVirt);
  asm {
    mov ax, 3              /* En AX pongo 3, el modo de pantalla de texto */
    int 0x10               /* Cambio a ese modo con la int 10h */
  }
  return 0;                /* Y se acabó */
}


Curso de C. Fuentes de ejemplo: Agenda de direcciones.

Este va a ser un programa que pondrá en práctica la mayoría de las cosas que hemos visto. Será una agenda de direcciones, que nos permitirá como opciones básicas: visualización de los datos existente, altas (añadir datos nuevos) y modificación de los datos existentes.

Para ello, utilizaremos un fichero con datos de longitud fija. En memoria sólo tendremos la ficha actual, para ganar en capacidad a cambio de perder en velocidad.

Entonces, el primer paso será decir qué datos vamos a guardar. Definiremos una estructura así:

  typedef
      struct                          /*  Nuestro tipo de datos  */
      {
          char nombre[21];           /* 20 letras */
          char direccion[31];        /* 30 letras */
          char ciudad[16];           /* 15 letras */
          char cp[6];                /*  5 letras */
          char telef[13];            /* 12 letras */
          char observ[41];           /* 40 letras */
       } tipoAgenda;

(Hay que recordar que si queremos almacenar 20 letras además del carácter de fin de cadena \0, deberemos reservar 21 espacios).

Para guardar cada ficha podríamos usar

  fprintf( fichAgenda, "%20s%30s%15s%5s%12s%40s\n",
      ficha.nombre, ficha.direccion, ficha.ciudad, ficha.cp,
      ficha.telef, ficha.observ);

y para leerla:

  fscanf( fichAgenda, "%20s%30s%15s%5s%12s%40s\n",
      &ficha.nombre, &ficha.direccion, &ficha.ciudad, &ficha.cp,
      &ficha.telef, &ficha.observ);

pero no lo haremos por los problemas que daba "scanf" al leer datos que contuvieran espacios (se para en el primer espacio).

Por ello, para leer los datos que teclee el usuario usaremos gets en lugar de "scanf". El fichero será "binario", no de texto, y leeremos los datos de él empleando "fread":

  fread( &ficha, longFicha, 1, fichAgenda);

De igual modo, escribiremos con "fwrite":

  fwrite( &ficha, longFicha, 1, fichAgenda);

Para acceder a una cierta ficha dentro del fichero, bien sea para leer o para escribir, usaremos "fseek", teniendo en cuenta el número de fichas y el espacio que ocupa cada ficha:

  fseek( fichAgenda, (numFicha-1)*longFicha, SEEK_SET );

Para saber el tamaño del fichero, podemos desplazarnos al final con "fseek" y entonces emplear "ftell". Si queremos saber cuántas fichas hay, basta con dividir este tamaño entre el espacio que ocupa cada ficha.

Esto es lo básico del programa. Se pueden añadir muchísimas cosas más, pero voy a comentar sólo algunas de las otras cosas que he incluido:

A la hora de modificar, se muestra entre paréntesis el texto actual. Si se pulsa INTRO, se conserva tal y como está. Para ello, se lee el texto en una variable temporal:

  cprintf("\r\n Dirección (%s) ?", ficha.direccion);
  gets(temporal);
  if (strlen(temporal))
      strcpy(ficha.direccion, temporal);

El programa está preparado para Turbo C++. Por ello emplea las órdenes de manejo de pantalla de este compilador: cprintf, gotoxy, clrscr, clreol, etc., aunque no es difícil adaptarlo para otros compiladores, o incluso hacer una versión que sea capaz de compilar en distintas versiones de lenguaje C (al menos en un par de ellas).

De paso, se podría aprovechar para estructurar mejor el programa, e incluso dividirlo en varios fuentes. Esto queda propuesto como ejercicio...
/* =======================================================
      Ejemplito de Agenda, que maneje un poco de todo.

      Características:
      - Número de fichas ilimitado, pero mayor lentitud, porque
        los datos están permanentemente en disco, y en memoria se
        almacena sólo la ficha actual.
      - Muestra un ficha.
      - Se puede ver la siguiente, la anterior o la número "x"
      - Se pueden añadir fichas nuevas.

      Posibles mejoras: muchas.  Por ejemplo:
      - Mejorar la presentación.
      - Mejorar la entrada de datos y su modificación.
      - Buscar un determinado texto en las fichas.
      - Imprimir una o varias fichas.
      - Ordenar el fichero alfabéticamente.
      - Mantener todas o parte de las fichas en memoria para mayor
        velocidad (empleando índices si es preciso).
      - Borrar fichas.
        - Clave de acceso para que no pueda usarlo cualquiera.
      - Datos encriptados para que se puedan descubrir a partir del
        fichero de texto.
      - Etc, etc, etc...

      Original de Nacho Cabanes (en Pascal), Marzo 93.

      El original era un programa antiguo y no muy bien estructurado,
      pero he procurado conservar la misma estructura para que quien
      quiera pueda comparar las formas de obtener lo mismo en ambos
      lenguajes.

      -- Comprobado con: TC++ 2nd Ed., Djgpp 2.01 --

    ============================================================== */

#include <stdio.h>     /* Entrada y salida estándar */
#include <string.h>    /* Manejo de cadenas de texto */
#include <io.h>        /* Funciones de ficheros */
#include <conio.h>     /* Acceso a la pantalla en Turbo C */

#define NOMBREF "agenda.dat"

typedef
    struct                          /*  Nuestro tipo de datos  */
    {
        char nombre[21];           /* 20 letras */
        char direccion[31];        /* 30 letras */
        char ciudad[16];           /* 15 letras */
        char cp[6];                /*  5 letras */
        char telef[13];            /* 12 letras */
        char observ[41];           /* 40 letras */
    }  tipoAgenda;

/* Declaraciones de variables globales */

FILE *fichAgenda;
tipoAgenda ficha;
int longFicha = sizeof (tipoAgenda);
int numFicha;
int ultima;
char opcion;

void pausa()              /*  ----- Espera a que se pulse una tecla  */
{
    getch();
};

void saludo()                   /*  ----- Cartelito de presentación  */
{
    textbackground(BLACK); textcolor(LIGHTGRAY);
    clrscr();
    window(23,3,80,25);
    textcolor(LIGHTCYAN);
    cprintf("\r\n\r\n\r\n+------------------------------+\r\n");
    cprintf("¦                              ¦__\r\n");
    cprintf("¦                              ¦__\r\n");
    cprintf("¦         A G E N D A          ¦__\r\n");
    cprintf("¦                              ¦__\r\n");
    cprintf("¦                              ¦__\r\n");
    cprintf("+------------------------------+__\r\n");
    cprintf("  ________________________________");
    textcolor(LIGHTGRAY);
    window(1,1,80,25);
    gotoxy(1,25);      /* Para que el cursor no moleste */
    pausa();
};

void escribe()              /*  ----- Escribe los datos en pantalla  */
{
    int i;

    clrscr();
    textcolor(WHITE);
    textbackground(BLUE);
    gotoxy(1,2);cprintf("          Agenda            (ficha actual: %d de %d)",
        numFicha, ultima);
    clreol();               /* Borro hasta el final de la línea en azul */
    gotoxy(1,3);
    for (i=1;i<=80;i++) cprintf("-");        /* 80 guiones */
    textbackground(BLACK);
    textcolor(LIGHTGRAY);

    /* Me coloco en la ficha que toca */
    fseek( fichAgenda, (numFicha-1)*longFicha, SEEK_SET );
    /* y la leo */
    fread( &ficha, longFicha, 1, fichAgenda);
    /* No usaremos fscanf para evitar problemas con comas y espacios */

    gotoxy(1,6);
    cprintf("Nombre:        %s\r\n\r\n", ficha.nombre);
    cprintf("Dirección:     %s\r\n\r\n", ficha.direccion);
    cprintf("Ciudad:        %s\r\n\r\n", ficha.ciudad);
    cprintf("Código Postal: %s\r\n\r\n", ficha.cp);
    cprintf("Teléfono:      %s\r\n\r\n\r\n", ficha.telef);
    cprintf("Observaciones: %s\r\n",ficha.observ);

    textcolor(WHITE);
    textbackground(BLUE);
    gotoxy(1,23);
    for (i=1;i<=80;i++) cprintf("-");     /* 80 guiones */                                                          /*      Abajo: escribe las opciones      */
    gotoxy(1,24);
    cprintf(" 1-Anterior   2-Posterior   3-Número    4-Añadir   5-Corregir    0-Terminar");
    clreol();
    textbackground(BLACK);
    textcolor(LIGHTGRAY);
};

void fichaNueva()                   /*  ----- Añade una ficha nueva  */
{
    clrscr();
    textcolor(YELLOW);
    numFicha = ultima + 1;           /* Hay que escribir al final */
    cprintf("Añadiendo la ficha %d.",numFicha);
    textcolor(LIGHTGRAY);

    cprintf("\r\n¿ Nombre ?\r\n");  gets(ficha.nombre );
    cprintf("\r\n¿ Dirección ?\r\n");  gets(ficha.direccion );
    cprintf("\r\n¿ Ciudad ?\r\n");  gets(ficha.ciudad );
    cprintf("\r\n¿ Código Postal ?\r\n");  gets(ficha.cp );
    cprintf("\r\n¿ Teléfono ?\r\n");  gets(ficha.telef );
    cprintf("\r\n¿ Observaciones ?\r\n");  gets(ficha.observ );

    fseek( fichAgenda, (numFicha-1)*longFicha, SEEK_SET );  /* Se sitúa      */
    fwrite( &ficha, longFicha, 1, fichAgenda);                    /* y escribe la ficha      */
    ultima ++;               /*  Ahora hay una más      */
};

void modifica()                  /*  ----- Modifica la ficha actual  */
{
    char temporal[100];     /* Almacena cada valor temporalmente */

    clrscr();
    textcolor(YELLOW);
    cprintf("Corrigiendo la ficha %d.",numFicha);
    textcolor(LIGHTGRAY);

    cprintf("\r\n¿ Nombre (%s) ?", ficha.nombre);
    strcpy(temporal,"\0");      /* Muestro el valor anterior */
    gets(temporal);             /* y pido el nuevo */
    if (strlen(temporal))       /* Si se teclea algo, */
        strcpy(ficha.nombre, temporal);      /* lo modifico (si no, no cambia) */

    cprintf("\r\n¿ Dirección (%s) ?", ficha.direccion);
    strcpy(temporal,"\0");
    gets(temporal);
    if (strlen(temporal))
        strcpy(ficha.direccion, temporal);

    cprintf("\r\n¿ Ciudad (%s) ?", ficha.ciudad);
    gets(temporal);
    if (strlen(temporal))
        strcpy(ficha.ciudad, temporal);

    cprintf("\r\n¿ Código postal (%s) ?", ficha.cp);
    gets(temporal);
    if (strlen(temporal))
        strcpy(ficha.cp, temporal);

    cprintf("\r\n¿ Teléfono (%s) ?", ficha.telef);
    gets(temporal);
    if (strlen(temporal))
        strcpy(ficha.telef, temporal);

    cprintf("\r\n¿ Observaciones (%s) ?", ficha.observ);
    gets(temporal);
    if (strlen(temporal))
        strcpy(ficha.observ, temporal);

    /* Finalmente, la guardo */
    fseek( fichAgenda, (numFicha-1)*longFicha, SEEK_SET );
    fwrite( &ficha, longFicha, 1, fichAgenda);

}

void numeroFicha()     /*  ----- Va a la ficha con un cierto numero  */
{
    int numero;

    clrscr();
    textcolor(YELLOW);
    cprintf("Saltar a la ficha con un determinado número ");
    textcolor(LIGHTGRAY);
    cprintf("\r\n¿ Qué número de ficha ?");
    scanf("%d", &numero );
    if (numero>0)                 /* Compruebo que sea válido */
        if (numero<=ultima)
            numFicha=numero;      /* si es <= que la última, salto */
         else
            numFicha=ultima;     /* si es mayor, me quedo en la última */
};

void prepara()            /*  ----- Inicialización de las variables  */
{                               /*  y apertura/creación del fichero  */

    numFicha = 1;
    ultima = 0;

    fichAgenda = fopen (NOMBREF, "r+b");      /* Pruebo a abrir el fichero */
    if (fichAgenda == NULL)                   /* Si no existen datos */
    {
        clrscr();
        cprintf(" No existen datos.  Pulse una tecla para introducirlos.");
        pausa();
        fichAgenda = fopen (NOMBREF, "wb");   /* los creo */

        fichaNueva();                 /* y obligo a añadir una ficha */
    }
    else
    {
        fseek( fichAgenda, 0, SEEK_END);
        ultima = ftell( fichAgenda ) / longFicha;  /* Número de fichas */
    }
};

int main()
{                               /*  ----- Cuerpo del programa -----  */
    saludo();
    prepara();
    do
    {
        escribe();

        opcion='a';
        do
            opcion = getch();
        while ((opcion<'0') || (opcion>'5'));

        switch (opcion)
        {
            case '1':        /* Ficha anterior */
               if (numFicha>1) numFicha--;
               break;
            case '2':        /* Ficha posterior */
               if (numFicha<ultima) numFicha++;
               break;
            case '3':        /* Número de ficha */
               numeroFicha();
               break;
            case '4':        /* Añadir una ficha */
               fichaNueva();
               break;
            case '5':        /* Corregir la ficha actual */
               modifica();
               break;
        };
    }
    while (opcion != '0');      /* Terminar */
    fclose( fichAgenda );
    clrscr();
    return 0;
}


Curso de C. Fuentes de ejemplo: Sistema de menús para MsDos.

Más de un usuario me ha pedido que incluyera el fuente de un menú como el que aparecía en las versiones 1.xx del curso.

Es un fuente antiguo mío, que primero fue un programa en QBasic, que después convertí a Pascal y posteriormente a C. Por eso, no está bien estructurado ni es tampoco demasiado fácil de leer, pero por si a alguien le resulta de utilidad, aquí está.

Muestra una serie de opciones en pantalla, cada una con un nombre que la describe, una orden de MsDos que ejecutar, 10 líneas de texto como ayuda. Además, a cada opción se le puede indicar una tecla rápida para acceder con una sola pulsación, así como indicar si queremos una pausa antes de volver al programa.

/*-------------------------*/
/*  Ejemplo en C: sistema  */
/*  de menús para MsDos    */
/*  NMENU.C                */
/*                         */
/*  Curso de C,            */
/*          Nacho Cabanes  */
/*                         */
/*  Comprobado con:        */
/*    - Turbo C++ 1.01     */
/*    - Djgpp 2.01         */
/*-------------------------*/

/*********************************
             nMenu
   Sistema de menús para MsDos

       Nacho Cabanes, 95

    Versión reducida para el
       curso de lenguaje C

   La versión completa leía los
   datos de disco en vez de estar
   definidos en el programa,
   permitía el uso de ratón, dejaba
   casi toda la memoria libre
   cuando llamaba a otro programa,
   incluía ayuda, etc...

   Espero que esta versión, a cambio
   de tener menos refinamientos,
   sea más fácil de entender, aunque
   me temo que al ser la conversión
   a C de la conversión a Pascal de
   un original en Basic poco estruc-
   turado... ¡lo increíble es que
   funcione!

*********************************/

#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>                /* Para "toupper" */

#define kVERSION     "1.00 cc"    /* Número de versión y fecha */
#define kFECHA       "Oct. 95"

#define kLONG_NOMB   20           /* Longitud de cada dato */
#define kLONG_COM    41           /* (nombre, orden y ayuda) */
#define kLONG_AYU    45

#define kbUP         0x48         /* Teclas especiales */
#define kbDOWN       0x50
#define kbPGDN       0x51
#define kbPGUP       0x49
#define kbINIC       0x47
#define kbFIN        0x4F
#define kbF1         0x3B

/* Espacios en blanco, para "borrar" trozos de pantalla */
#define BLANCO1 "                   "
#define BLANCO2 "                                            "

/* ------------- Prototipos de funciones ---------------- */

void fondo(void);                             /* Fondo de la pantalla */
void recuadro(int x1,int y1,int x2,int y2 );
char escribeDatos(void);                      /* Escribe los datos y
                                                 espera a que se pulse
                                                 una tecla */
void strings(int longit, char letra);         /* Crea una cadena de texto
                                                 repitiendo una letra
                                                 varias veces; guarda el
                                                 resultado en la variable
                                                 "texto" */
void strQuita(char *cadena, int n,            /* Quita n chars de la dcha */
  char* result);                              /* de una cadena de texto */

/* ------------- Variables globales --------------------- */

int colorFondo = 1;  /* Mejor 0 si es monocromo */

char texto[101];     /* Para textos temporales */

struct dato {
  char nomb[kLONG_NOMB];
  char com[kLONG_COM];
  char atj;
  char pau;
  char ayu[10][kLONG_AYU];
};

/* ------------- Datos ---------------------------------- */

#define kMAX_FICHA   2      /* Número de fichas */

struct dato ficha[kMAX_FICHA] = {

  {"WordPerfect 5.1",      /* Nombre de la opción */
    "WP",                  /* Orden de MsDos a ejecutar */
    'W',                   /* Tecla rápida */
    'N',                   /* ¿Hay pausa después? */
    "Wordperfect es el procesador de textos más",
    "extendido para MsDos",
    "",
    "Las teclas más importantes que conviene",
    "recordar son: F3 para ayuda, F1 para",
    "cancelar y F7 para salir.",
    "",
    "",
    "",
    ""},                   /* 10 líneas de ayuda */

  {"Lotus 1.2.3 2.2",      /* Nombre de la opción */
    "C:\\123\\123",        /* Orden de MsDos a ejecutar */
    '1',                   /* Tecla rápida */
    'N',                   /* ¿Hay pausa después? */
    "Lotus 1-2-3 es una hoja de cáculo para MsDos",
    "de las que han creado escuela.",
    "",
    "Para acceder al menú de opciones hay que",
    "pulsar la barra (/) o la tecla que hay",
    "junto a la Z (que en el teclado español",
    "suele ser la de <).",
    "",
    "",
    ""}                    /* 10 líneas de ayuda */

  };

/* ------------- Desarrollo de las funciones ------------ */

void fondo (void)   /* Fondo de la pantalla */
{
  textattr (15 + colorFondo*16);
  clrscr();
  recuadro(1, 1, 80, 24);
  gotoxy (4, 2);
  cprintf( "nMenú, versión %-10s                       Por Nacho Cabanes, %s",
    kVERSION, kFECHA);
  gotoxy (8, 25);
  textattr (7 + colorFondo*16);
  cputs( "Teclas: Flechas para mover, <-+ para ejecutar, ESC para salir");
}

void recuadro( int x1, int y1, int x2, int y2 )  /* Dibujo de recuadros */
{
  int bucle;

  for (bucle = y1 + 1; bucle <= y2 - 1 ; bucle ++)
    {
    gotoxy(x1, bucle);
    putch('¦');
    gotoxy(x2, bucle);
    putch('¦');
    }
  gotoxy (x1, y1);
    strings(78,'-');
    cprintf( "+%s+", texto );
  gotoxy (x1, y2);
    cprintf( "+%s+", texto );
  gotoxy (x1+1, y1 + 2);
    strings(78,'-');
    cprintf( "%s", texto );
  for (bucle = y1 + 3; bucle <= y2 - 1 ; bucle ++)
    {
    gotoxy (25, bucle);
    putch( '¦');
    }
}

char escribeDatos(void)
{
    char opcion;
    char texto[45];
    int i, j;
    int marcado=1;           /* Opción resaltada */
    int inicial=1;           /* Primera opción en pantalla */
    int maximo=kMAX_FICHA;   /* Ultima opción en pantalla */
    int redibujar=1;         /* Si hay que redibujar (por defecto: sí) */

    if (maximo>18)           /* Para mostrar sólo los que deba */
       maximo = 18;

    do                       /* Parte que se repite */
    {
      if (redibujar)                      /* Si hay que redibujar */
      for (i=inicial; i<=maximo; i++)     /* Repito para cada dato */
      {
        strcpy(texto, ficha[i-1].nomb);
        textattr(7 + colorFondo*16);      /* Descripción, en gris */
        gotoxy (4, i-inicial + 5);
        putch(' ');                       /* Borro el atajo anterior */
        gotoxy (4, i-inicial + 5);
        putch(ficha[i-1].atj);            /* Escribe el atajo atual */
        gotoxy (6, i-inicial + 5);
        cputs(BLANCO1);                   /* Borro el título anterior */
        if (i==marcado)
        {
            gotoxy (32, 5);               /* Nombre, en la descripción */
            cputs(BLANCO2);               /* Borro la anterior */
            gotoxy (32, 5);               /* Me vuelvo a colocar */
            textattr(15 + colorFondo*16); /* Blanco sobre azul */
            cputs(texto);
            textattr(7 + colorFondo*16);  /* Descripción, en gris */
            for (j=1; j<=10; j++)
            {
                gotoxy(32,6+j);           /* Líneas de ayuda (10) */
                cputs(BLANCO2);
                gotoxy(32,6+j);
                cputs(ficha[i-1].ayu[j-1]);
            }
            textattr(14 + 16*2);
        }
        else
            textattr(7 + colorFondo*16);
        gotoxy (6, i-inicial + 5);
        cputs(texto);
      }
      redibujar = 1;        /* Por defecto, hay que redibujar */

      opcion = getch();
      switch(opcion)
      {
        case 13:            /* Ejecuta la opción */
          textattr(7);
          clrscr();
          system( ficha[marcado-1].com );
          if (ficha[marcado-1].pau == 'S')
          {                 /* Si hay pausa, espera después */
            gotoxy(40,25);
            textattr(15);
            cputs(" Pulse una tecla para volver... ");
            getch();
          }
          fondo();
          break;

        case 0:                 /* Tecla de control */
          opcion = getch();     /* Leemos el segundo código */
          switch(opcion)
          {
              case kbUP:              /* Subir */
                if (marcado>1) marcado--;
                   else redibujar = 0;
                if (marcado<inicial)
                {
                    inicial--;
                    maximo--;
                }
                break;
              case kbDOWN:            /* Bajar */
                if (marcado<kMAX_FICHA) marcado++;
                  else redibujar = 0;
                if (marcado>maximo)
                {
                  inicial++;
                  maximo++;
                }
                break;
              case kbPGUP:            /* Subir una página */
                if ((inicial==1) && (marcado == 1))
                  redibujar = 0;
                if (marcado>18) marcado -= 18;
                  else marcado=1;
                if (marcado<inicial)
                {
                  inicial = marcado;
                  maximo = inicial+17;
                }
                break;
              case kbPGDN:            /* Bajar página */
                if ((maximo==kMAX_FICHA) && (marcado == maximo))
                  redibujar = 0;
                if (marcado<=kMAX_FICHA-18) marcado += 18;
                  else marcado = kMAX_FICHA;
                maximo = maximo+18;
                if (maximo>kMAX_FICHA) maximo=kMAX_FICHA;
                if (maximo>17)
                  inicial = maximo -17;
                else
                  inicial = 1;
                break;
              case kbINIC:            /* Volver al principio */
                if ((inicial==1) && (marcado == 1))
                  redibujar = 0;
                inicial = 1;
                if (kMAX_FICHA > 17)
                  maximo = 18;
                else
                maximo = kMAX_FICHA;
                marcado = 1;
                break;
              case kbFIN:             /* Ir al final */
                if ((maximo==kMAX_FICHA) && (marcado == maximo))
                  redibujar = 0;
                maximo=kMAX_FICHA;
                if (maximo>17)
                  inicial = maximo -17;
                else
                  inicial = 1;
                marcado = kMAX_FICHA;
                break;
          }
          break;

        default:                  /* Otra tecla: miro si es atajo */
         redibujar = 0;
         for (i=1; i<=kMAX_FICHA; i++)
           if (toupper(opcion) == ficha[i-1].atj)
           {
           textattr(7);
           clrscr();
           system( ficha[i-1].com );
           gotoxy(40,25);
           textattr(15);
           cputs(" Pulse una tecla para volver... ");
           getch();
           fondo();
           redibujar = 1;
           break;
           }
         break;
        }
    }
    while (opcion != 27);          /* Tecla ESC para salir */
    return opcion;
}

/* Crea una cadena de texto repitiendo una letra varias veces;
guarda el resultado en la variable "texto" */

void strings(int longit, char letra)
{
    memset(texto, letra, 100);
    texto[longit] = 0;
}

/* Quita n chars de la dcha de una cadena de texto */

void strQuita(char *cadena, int n, char *result)
{
     int j = n;

     strcpy(result,cadena);
     do
         { result[j-n] = result[j]; j++;}
     while (result[j]);
     result[strlen(cadena)-n] = 0;

}

/* ------------- Cuerpo del prog. ----------------------- */

int main(void)
{
    char tecla;

    fondo();                      /* Prepara el fondo */
    do
       tecla = escribeDatos();    /* Repite el bucle principal */
    while (tecla != 27);          /* hasta que se pulse ESC */

    textattr(7);                  /* Restaura colores y sale */
    clrscr();
    gotoxy(1,1);
    cputs( "Saliendo de NMENU ... ");

    return 0;
}


Curso de C. Fuentes de ejemplo - Representar gráficamente funciones matemáticas en Turbo C.

Para representar la gráfica de una función matemática en pantalla, se puede partir con facilidad de lo que ya hemos hecho.

En general, una función matemática será de la forma:

 y = 3x +10
 y = log (2x^3)
 y = raiz (x^4 - x^2)
 y = sen (3x)

(donde no he usado todavía la sintaxis del lenguaje C, y he empleado el símbolo ^ para indicar "elevado a").

Así, un primer intento de representar una función desde un programa en C sería utilizar la función "putpixel" para dibujar puntos, de modo que la coordenada X iría variando desde 1 hasta 640 (o hasta la anchura de la pantalla, según el modo gráfico que empleemos) y la coordenada Y se calcularía empleando la función que hemos elegido.

Partiendo del primer ejemplo sobre modo gráfico (GRAFTC) podemos hacer unos pequeños cambios:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  Turbo C++
*/
/*  GRAFIC01.C
*/
/*
*/
/*  Representación de
*/
/*  funciones:
*/
/*  Primera aproximación,  */
/*  una recta, sin corre-  */
/*  gir escala ni despla-  */
/*  zamiento.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01     */
/*-------------------------*/

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

void inicio(void)
{
int gdriver = DETECT,
/* Que detecte qué pantalla tengo */
gmode, codError;
/* Modo gráfico, código de error */

   initgraph(&gdriver, &gmode, "");
/* Intento pasar a modo gráfico */
codError = graphresult();
/* Miro si hay error */
if (codError != grOk)
/* Si lo hay... */
{
printf("Error de gráficos:
%s\n",         /* Aviso */
grapherrormsg(codError));
/* Muestro el error */
printf("Pulse una tecla para salir...");
getch();
/* Espero */
exit(1);
/* y abandono el programa */
}

}

int main(void)
/* Cuerpo del programa */
{
int xmax, ymax;
/* Variables locales: aquí guardaré
las coordenadas máximas de la pantalla */
int x,y;
/* x es la primera coordenada de los puntos,
que recorreré con bucle "for"; y es la
segunda coordenada, calculada */

   inicio();                  /* Paso a modo gráfico */

   xmax = getmaxx();
/* Miro cuales son las coordenadas */
ymax = getmaxy();
/* máximas de la pantalla */

   for (x=0; x<xmax; x++)
{
/* Coordenada x: recorro toda la pantalla */
y =  x/2 - 10;
/* Función a dibujar: calculo Y */
putpixel(x,y,WHITE);
/* y muestro cada punto, en blanco */
}

   getch();
/* Espero a que se pulse una tecla */
closegraph();
/* Y termino con el modo gráfico */
return 0;
}

Y el resultado sería algo parecido a esto:

Este fuente tiene más de un problema:

  • La gráfica está “boca abajo”: las coordenadas Y en la pantalla de un PC aumentan según nos desplazamos desde la parte superior de la pantalla hacia la parte inferior, al contrario de lo que ocurre en “las gráficas reales”, de modo que en vez de dibujar la coordenada Y directamente, tendremos que restarla de la coordenada Y máxima, para “dar la vuelta” a la imagen.
  • Estamos calculando el valor de la función (Y) para unos ciertos valores X (desde 1 hasta 640), que no tienen por qué ser los útiles en un caso habitual: nos puede interesar hallar los valores de Y que corresponden a X negativas, o centrarnos en el intervalo [-10,+10], o ver qué ocurre para valores de X superiores a 2000...
  • Lo mismo ocurre con la Y: nos puede interesar variar la "escala", de modo que podamos ver en pantalla puntos que correspondan a una Y negativa o con valores altos (superiores a 1000, por ejemplo).
  • Por otra parte, no estaría mal que se mostrasen los ejes coordenados, para que sepamos si tenemos la gráfica centrada en pantalla o no. También podríamos poner "rayitas" en los ejes, que nos dieran una idea visual de si la escala es mayor o menor.

Con algunas de estas mejoras, nuestro fuente podría quedar así:

/*-------------------------*/
/*  Ejemplo en C: pantalla */
/*  en modo gráfico con    */
/*  Turbo C++
*/
/*  GRAFIC02.C
*/
/*
*/
/*  Representación de
*/
/*  funciones:
*/
/*  Segunda aproximación,  */
/*  una parábola, corri-   */
/*  giendo escala y despla-*/
/*  zamiento.
*/
/*
*/
/*  Comprobado con:
*/
/*    - Turbo C++ 1.01     */
/*-------------------------*/

#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <math.h>

void inicio(void)
{
int gdriver = DETECT,
/* Que detecte qué pantalla tengo */
gmode, codError;
/* Modo gráfico, código de error */

   initgraph(&gdriver, &gmode, "");
/* Intento pasar a modo gráfico */
codError = graphresult();
/* Miro si hay error */
if (codError != grOk)
/* Si lo hay... */
{
printf("Error de gráficos:
%s\n",         /* Aviso */
grapherrormsg(codError));
/* Muestro el error */
printf("Pulse una tecla para salir...");
getch();
/* Espero */
exit(1);
/* y abandono el programa */
}

}

int main(void)
/* Cuerpo del programa */
{
int xmax, ymax;
/* Variables locales: aquí guardaré
las coordenadas máximas de la pantalla */
float x,y;
/* x es la primera coordenada de los puntos,
que recorreré con bucle "for"; y es la
segunda coordenada, calculada */

   int xPant,yPant;        /* Coordenadas que se veran en pantalla, una
                              vez escaladas y desplazadas */

   float xIni, xFin,
/* Limites de visualizacion */
escalaX, escalaY,
/* Escala en X (calculada) y en Y (fijada) */
despX, despY,
/* Desplaz en X (calculado) y en Y (fijado) */
xIncr;
/* Incremento en X (inversa de la escala) */

   inicio();               /* Paso a modo gráfico */

   xmax = getmaxx();
/* Miro cuales son las coordenadas */
ymax = getmaxy();
/* máximas de la pantalla */

   xIni = -10;
/* Limites a usar en el eje X */
xFin = 10;

   escalaX = xmax / (xFin-xIni);  /* Escala en X (calculada) */
   escalaY = 10;           /* Escala en Y (prefijada) */

   despX = xmax/2;
/* Desplazamiento en pantalla del (0,0) */
despY = ymax/2;
xIncr = 1/escalaX;

   /* Dibujo el eje de abcisas (X) */
   setcolor(YELLOW);
   moveto (0, despY);
   linerel (xmax,0);

   /* Dibujo el eje de ordenadas (Y) */
   setcolor(YELLOW);
   moveto (despX, 0);
   linerel (0,ymax);

   for (x=xIni; x<xFin; x+=xIncr)
{
/* Coordenada x: recorro toda la pantalla */
y =  x*x -2*x - 10;
/* Función a dibujar:  Y = X^2 - 2x +10 */
xPant = (int) ( x*escalaX + despX );
yPant = (int) ( - y*escalaY + despY );
putpixel(xPant,yPant,WHITE);
/* y muestro cada punto */
}

   getch();
/* Espero a que se pulse una tecla */
closegraph();
/* Y termino con el modo gráfico */
return 0;
}

Y el resultado sería algo parecido a esto:

Aun así, sigue siendo muy mejorable: los ejes aparecen centrados en la pantalla (y para ciertas gráficas como las raíces cuadradas, no tiene sentido mostrar la parte negativa del eje X, porque no la podremos utilizar), no hemos incluido esas rayitas que había dicho para dar una ida visual de la escala que hemos empleado, la función no se puede teclear por el usuario sino que ha de estar programada en el fuente, ni siquiera le permitimos que teclee los valores límites de X ni la escala de Y, etc.

Pero esas mejoras (y otras muchas que pueden surgir - de hecho, a mí se me ocurre más de una) quedan como ejercicios para quien quiera retocarlo...


Curso de C. OOP y C++.

Índice de temas.

Introducción a la Programación Orientada a Objetos

1. Primera toma de contacto con C++.
2. Constructores y destructores; fuente en dos bloques.
3. Primer ejemplo de herencia.
4. Dos objetos que interaccionan.
5. Modificaciones para otro compilador.
6. Otros cambios en C++ frente a C.
7. Sobrecarga de operadores. Funciones amigas.
8. Más sobre entrada y salida en C++.
9. Ficheros en C++.

A. El nuevo estándar de “strings” en C++.


Curso de C. OOP y C++. Introducción a la programación orientada a objetos.

El lenguaje C++ es un lenguaje creado con la base del lenguaje C, pero ampliado para permitir Programación Orientada a Objetos (también hay alguna otra diferencia, que ya iremos viendo).

Por eso, vamos a empezar viendo las generalidades de la Programación Orientada a Objetos y después iremos particularizando para el caso de C++...

(Nota: quien haya seguido el curso de Pascal, verá que esta introducción es casi idéntica, pero aun así, le aconsejo que la lea, porque hay algún cambio que espero que sea interesante).
Hasta ahora estamos estado "cuadriculando" todo para obtener algoritmos: tratábamos de convertir cualquier cosa en funciones y variables que pudieramos emplear en nuestros programas.

Pero no todo lo que nos rodea es tan fácil de cuadricular. Supongamos por ejemplo que tenemos que introducir datos sobre una puerta en nuestro programa. Nos limitamos a programar las funciones AbrirPuerta y CerrarPuerta? Al menos, deberíamos ir a la zona de declaración de variables, y allí guardaríamos otros datos como su tamaño, color, etc.

No está mal, pero es antinatural. ¿A qué me refiero? Pues a que una puerta es un conjunto: no podemos separar su color de su tamaño, o de la forma en que debemos abrirla o cerrarla. Sus características son tanto las físicas (lo que hasta ahora llamábamos variables) como sus comportamientos en distintas circunstancias (lo que para nosotros eran las funciones). Todo ello va unido, formando un OBJETO.

Por otra parte, si tenemos que explicar a alguien lo que es el portón de un garaje, y ese alguien no lo ha visto nunca, pero conoce cómo es la puerta de su casa, le podemos decir "se parece a una puerta de una casa, pero es más grande para que quepan los coches, está hecha de metal en vez de madera...". Las dos cosas son puertas: se trata de dos objetos que pertenecen a la misma CLASE.

Finalmente, conviene recordar que "abrir" no se refiere sólo a una puerta. También podemos hablar de abrir una ventana o un libro, por ejemplo.

Pues con esta discusión filosófica ;-) hemos comentado casi sin saberlo las tres características más importantes de la Programación Orientada a Objetos (OOP):

  • ENCAPSULACION: No podemos separar los comportamientos de las características de un objeto. Los comportamientos serán procedimientos o funciones, que en OOP llamaremos METODOS. Las características serán variables, como las que hemos usado siempre, que en OOP llamaremos ATRIBUTOS de un objeto. La apariencia de un objeto en C++, como veremos un poco más adelante, recordará a un registro o "struct".
    (Nota: en C++ es frecuente llamar también "variables de instancia" o "variables miembro" a los atributos, y "funciones miembro" a los métodos).
  • HERENCIA: Unos objetos pueden "heredar" métodos y atributos de otros. Esto hace más fácil definir objetos nuevos a partir de otros que ya teníamos anteriormente (como ocurría con el portón y la puerta) y facilitará la reutilización de los programas, pudiendo aprovechar buena parte de los anteriores... si están bien diseñados.
  • POLIMORFISMO: Un mismo nombre de un método puede hacer referencia a comportamientos relativamente distintos (como abrir una puerta o abrir un libro). Igual ocurre para los datos: el peso de una puerta y el de un portón los podemos llamar de igual forma, pero obviamente no valdrán lo mismo. En C++ el polimorfismo llega incluso al nivel de los operadores: podremos "redefinir" el operador + para que sume matrices, ficheros o cualquier tipo de objeto que nos interese. (Realmente, este tipo de polimorfismo recibe el nombre de sobrecarga, como ya comentaremos).

Comentado esto, vamos a empezar a ver ejemplos en C++ para tratar de fijar estos primeros conceptos y de ver la sintaxis de este lenguaje. A partir de unos primeros ejemplos sencillos, iremos profundizando paulatinamente.


Curso de C. OOP y C++. Introducción a C++ (1).

En C++, lo habitual (y lo que se debe hacer) es dividir la definición de un objeto en dos partes:
  • Las variables (atributos) y los prototipos de las funciones (métodos) se suelen definir en un fichero de cabecera (los que en C tenían extensión .h, y que en C++ será preferible indicar con extensión .hpp ó .hh).
  • Los detalles concretos de cómo trabaja cada función (el desarrollo) se definen en un fichero fuente normal (los que en C tenían extensión .c, y que en C++ tendrán extensión .cpp ó .cc ó .C).

A pesar de ello, en el primer ejemplo incluiremos todo junto en un único fichero, para tener una visión de conjunto algo mejor, y poder explicar cada una de las cosas nuevas que aparecen. A partir del segundo ejemplo será cuando empecemos a hacer las cosas "como Dios manda".

Vamos a empezar por hacer algo frecuente: crear un objeto "pantalla", que nos aisle totalmente del hardware, del sistema operativo y del compilador que estemos utilizando. Así, luego podremos definir un objeto "ventana" (por ejemplo), que muestre la información accediendo al objeto "pantalla".

Si después queremos que nuestro programa funcione en otro entorno distinto, bastará con crear una nueva versión del objeto "pantalla", que internamente trabaje de forma distinta, pero externamente responda a los mismos mensajes que el objeto "pantalla" inicial.

Así, todo el resto del programa funcionará en el nuevo entorno sin ningún cambio adicional.

En nuestro caso, nos centraremos en un objeto que nos oculte el acceso directo a la pantalla, y que estará particularizado (inicialmente) para el compilador Turbo C++, bajo MsDos. Después del fuente están los comentarios sobre él.

  //
  //  pant01.cpp
  //
  //  Introducción a C++
  //  Primer ejemplo (en un sólo fuente)
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #include <conio.h>

  // ---------------------------------------------------------
  // CLASE  : PantallaTexto
  // Oculta el funcionamiento de la pantalla en modo texto.
  // ---------------------------------------------------------

  class PantallaTexto {

      char maxX, minX;
// Coordenadas máxima y mínima (horizontal)
char maxY, minY;
// Coordenadas máxima y mínima (vertical)

  public:

      void Borra();             // Borra toda la pantalla
      void Escribe(int X,
        int Y, char *texto);    // Escribe un mensaje en ciertas coordenadas

      int LeerMaxX();           // Devuelve coordenada X máxima

  };

  // ==========================================================================
//
//  A continuación viene el desarrollo de las
funciones (métodos)
//
// ========

  /* ----------------- PantallaTexto -------------------------
*/
/* Borra toda la pantalla
*/
/* _________________________________________________________
*/

  void
  PantallaTexto::Borra() {
      clrscr();
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Escribe un mensaje en ciertas coordenadas
*/
/* _________________________________________________________
*/

  void
  PantallaTexto::Escribe(int X, int Y, char *texto) {
      gotoxy(X, Y);
      cputs(texto);
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada horizontal máxima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMaxX() {
      return maxX;
  }

  // ==========================================================================
  //
  //  Finalmente: un programa sencillo de ejemplo
  //
  // ========

  PantallaTexto pant;          // Definimos un objeto de la clase "PantallaTexto"

  main() {
      pant.Borra();
      pant.Escribe(30,14, "Hola");
      return 0;
  }

Como este fuente es el primero que hacemos, hay muchas cosas que comentar:

  • Ya en la primera línea, aparece texto después de una doble barra (//). Se trata de un comentario hasta fin de línea: el comentario comienza después de // y termina cuando acaba la línea.
  • Después definimos una clase de objetos: la clase PantallaTexto.
  • Esta clase tiene ciertas variables (atributos), como por ejemplo las coordenadas máxima y mínima, que nos pueden interesar para poder saber si cierto texto cabrá en la pantalla, o para centrarlo.
  • También será capaz de hacer ciertas cosas: borrarse o mostrar un texto, por ejemplo.
  • Estas funciones (métodos) son públicas: se podrá acceder a ellas desde fuera de la definición del objeto (desde un programa que use algún objeto de la clase PantallaTexto, por ejemplo). Por el contrario, las variables (atributos) son privadas: no se podrán leer ni modificar salvo desde los propios métodos del objeto. Esto DEBERIA HACERSE SIEMPRE ASI, para que el programa que use nuestro objeto sea independiente de cómo el objeto es internamente. (Nota: un poco más adelante veremos algún detalle más sobre partes públicas y privadas de una clase)
  • Por eso, he definido el atributo "maxX" como "char", para que ocupe poco espacio en memoria, pero el método "LeerMaxX" devuelve un valor "int": si después nos damos cuenta de que no nos basta con un "char", porque trabajamos con pantallas capaces de mostrar muchísimo texto, redefinimos el dato "maxX" y lo convertimos en "int". El programa internamente se ha corregido, pero al usuario que emplea nuestro objeto pantalla no le afecta el cambio: antes leía un número entero y ahora sigue leyendo un número entero.
  • Para detallar el funcionamiento interno de cada función (método), tenemos que indicar el nombre de la clase y el nombre del método, separados por dos símbolos de "dos puntos" (el conjunto de los dos se llama “operador de resolución de ámbito”):
     void PantallaTexto::Borra()
  • Ya en el programa de ejemplo, definimos un objeto de esta clase y accedemos a sus métodos empleando el nombre del objeto y el nombre del método, separados por un punto:
     pant.Borra();
En C++, a esto se le suele llamar "Pasar un mensaje a un objeto": en nuestro caso, estamos pasando al objeto "pant" el mensaje de que se borre, a lo que el objeto responde siguiendo los pasos que se le han indicado en el método "Borra".
De hecho, si fueramos puristas, deberíamos haber creado incluso un objeto "aplicación", de modo que el programa fuese un conjunto de objetos que se envían mensajes unos a otros e interactuan.
Espero que todo esto no resulte muy confuso. Son muchos conceptos nuevos, pero confío en que se vayan entendiendo y recordando a base de usarlos.


Curso de C. OOP y C++. Introducción a C++ (2).

Este primer fuente ya hace algo, pero queda mucho por mejorar: nos podría interesar comprobar si un cierto texto va a caber en la pantalla, con algo como

      if (pant.LeerMaxX() > 40)
        pant.Escribe(45,8, "Hola");

(si la pantalla tiene más de 40 letras en la horizontal, escribir el texto). Esto todavía no lo podemos hacer, porque no tenemos el valor del tamaño horizontal de la pantalla. Quien ya haya programado en C, se puede sentir tentado a dale un valor inicial haciendo

  class PantallaTexto {
char maxX=80, minX=1;
// Coordenadas máxima y mínima (horizontal)
[...]

pero esto no es válido en C++. Eso sí, tenemos otra opción más elegante: podemos definir un método que se ponga en funcionamiento nada más crearse el objeto, y que será el encargado de dar valores iniciales a las variables, de reservar memoria dinámica si fuera necesario, etc. Este es el método CONSTRUCTOR, que se definiría de la siguiente forma:

  PantallaTexto::PantallaTexto() {
      maxX = 80;   minX = 1;
      maxY = 25;   minY = 1;
  }

(es decir, tiene como nombre el mismo que la clase a la que pertenece, y no devuelve valor de ningún tipo.

Si hubieramos reservado memoria dinámica (no es nuestro caso), también nos interesaría utilizar un método DESTRUCTOR, que liberase esta memoria cuando el objeto se deja de utilizar, y que tendría un aspecto parecido a esto:

  PantallaTexto::~PantallaTexto() {
      // Aquí liberaríamos el espacio reservado, etc.
  }

(es decir, el mismo nombre que la clase pero precedido por ~ ).

Pues ahora ya vamos a crear nuestra clase "PantallaTexto", pero esta vez un poco más completa, y dividida en dos partes: un fichero de cabecera para las definiciones y otro "normal" para el desarrollo.

El fichero de cabecera podría ser así:

  //
  //  pant02.h
  //
  //  Introducción a C++
  //  Segundo ejemplo (en dos fuentes)
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #ifndef _PANT2_H
  #define _PANT2_H

  // ---------------------------------------------------------
  // CLASE  : PantallaTexto
  // Oculta el funcionamiento de la pantalla en modo texto.
  // ---------------------------------------------------------

  class PantallaTexto {

      char maxX, minX;
// Coordenadas máxima y mínima (horizontal)
char maxY, minY;
// Coordenadas máxima y mínima (vertical)

  public:

      PantallaTexto();          // Método constructor

      void Borra();             // Borra toda la pantalla
      void Escribe(int X,
        int Y, char *texto);    // Escribe un mensaje en ciertas coordenadas

      int LeerMaxX();
// Devuelve coordenada X máxima
int LeerMinX();
// Devuelve coordenada X mínima
int LeerMaxY();
// Devuelve coordenada Y máxima
int LeerMinY();
// Devuelve coordenada Y mínima

  };

  #endif

No debería haber ninguna dificultad. Lo único "poco habitual", pero que ya hemos visto en el tema de programas a partir de varios fuentes es el #ifndef y el #define del principio, que nos sirven para permitir que este fichero de cabecera pueda ser accedido desde varios fuentes y a la vez asegurar que realmente no se incluya mas que una única vez.

El fichero que realmente contiene la definición del objeto podría ser como éste:

  //
  //  pant02.cpp
  //
  //  Introducción a C++
  //  Segundo ejemplo (en un sólo fuente)
  //    (usado por "ejcpp02.cpp")
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #include <conio.h>
  #include "pant02.h"

  // ==========================================================================
  //  A continuación viene el desarrollo de las funciones (métodos)
  //

  /* ----------------- PantallaTexto -------------------------
*/
/* Método constructor: valores iniciales
*/
/* _________________________________________________________
*/

  PantallaTexto::PantallaTexto() {
      maxX = 80;   minX = 1;
      maxY = 25;   minY = 1;
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Borra toda la pantalla
*/
/* _________________________________________________________
*/

  void
  PantallaTexto::Borra() {
      clrscr();
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Escribe un mensaje en ciertas coordenadas
*/
/* _________________________________________________________
*/

  void
  PantallaTexto::Escribe(int X, int Y, char *texto) {
      gotoxy(X, Y);
      cputs(texto);
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada horizontal máxima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMaxX() {
      return maxX;
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada horizontal mínima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMinX() {
      return minX;
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada vertical máxima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMaxY() {
      return maxY;
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada vertical mínima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMinY() {
      return minY;
  }

Hay muy pocas novedades. Aun así, conviene recordar la diferencia entre los dos #include:

   #include <conio.h>   entre <> porque es un fichero del sistema.
   #include "pant02.h"  entre "" porque es creado por nosotros.

Así que ahora sigamos concretando más las cosas que hemos ido dejando un poco en el aire:

Sobre los especificadores de acceso:

  • Hemos comentado que en el fichero .h los atributos son privados y los métodos son públicos. Está claro por qué son públicos los métodos: los hemos precedido con la palabra public. Así, desde cualquier otro objeto o función se podrá acceder a ellos.
  • Los atributos son privados (sólo podrán ser leídos o modificados por los métodos del propio objeto), y esto lo podemos conseguir precediéndolos con la palabra private. No lo hemos hecho así porque ese es el valor por defecto: si no indicamos lo contrario, se asume que los atributos y métodos son privados.
  • Existe un tercer modo de acceso, que se denota con la palabra protected. Los atributos y/o métodos que definamos como protegidos serán accesibles sólo por los métodos de este objeto y por sus descendientes (dentro de poco veremos la herencia).
  • Hay una excepción a todo esto: podremos declarar funciones "amigas" (friend), que sí tendrán acceso a los miembros privados y/o protegidos, pero es algo que no trataremos... al menos por ahora.

Sobre las extensiones de los ficheros:

  • Estamos usando la extensión .cpp para los fuentes, pero hemos comentado que existen otras:
    • cpp es la extensión estándar en MsDos, es decir que si a un compilador de C y C++ para MsDos le decimos que compile un fichero cuya extensión es .cpp, sabrá que se trata de un fuente en C++ y no en C.
    • cc es la extensión estándar en Unix (si intentamos compilar un fuente con extensión cpp desde Unix es posible que el ordenador no reconozca esa extensión como una propia de C++, e intente compilarlo como si se tratase de un fuente de C, de modo que no funcionará).
    • C (en mayúsculas) es otra extensión que se puede usar en Unix, pero que es menos aconsejable porque existen muchos sistemas operativos que no distinguen entre mayúsculas y minúsculas, lo que dará problemas de portabilidad (no se podrá distinguir los fuentes en C, con extensión .c, de los de C++, con extensión .C, si los llevamos a uno de estos sistemas; es el caso de MsDos y de Windows).
  • Las extensiones .hpp (bajo MsDos) y .hh (bajo Unix) son aconsejables para indicar que se trata de un fichero de cabecera de C++, pero no es imprescindible, de modo que muchos programadores usan de todas formas la extensión .h, y eso mismo será lo que voy a hacer yo, para intentar no complicar demasiado la situación.

Después de todas estas aclaraciones, veamos cómo sería un fuente que emplease un objeto de la clase "PantallaTexto":

  //
  //  ejcpp02.cpp
  //
  //  Introducción a C++
  //  Segundo ejemplo (en dos fuentes)
  //    (este fuente se debe usar con
  //    "pant02.cpp")
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #include "pant02.h"

  //  Programa sencillo de ejemplo

  PantallaTexto pant;          // Definimos un objeto de la clase "PantallaTexto"

  main() {
      pant.Borra();
      pant.Escribe(30,14, "Hola");
      if (pant.LeerMaxX() > 40)
        pant.Escribe(45,8, "Hola");
      return 0;
  }

Este fuente, que no debería tener ninguna dificultad, se puede compilar con Turbo C++ 1.01 para MsDos tecleando

  TCC  EJCPP02.CPP  PANT02.CPP

(le indicamos el nombre del programa principal y a continuación el de los auxiliares que necesitamos, en este caso sólo uno).


Curso de C. OOP y C++. Introducción a C++ (3).

Ahora podemos plantearnos mejorar esta clase de objetos para "pantalla de texto", y añadirle una serie de posibilidades extras, o modificar ligeramente su comportamiento actual.

Pues aquí entra en juego una de las posibilidades más interesantes de la Programación Orientada a Objetos: la reutilización. No necesitamos partir de cero, sino que podemos crear una clase que tome como base la que ya existe, "heredando" de ella todas sus características. Podremos redefinir las que no nos interesen, o añadir otras nuevas.

Por ejemplo, vamos a crear una clase "pantalla de texto" mejorada, que nos permita además escribir un texto centrado en cierta línea de la pantalla.

Igual que antes, crearemos un fichero de cabecera, otro de desarrollo y otro que pruebe el resultado.

El fichero de cabecera será:

  //
  //  pant03.h
  //
  //  Introducción a C++
  //  Tercer ejemplo (en dos fuentes)
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #ifndef _PANT3_H
  #define _PANT3_H

  #include "pant02.h"

  // ---------------------------------------------------------
  // CLASE  : PantallaTM
  // Pantalla en modo texto, mejorada
  // ---------------------------------------------------------

  class PantallaTM: public PantallaTexto {

  public:

      void EscribeCent(
        int Y, char *texto);    // Escribe un mensaje centrado

  };

  #endif

Sólo hay una novedad: la clase PantallaTM es un caso concreto de "pantalla de texto", un descendiente de ésta. Por eso la declaramos diciendo que proviene de PantallaTexto (el indicador de acceso "public" es para que los métodos y atributos sigan siendo como en la clase "padre": en nuestro caso concreto, los atributos eran y seguirán siendo privados, y los métodos eran y seguirán siendo públicos).

El fichero de desarrollo de la clase "PantallaTM" será

  //
  //  pant03.cpp
  //
  //  Introducción a C++
  //  Tercer ejemplo (en dos fuentes)
  //    (usado por "ejcpp03.cpp")
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #include <conio.h>
  #include <string.h>
  #include "pant03.h"

  // ==========================================================================
  //  A continuación viene el desarrollo de las funciones (métodos)
  //

  /* ----------------- PantallaTexto -------------------------
*/
/* Escribe un mensaje centrado en una línea
*/
/* _________________________________________________________
*/

  void
PantallaTM::EscribeCent( int Y, char *texto) {
Escribe( (LeerMaxX() - LeerMinX()
- strlen(texto)) / 2, Y, texto);
}

Para acceder a los valores máximo y mínimo de las coordenadas X e Y, hemos empleado las funciones LeerMaxX() y similares, dado que no estamos en la clase original PantallaTexto, sino en una que desciende de ella:

   gotoxy( (LeerMaxX() - LeerMinX() - strlen(texto)) / 2, Y);

Si quisiéramos acceder directamente a los atributos, no deberíamos haberlos declarado como "private", sino como "protected", como ya se comentó en el apartado anterior:

   class PantallaTexto {

   protected:
char maxX, minX;
// Coordenadas máxima y mínima (horizontal)
char maxY, minY;
// Coordenadas máxima y mínima (vertical)

   public:
PantallaTexto();
// Método constructor
// ...

con lo que el cuerpo del método EscribeCent quedaría

   gotoxy( maxX - minX - strlen(texto)) / 2, Y);

El ejemplo que usa esta clase "PantallaTM" podría ser:

  //
  //  ejcpp03.cpp
  //
  //  Introducción a C++
  //  Tercer ejemplo (en dos fuentes)
  //    (este fuente se debe usar con
  //    "pant02.cpp" y "pant03.cpp")
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #include "pant03.h"

  //  Programa sencillo de ejemplo

  PantallaTM pant;          // Objeto de la clase "PantallaTexto" mejorada

  main() {
      pant.Borra();
      pant.Escribe(30,14, "Hola");
      pant.EscribeCent(11, "Centrado");
      if (pant.LeerMaxX() > 40)
        pant.Escribe(45,8, "Hola");
      return 0;
  }

Para compilarlo, le indicamos al compilador el nombre de los ficheros fuente que debe combinar para crear el ejecutable:

   TCC  EJCPP03.CPP  PANT03.CPP  PANT02.CPP

En este caso, y al igual que ya hicimos con el ejemplo anterior, indicamos primero EJCPP03 porque queremos que sea éste el fichero que dé nombre al ejecutable.

En la mayoría de compiladores, existe una posibilidad más cómoda que ésta de escribirle uno a uno los nombres de los fuentes implicados: podemos crear un PROYECTO e indicarle qué ficheros van a ser los necesarios para construirlo. En la mayoría de los casos, esto ayudará incluso a que el compilador sólo reconstruya los que realmente hayan cambiado, en vez de volver a crear todos, y así se gana en rapidez y comodidad.


Curso de C. OOP y C++. Introducción a C++ (4).

Ahora vamos a crear una nueva clase de objetos que se utilice la anterior. Puede tratarse, por ejemplo, de una clase "ventana", que se encargue de mostrar ventanas en una pantalla de texto.

Cada ventana podrá tener un cierto tamaño, estar en cierta posición y contener una línea de texto. No he contemplado la posibilidad de que se puedan borrar las ventanas y el fondo de la pantalla quede restaurado como estaba, ni de que se pueda escribir más de una línea de texto dentro de la ventana (ni otras muchas)

Así, la cabecera podría ser ésta:

  //
  //  vent01.h
  //
  //  Introducción a C++
  //  Cuarto ejemplo
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #ifndef _VENT1_H
  #define _VENT1_H

  #include "pant03.h"

  // ---------------------------------------------------------
// CLASE  : Ventana
// Para dibujar en pantalla ventanas con una línea
de texto.
// ---------------------------------------------------------

  class Ventana {

      char maxX, minX;
// Coordenadas máxima y mínima (horizontal)
char maxY, minY;
// Coordenadas máxima y mínima (vertical)

      char msg[80];
// El mensaje que mostrará la ventana
char msgX, msgY;
// Coordenadas del mensaje en la ventana

      PantallaTM pant;     // La pantalla en la que escribir

  public:

      Ventana(PantallaTM p);          // Métodos constructores: 3 distintos,
      Ventana(PantallaTM p, int x1,   //   según los parámetros
        int y1, int x2, int y2);
      Ventana(PantallaTM p, int x1,
        int y1, int x2, int y2, char* texto);

      int FijaCoords(int x1,     // Fija el tamaño
        int y1, int x2, int y2);

      void Escribe(int X,
int Y, char *texto);
// Escribe un mensaje en ciertas coordenadas
void Escribe(int Y,
char *texto);
// Escribe un mensaje en una línea
void Escribe(char *texto); // Escribe
un mensaje centrado

      void Muestra();            // Muestra la ventana

  };

  #endif

Hay apenas una novedad interesante: hay 3 métodos constructores distintos y otros 3 métodos "Escribe" distintos, cada uno con distintos parámetros, y cada uno se comporta de forma ligeramente distinta. Es un ejemplo de polimorfismo (en este caso concreto, en que las 3 funciones tienen igual nombre y distintos parámetros, es un caso especial de polimorfismo llamado sobrecarga, se diría que la función Escribe “está sobrecargada”).

El desarrollo de la clase "Ventana" podría ser éste:

  //
  //  vent01.cpp
  //
  //  Introducción a C++
  //  Cuarto ejemplo
  //    (usado por "ejcpp04.cpp")
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #include "vent01.h"
  #include <conio.h>
  #include <string.h>

  // ==========================================================================
  //  A continuación viene el desarrollo de las funciones (métodos)
  //

  /* ----------------- Ventana -------------------------------
*/
/* Métodos constructores: valores iniciales
*/
/* _________________________________________________________
*/

  Ventana::Ventana(PantallaTM p) {
      maxX = 60;   minX = 20;
      maxY = 20;   minY = 6;

      msgX = 30;   msgY = 8;
      strcpy(msg,"");

      pant = p;
  }

  Ventana::Ventana(PantallaTM p, int x1, int y1, int x2, int
y2) {
maxX = x2;   minX = x1;
maxY = y2;   minY = y1;

      msgX = 30;   msgY = 8;
      strcpy(msg, "");

      pant = p;
  }

  Ventana::Ventana(PantallaTM p, int x1, int y1, int x2, int
y2,
char *texto) {
maxX = x2;   minX = x1;
maxY = y2;   minY = y1;

      msgX = 30;   msgY = 8;
      strcpy(msg, texto);

      pant = p;
  }

  /* ----------------- Ventana -------------------------------
*/
/* Fija las coordenadas, comprobando si son válidas
*/
/* _________________________________________________________
*/

  int
Ventana::FijaCoords(int x1, int y1, int x2, int y2) {
if ((x1 < pant.LeerMinX()) ||
(x2 > pant.LeerMaxX())
||
(y1 <
pant.LeerMinY()) ||
(y2 > pant.LeerMaxY())
) return 0;
maxX = x2;   minX = x1;
maxY = y2;   minY = y1;
return 1;
}

  /* ----------------- Ventana -------------------------------
*/
/* Escribe un mensaje en ciertas coordenadas
*/
/* _________________________________________________________
*/

  void
  Ventana::Escribe(int X, int Y, char *texto) {
      msgX = X;
      msgY = Y;
      strcpy(msg, texto);
  }

  /* ----------------- Ventana -------------------------------
*/
/* Escribe un texto centrado en una línea
*/
/* _________________________________________________________
*/

  void
Ventana::Escribe(int Y, char *texto) {
char X = (maxX - minX - strlen(texto))
/ 2;
Escribe(X, Y, texto);
}

  /* ----------------- Ventana -------------------------------
*/
/* Escribe un texto centrado en la ventana
*/
/* _________________________________________________________
*/

  void
  Ventana::Escribe(char *texto) {
      char Y = (maxY - minY) / 2;
      Escribe(Y, texto);
  }

  /* ----------------- Ventana -------------------------------
*/
/* Muestra la ventana
*/
/* _________________________________________________________
*/

  void
  Ventana::Muestra() {
      int i, j;

      // Borde superior
      for (i=minX; i<=maxX; i++)
        pant.Escribe(i, minY, "-");

      // Laterales que bajan
for (i=minY+1; i<=maxY-1; i++)
{
pant.Escribe(minX, i,
"|");
pant.Escribe(maxX, i,
"|");

      // Borde inferior
      for (i=minX; i<=maxX; i++)
        pant.Escribe(i, maxY, "-");

      // Texto de la ventana
      pant.Escribe (minX+msgX, minY+msgY, msg);
  }

Confío en que en este desarrollo no haya ninguna dificultad. Se apoya en parte en el objeto de clase "PantallaTm", y las rutinas "Escribe" se basan unas en otras. La rutina "Muestra" es sencilla aunque eso hace que tampoco es resultado sea muy lucido.

Finalmente, un ejemplo que usase las clases de objetos "Ventana" y "PantallaTM" podría ser:

  //
  //  ejcpp04.cpp
  //
  //  Introducción a C++
  //  Cuarto ejemplo
  //    (este fuente se debe usar con
  //    "pant02.cpp", "pant03.cpp"
  //    y "vent01.cpp")
  //  Versión para TC++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #include "pant03.h"
  #include "vent01.h"

  //  Programa sencillo de ejemplo

  PantallaTM pant;
// Objeto de la clase "PantallaTexto" mejorada
Ventana  vent1(pant);     // Primera
ventana, vacía
Ventana  vent2(pant,
// Segunda, con coordenadas ya fijadas
40, 5, 75, 11);

  main() {
      pant.Borra();
      pant.Escribe(10,10, "Pantalla de fondo");

      if (!vent1.FijaCoords(25, 12, 55, 18)) {
          pant.Escribe(10,20, "La ventana no cabe!");
          return 0;

      vent1.Escribe("Texto en la ventana");
      vent1.Muestra();

      vent2.Escribe(2,2,"Segunda ventana");
      vent2.Muestra();

      pant.EscribeCent(23, "Texto centrado
en la pantalla de fondo");
return 0;
}

En este programa se ve la forma de utilizar los distintos métodos constructores para crear las ventanas, así como las dos llamadas a "Escribe" con distintos parámetros.

Para poner en marcha este programa, deberemos compilarlo creando un fichero de proyeto o indicando al compilador qué fuentes lo componen:

   TCC  EJCPP04.CPP  VENT01.CPP  PANT03.CPP  PANT02.CPP

El resultado de este ejemplo tiene un aspecto parecido a éste:

                                  ------------------------------------
                                  |                                  |
                                  | Segunda ventana                  |
                                  |                                  |
                                  |                                  |
    Pantalla de fondo             |                                  |
                                  ------------------------------------
                   -------------------------------
                   |                             |
                   |                             |
                   |    Texto en la ventana      |
                   |                             |
                   |                             |
                   -------------------------------

               Texto centrado en la pantalla de fondo

Reconozco que tampoco es demasiado vistoso, pero todavía somos principiantes en C++...


Curso de C. OOP y C++. Introducción a C++ (5).

Modificaciones para otro compilador.

Los fuentes en C++ que hemos estado haciendo hasta ahora estaban creados pensando en Turbo C++ 2nd Edition. Funcionan sin ningún cambio en compiladores que usen las mismas rutinas de escritura en pantalla, como es el caso de Djgpp 2.01 y de Borland C++ 4.52 (y de otros; estos son los dos que yo poseo, y con los que los he comprobado).

En el gaso de Djgpp, sólo cambia el nombre del compilador, así que escribiremos

   GCC  EJCPP04.CPP  VENT01.CPP  PANT03.CPP  PANT02.CPP

Nota, como ya comentamos en el tema sobre Djgpp, el fichero resultante se llamará A.EXE; si queremos otro nombre más "vistoso", deberemos emplear la opción -o:

   GCC  EJCPP04.CPP  VENT01.CPP  PANT03.CPP  PANT02.CPP -O EJCPP04.EXE

En el de Borland C++ 4.52, basta también con cambiar el nombre del compilador:

   BCC  EJCPP04.CPP  VENT01.CPP  PANT03.CPP  PANT02.CPP

Ahora vamos a hacer funcionar todo bajo otro compilador para el cual el manejo de pantalla sea distinto. Por ejemplo, en nuestro caso se va a tratar de Symantec C++ 6.0, también bajo MsDos.

Los cambios se deberán hacer en los módulos que dependan del compilador o del hardware. En nuestro caso, eso sólo ocurre con los detalles de desarrollo de la clase PantallaTexto, que se encuentran en el fichero PANT02.CPP.

Una versión de este módulo para Symantec C++ podría ser:

  //
  //  pant02.cpp
  //
  //  Introducción a C++
  //  Segundo ejemplo (en un sólo fuente)
  //    (usado por "ejcpp02.cpp")
  //  Versión para Symantec C++ (MsDos)
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

  #include <disp.h>
  #include "pant02.h"

  // ==========================================================================
  //  A continuación viene el desarrollo de las funciones (métodos)
  //

  /* ----------------- PantallaTexto -------------------------
*/
/* Método constructor: valores iniciales
*/
/* _________________________________________________________
*/

  PantallaTexto::PantallaTexto() {
      maxX = 80;   minX = 1;
      maxY = 25;   minY = 1;

      disp_open();
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Borra toda la pantalla
*/
/* _________________________________________________________
*/

  void
  PantallaTexto::Borra() {
      disp_move(0,0);
      disp_eeop();
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Escribe un mensaje en ciertas coordenadas
*/
/* _________________________________________________________
*/

  void
  PantallaTexto::Escribe(int X, int Y, char *texto) {
      disp_move(X+1, Y+1);
      disp_puts(texto);
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada horizontal máxima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMaxX() {
      return maxX;
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada horizontal mínima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMinX() {
      return minX;
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada vertical máxima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMaxY() {
      return maxY;
  }

  /* ----------------- PantallaTexto -------------------------
*/
/* Devuelve la coordenada vertical mínima
*/
/* _________________________________________________________
*/

  int
  PantallaTexto::LeerMinY() {
      return minY;
  }

Como ya vimos en el tema que trataba la pantalla en modo texto desde Symantec C++, las rutinas de escritura, de posicionamiento y de borrado de pantalla son distintas. Estos cambios afectan a los métodos "Borra" y "Escribe". También hay un ligero cambio en el método constructor, porque en Symantec C++ hay que inicializar la pantalla con "disp_open()" antes de escribir en ella.

Estos son los únicos cambios que es necesario hacer al módulo. También podíamos haber creado un método destructor, que llamase a "disp_close()" cuando se abandona el programa. No lo he incluido para no complicar el fuente (habría que declararlo también en "pant02.h"), pero sería simplemente así:

  PantallaTexto::PantallaTexto() {
      disp_close();
  }

Por cierto... ¡este ejemplo no funciona correctamente en mi versión de Symantec C++! En efecto: dibuja el texto en la pantalla de fondo, pero no las ventanas ni el texto que contienen éstas. Es algo que podría alarmar, pero en mi caso no me preocupo, por tres motivos:

  • Mi versión de Symantec C++ (6.0) tiene algunos bugs (errores de programación), y a veces no se comporta como debería.
  • En los otros tres compiladores con que he probado estos ejemplos no he tenido ningún problema, lo que me da más motivo todavía para pensar que la culpa no es mía, sino de SC++.
  • En cualquier caso, el propósito de este fuente no era funcionar (es un decir), sino mostrar la forma en que la Programación Orientada a Objetos permite hacer programas modulares, independientes del hardware y del sistema operativo, y cómo basta con modificar un módulo o unos pocos para que el programa funcione en un entorno totalmente distinto.


Curso de C. OOP y C++. Introducción a C++ (6).

Hay muuuchos temas de Programación Orientada a Objetos que no hemos tratado, por ejemplo: sobrecarga de operadores (p.ej., redefinir el operador + para añadir texto a una ventana), herencia múltiple (que una clase herede métodos y atributos de más de un "padre"), etc. Algunos de estos temas “más avanzados” los veremos más adelante. Comentemos ahora otros aspectos más generales...

Primero repasemos alguna de las diferencias que ya hemos comentado.

  • Orientado a objetos, con todos los cambios que ello implica, y en los que no voy a insistir.
  • Comentarios hasta fin de línea, que se marcan con una doble barra //.
Y mencionemos alguna más:
  • Declaraciones después de instrucciones. En C hay que declarar las variables locales al comienzo de una función; en C++ se pueden declarar después de sentencias ejecutables, lo que permite incluso declararlas justo en el momento de usarlas, con órdenes como

    for (int i=1; i<=10; i++)
  • Nuevas rutinas de acceso a pantalla y teclado: se lee del teclado con "cin >> variable" y se manda a pantalla con "cout >> texto", como se muestra en el siguiente ejemplo:

    • #include <iostream.h>

    char nombre[40];

    main() {
cout << "Teclea tu nombre\n";
cin >> nombre;
cout << "Así que te llamas " << nombre
<< ", verdad?\n";
return 0;
}

  • Los enum y struct se tratan como tipos. Así, se pueden definir variables de tipo “enum” y “struct” sin necesidad de repetir las palabras "enum" y "struct". Como seguro que suena a chino, mejor pongamos un ejemplo:
enum Color { rojo, verde, azul ;
enum Color unColor;   // esto es lo que habría que hacer en C
Color unColor;        // esto es válido en C++

struct Complejo {float mod, arg ;
struct Complejo c1;   // esto es lo que habría que hacer en C
Complejo c1;          // esto es válido en C++

  • Se pueden dar valores por defecto a los parámetros de una función:

    • void Escribe(char *texto, int x=30, int y=10);

    Escribe(saludo, 20, 5);  // Válido, escribe en (20,5)
Escribe(saludo, 20);     // Válido,
escribe en (20,10)
Escribe(saludo);
// Válido, escribe en (30,10)
Escribe();
// No válido: no hay valor para "texto"

La restricción es que los valores por defecto deben corresponder a los parámetros que aparecen más a la derecha.

  • Dispone de los operadores new y delete para reservar memoria dinámica, similares a malloc y free.
Por ejemplo, para crear dinámicamente una cadena de texto de 80 letras se haría

texto = new char[80];  // en vez de = (char *) malloc (80*sizeof(char))

y para eliminarla

delete texto;
  • Operador :: para indicar el ámbito. Lo hemos utilizado ya para el desarrollo de los métodos de una clase, pero también tiene otros usos, como puede ser el de acceder a una variable global cuando existe una variable local con el mismo nombre:
int i = 5;

void funcion () {
  int i = 4;
  cout << "La variable local vale " << i;
  cout << "La variable global vale " << ::i;
}

  • Se pueden declarar referencias:

    • int i = 0;
    int &i2 = i;      // es un alias para i
    i2 = 4;           // tiene el mismo efecto que i = 4

También se pueden pasar parámetros por referencia, que es más legible que pasar parámetros que sean punteros. Por ejemplo, para crear una función que calcule el triple de un número, lo podemos hacer de 3 formas:
Primera forma, pasando valores:
    int tripleA( int n )   // Desarrollo de la función
    { return n * 3; }

    int x, i = 5;
x = tripleA (i);
// Ahora x vale 15

Segunda forma, como puntero, pasando direcciones:

    void tripleB( int *n )   // Desarrollo de la función
    { *n = (*n) * 3; }

    int x, i = 5;
x = i;
// Ahora x vale 5
tripleB (&x);
// Ahora x vale 15

Tercera forma, empleando referencias, más legible:

    int tripleC( int& n )   // Desarrollo de la función
    { n = n * 3; }

    int x, i = 5;
x = i;
// Ahora x vale 5
tripleC (x);
// Ahora x vale 15

  • Y todavía más, pero creo que como introducción ya basta...


Curso de C. OOP y C++. Introducción a C++ (7).

Sobrecarga de operadores. Funciones amigas.

Una de las posibilidades de C++ que todavía no hemos visto y que ayuda mucho a mejorar la legibilidad de los programas es la “sobrecarga de operadores”.

Hemos visto como sobrecargar (redefinir) funciones, de modo que una misma función se puede comportar de formas distintas, según los parámetros que le pasemos. Ahora vamos a intentar hacer lo mismo: “sobrecargar” (redefinir) operadores como +, - *, <<, etc., para que nos permitan sumar dos objetos que nosotros hayamos creado. Veremos también una introducción a las funciones amigas y a las clases amigas.

Vamos allá...

Usaremos uno de los ejemplos típicos: vamos a crear una clase que será un Vector en el plano, definido por las dos coordenadas X e Y de su extremo (el origen del vector lo consideraremos situado en el origen de coordenadas). Nuestro objetivo será sumar vectores usando el operador + y mostrar vectores en pantalla usando el operador <<.

Comenzaremos haciendolo “a la manera tradicional”: si no sabemos redefinir cosas como + y <<, lo que haríamos sería definir un método “Sumar” y otro método “Mostrar”, así:

 //
 //  sobrec01.cpp
 //
 //  Introduccion a C++
 //  Sobrecarga de operadores: aproximación
 //  Probado con TC++ (MsDos)
 //
 //  Curso de C
 //  Jose Ignacio Cabanes
 //
 /////////////////////////////////////////////

 #include <iostream.h>

 // ---------------------------------------------------------
// CLASE  : NVector
// Vector, determinado por las dos coordenadas de su
//   extremo (se supone su origen en el origen
de
//   coordenadas)
// ---------------------------------------------------------

 class NVector {

     int coordX, coordY;     // Coordenadas del extremo del vector

 public:

     int LeerX();
// Leer valores y fijarlos
int LeerY();
void FijarX( int valor );
void FijarY( int valor );

     void Mostrar();            // Mostrar en pantalla: (x,y)
     void Sumar( NVector v2 );  // Sumar otro vector a éste
 };

 // ==========================================================================

 //
 //  A continuación viene el desarrollo de las funciones (métodos)
 //
 // ========

 /* ----------------- NVector -------------------------------
*/
/* Leer los valores de las coordenadas
*/
/* _________________________________________________________
*/

 int
 NVector::LeerX() {
     return coordX;
 }

 int
 NVector::LeerY() {
     return coordY;
 }

 /* ----------------- NVector -------------------------------
*/
/* Fijar los valores de las coordenadas
*/
/* _________________________________________________________
*/

 void
 NVector::FijarX( int valor ) {
     coordX = valor;
 }

 void
 NVector::FijarY( int valor ) {
     coordY = valor;
 }

 /* ----------------- NVector -------------------------------
*/
/* Otras: mostrar y sumar
*/
/* _________________________________________________________
*/

 void
 NVector::Mostrar() {
     cout << "(" << coordX << "," << coordY << ")\n";
 }

 void
 NVector::Sumar( NVector v2 ) {
     coordX += v2.coordX;
     coordY += v2.coordY;
 }

 // ==========================================================================
 //
 //  Finalmente: un programa sencillo de ejemplo
 //
 // ========

 NVector vec1, vec2;        // Definimos dos objetos de la clase "NVector"

 main() {
     vec1.FijarX(3); vec1.FijarY(2);
     vec1.Mostrar();

     vec2.FijarX(5); vec2.FijarY(4);
     vec2.Mostrar();

     vec1.Sumar(vec2);
     vec1.Mostrar();
     return 0;
 }

En principio, no debería haber ninguna dificultad con este programa. He conservado todo en un único fuente, aunque no sea lo más correcto, buscando una mayor legibilidad, ya que vamos a ver conceptos nuevos.

Eso sí, por lo menos he intentado favorecer un poco la encapsulación mediante las funciones miembro LeerX, LeerY, FijarX, FijarY, de modo que no sea necesario acceder directamente a las variables miembro, que he definido como privadas.

El resultado de este programa debería ser algo así:

(3,2)
(5,4)
(8,6)

Mejorémoslo. La primera aproximación se va a parecer ya mucho a lo que buscamos: vamos a redefinir el operador +, de modo que podamos escribir vec1 = vec1 + vec2. La idea será simplemente crear una función llamada “operator+”, en vez de la función “Sumar” que teníamos, así:

 //
 //  sobrec01.cpp
 //
 //  Introduccion a C++
 //  Sobrecarga de operadores: ejemplo
 //  Probado con TC++ (MsDos)
 //
 //  Curso de C
 //  Jose Ignacio Cabanes
 //
 /////////////////////////////////////////////

 #include <iostream.h>

 // ---------------------------------------------------------
// CLASE  : NVector
// Vector, determinado por las dos coordenadas de su
//   extremo (se supone su origen en el origen
de
//   coordenadas)
// ---------------------------------------------------------

 class NVector {

     int coordX, coordY;     // Coordenadas del extremo del vector

 public:

     NVector() { coordX = 0; coordY = 0; }   // Constructor

     int LeerX();
// Leer valores y fijarlos
int LeerY();
void FijarX( int valor );
void FijarY( int valor );

     void Mostrar();
// Mostrar en pantalla: (x,y)
NVector operator+( NVector v2 );
// Sumar otro vector a ‚ste
};

 // ==========================================================================

 //
 //  A continuación viene el desarrollo de las funciones (métodos)
 //
 // ========

 /* ----------------- NVector -------------------------------
*/
/* Leer los valores de las coordenadas
*/
/* _________________________________________________________
*/

 int
 NVector::LeerX() {
     return coordX;
 }

 int
 NVector::LeerY() {
     return coordY;
 }

 /* ----------------- NVector -------------------------------
*/
/* Fijar los valores de las coordenadas
*/
/* _________________________________________________________
*/

 void
 NVector::FijarX( int valor ) {
     coordX = valor;
 }

 void
 NVector::FijarY( int valor ) {
     coordY = valor;
 }

 /* ----------------- NVector -------------------------------
*/
/* Otras: mostrar y sumar
*/
/* _________________________________________________________
*/

 void
 NVector::Mostrar() {
     cout << "(" << coordX << "," << coordY << ")\n";
 }

 NVector
 NVector::operator +( NVector v2 ) {
     NVector temporal;

     temporal.coordX = coordX + v2.coordX;
     temporal.coordY = coordY + v2.coordY;

     return temporal;
 }

 // ==========================================================================
 //
 //  Finalmente: un programa sencillo de ejemplo
 //
 // ========

 NVector vec1, vec2;   // Definimos dos objetos de la clase "NVector"

 main() {
     vec1.FijarX(3); vec1.FijarY(2);
     vec1.Mostrar();

     vec2.FijarX(5); vec2.FijarY(4);
     vec2.Mostrar();

     vec1 = vec1 + vec2;
     vec1.Mostrar();
     return 0;
 }

No parece difícil, ¿verdad? Lo único que hemos hecho es crear una función llamada “operator+”, que devuelve un NVector (bueno, y también hemos añadido un constructor, que inicializa a 0 las dos componentes del vector).

Eso sí, esto no funcionará si en vez de escribir “vec1 = vec1 + vec2” usamos la notación abreviada “vec1 += vec2”. La forma de solucionarlo sería redefiniendo también el operador += de la siguiente forma

     NVector& operator+=( NVector v2 ) {  // Válido para vec1 = vec1 + vec2
          return *this = *this + v2;
     }

En la definición de este operador me he apoyado en la del operador +. que ya estaba definido. He empleado el puntero this, que es un puntero especial, que hace referencia al objeto que ha generado la llamada. Este puntero se le pasa implícitamente a todas las funciones miembro.

Para sobrecargar el operador <<, antes vamos a ver una introducción a las funciones y clases amigas...

Una función amiga es aquella que tiene acceso a la parte privada de una clase. Es algo que rompe la encapsulación, por lo que no se debería abusar de ello.

Como ejemplo, vamos a calcular el módulo (la longitud) de un vector. Podemos hacerlo de dos formas. La más correcta (y que no usaremos) sería, dado que el módulo es una propiedad del vector, definirlo como una función miembro del vector, de modo que escribiríamos cosas como “cout << vec1.modulo()”. Doy por sentado que eso no supone ninguna dificultad a estas alturas, y vamos a hacerlo de la forma “menos correcta”, pero que en este caso será la más didáctica: crearemos una función “módulo” que declararemos como “amiga” para que tenga acceso a la parte privada de nuestro NVector:

 //
 //  amiga01.cpp
 //
 //  Introduccion a C++
 //  Funciones amigas
 //  Probado con TC++ (MsDos)
 //
 //  Curso de C
 //  Jose Ignacio Cabanes
 //
 /////////////////////////////////////////////

 #include <iostream.h>
 #include <math.h>      // Para la raíz cuadrada, sqrt

 // ---------------------------------------------------------
// CLASE  : NVector
// Vector, determinado por las dos coordenadas de su
//   extremo (se supone su origen en el origen
de
//   coordenadas)
// ---------------------------------------------------------

 class NVector {

     int coordX, coordY;     // Coordenadas del extremo del vector
     friend float modulo( NVector v )    // Modulo del vector
       { return sqrt (v.coordX*v.coordX + v.coordY*v.coordY); }

 public:

     int LeerX();
// Leer valores y fijarlos
int LeerY();
void FijarX( int valor );
void FijarY( int valor );

     void Mostrar();
// Mostrar en pantalla: (x,y)
};

 // ==========================================================================

 //
 //  A continuación viene el desarrollo de las funciones (métodos)
 //
 // ========

 /* ----------------- NVector -------------------------------
*/
/* Leer los valores de las coordenadas
*/
/* _________________________________________________________
*/

 int
 NVector::LeerX() {
     return coordX;
 }

 int
 NVector::LeerY() {
     return coordY;
 }

 /* ----------------- NVector -------------------------------
*/
/* Fijar los valores de las coordenadas
*/
/* _________________________________________________________
*/

 void
 NVector::FijarX( int valor ) {
     coordX = valor;
 }

 void
 NVector::FijarY( int valor ) {
     coordY = valor;
 }

 /* ----------------- NVector -------------------------------
*/
/* Otras: mostrar
*/
/* _________________________________________________________
*/

 void
 NVector::Mostrar() {
     cout << "(" << coordX << "," << coordY << ")\n";
 }

 // ==========================================================================
 //
 //  Finalmente: un programa sencillo de ejemplo
 //
 // ========

 NVector vec1;        // Definimos un objeto de la clase "NVector"

 main() {
     vec1.FijarX(3); vec1.FijarY(2);
     vec1.Mostrar();

     cout << "Modulo: " << modulo(vec1) << "\n";

     return 0;
 }

Creo que no es difícil: a la clase NVector le decimos que tendrá una función amiga, llamada módulo, la cual, de paso, hemos definido allí mismo. En el cuerpo del programa mostramos el valor del módulo de “vec1”. La salida de este programa será algo así:

(3,2)
Modulo: 3.06555

Y también podemos decir que una clase será amiga de otra, y por tanto tendrá acceso a toda su parte privada, como ésta que sea capaz de deformar un NVector, haciéndolo más grande o más pequeño, al multiplicar sus componentes por un cierto número:

 //
 //  amiga02.cpp
 //
 //  Introduccion a C++
 //  Clases amigas
 //  Probado con TC++ (MsDos)
 //
 //  Curso de C
 //  Jose Ignacio Cabanes
 //
 /////////////////////////////////////////////

 #include <iostream.h>
 #include <math.h>      // Para la raíz cuadrada, sqrt

 // ---------------------------------------------------------
// CLASE  : NVector
// Vector, determinado por las dos coordenadas de su
//   extremo (se supone su origen en el origen
de
//   coordenadas)
// ---------------------------------------------------------

 class EstiraVector;   // La declaramos, para que el compilador
                       // sepa que existe y que la definiremos más
                       // tarde

 class NVector {

     int coordX, coordY;        // Coordenadas del extremo del vector
     friend class EstiraVector; // Clase amiga

 public:

     int LeerX();
// Leer valores y fijarlos
int LeerY();
void FijarX( int valor );
void FijarY( int valor );

     void Mostrar();
// Mostrar en pantalla: (x,y)
};

 class EstiraVector {

 public:

     void Modifica( NVector &v, int factor );  // Multiplica las componentes
 };

 // ==========================================================================

 //
 //  A continuación viene el desarrollo de las funciones (métodos)
 //
 // ========

 /* ----------------- NVector -------------------------------
*/
/* Leer los valores de las coordenadas
*/
/* _________________________________________________________
*/

 int
 NVector::LeerX() {
     return coordX;
 }

 int
 NVector::LeerY() {
     return coordY;
 }

 /* ----------------- NVector -------------------------------
*/
/* Fijar los valores de las coordenadas
*/
/* _________________________________________________________
*/

 void
 NVector::FijarX( int valor ) {
     coordX = valor;
 }

 void
 NVector::FijarY( int valor ) {
     coordY = valor;
 }

 /* ----------------- NVector -------------------------------
*/
/* Otras: mostrar
*/
/* _________________________________________________________
*/

 void
 NVector::Mostrar() {
     cout << "(" << coordX << "," << coordY << ")\n";
 }

 /* ----------------- EstiraVector --------------------------
*/
/* Modifica: multiplica las componentes de un vector por
*/
/*   un cierto factor
*/
/* _________________________________________________________
*/

 void
 EstiraVector::Modifica( NVector &v, int factor ) {
     v.coordX *= factor;
     v.coordY *= factor;
 }

 // ==========================================================================
 //
 //  Finalmente: un programa sencillo de ejemplo
 //
 // ========

 NVector vec1;        // Definimos un objeto de la clase "NVector"
 EstiraVector e;      // Y un "estirador de vectores"

 main() {
     vec1.FijarX(3); vec1.FijarY(2);
     vec1.Mostrar();

     e.Modifica(vec1, 3);
     vec1.Mostrar();

     return 0;
 }

Tampoco debería suponer ningún problema: decimos que la clase “EstiraVector” va a ser amiga de “NVector”, de modo que tendrá acceso a su parte privada. Eso sí, como no quiero limitarme a leer los datos de la parte privada, sino que deseo modificarlos, el vector a modificar lo paso como referencia (en caso contrario, los cambios no se guardarían al salir de “Modifica”).

Por otra parte, la obligación de declarar la clase “EstiraVector” para que el compilador no nos regañe cuande vea que la mencionamos dentro de “NVector” también es algo bastante razonable, que no debería sorprendernos. Más adelante es cuando ya damos los datos concretos sobre cómo debe trabajar esa clase.

Así que sigamos: vamos ahora a ver cómo redefinir el operador <<...

Redefinir el operador << ahora ya no es tan difícil. El único problema es que debemos devolver un “flujo” del tipo “ostream”, y deberemos autorizar a la función “operator<<“ a acceder a la parte privada de NVector, declarandolo como amiga, así:

     friend ostream& operator<< ( ostream& salida, NVector v )

En el desarrollo de esta función consistirá en enviar la información que nos interesa al flujo que hemos llamado “salida”:

        { return salida << '(' << v.coordX << ',' << v.coordY << ')'; }

El fuente completo sería este:

 //
 //  sobrec01.cpp
 //
 //  Introduccion a C++
 //  Sobrecarga de operadores: ejemplo
 //  Probado con TC++ (MsDos)
 //
 //  Curso de C
 //  Jose Ignacio Cabanes
 //
 /////////////////////////////////////////////

 #include <iostream.h>

 // ---------------------------------------------------------
// CLASE  : NVector
// Vector, determinado por las dos coordenadas de su
//   extremo (se supone su origen en el origen
de
//   coordenadas)
// ---------------------------------------------------------

 class NVector {

     int coordX, coordY;     // Coordenadas del extremo del vector

 public:

     NVector() { coordX = 0; coordY = 0; }   // Constructor

     int LeerX();
// Leer valores y fijarlos
int LeerY();
void FijarX( int valor );
void FijarY( int valor );

                             // Mostrar en pantalla: (x,y)
     friend ostream& operator<< ( ostream& salida, NVector v )
        { return salida << '(' << v.coordX << ',' << v.coordY << ')'; }

     NVector operator+( NVector v2 );  // Sumar otro vector a éste

 };

 // ==========================================================================

 //
 //  A continuación viene el desarrollo de las funciones (métodos)
 //
 // ========

 /* ----------------- NVector -------------------------------
*/
/* Leer los valores de las coordenadas
*/
/* _________________________________________________________
*/

 int
 NVector::LeerX() {
     return coordX;
 }

 int
 NVector::LeerY() {
     return coordY;
 }

 /* ----------------- NVector -------------------------------
*/
/* Fijar los valores de las coordenadas
*/
/* _________________________________________________________
*/

 void
 NVector::FijarX( int valor ) {
     coordX = valor;
 }

 void
 NVector::FijarY( int valor ) {
     coordY = valor;
 }

 /* ----------------- NVector -------------------------------
*/
/* Otras: sumar
*/
/* _________________________________________________________
*/

 NVector
 NVector::operator +( NVector v2 ) {
     NVector temporal;

     temporal.coordX = coordX + v2.coordX;
     temporal.coordY = coordY + v2.coordY;

     return temporal;
 }

 // ==========================================================================
 //
 //  Finalmente: un programa sencillo de ejemplo
 //
 // ========

 NVector vec1, vec2;   // Definimos dos objetos de la clase "NVector"

 main() {
     vec1.FijarX(3); vec1.FijarY(2);
     cout << vec1 << '\n';

     vec2.FijarX(5); vec2.FijarY(4);
     cout << vec2 << '\n';

     vec1 = vec1 + vec2;
     cout << vec1 << '\n';
     return 0;
 }

Podríamos (¿y deberíamos?) haber distinguido entre operadores unarios (o monarios, que sólo permiten un operando, como el - de cambio de signo) y binarios (con dos operadores, como el * de la multiplicación), pero ya hemos visto un ejemplo de cómo redefinir ambos, casi sin darnos cuenta...

Así que ahora sólo queda experimentar. Por ejemplo, esta clase NVector se podría completar, de modo que se pudiese hallar el producto escalar de dos vectores sobrecargando el operador * (y *=), se debería cambiar las variables miembro de números enteros a números reales, se podría multiplicar un vector por un escalar (sobrecargando nuevamente * y *=), se podría calcular su módulo, con una función miembro llamada así o con un operador que parezca apropiado, etc...


Curso de C. OOP y C++. Introducción a C++ (8).

Más sobre entrada y salida en C++.

Hemos visto la base de la escritura en pantalla y de la lectura desde teclado empleando C++: cout se usa para escribir en pantalla y cin para leer desde teclado. Los distintos textos que debe escribir “cout” se preceden con “<<“, y las entradas de “cin” se preceden con “>>“.

Pero en ese manejo básico han quedado muchas "lagunas", cosas que sabíamos hacer en C pero que no hemos visto cómo hacer en C++. Por ejemplo, no sabemos cómo mostrar un número con una cierta cantidad prefijada de cifras decimales, ni cómo leer una cadena de texto que contenga espacios en blanco, ni cómo acceder a ficheros.

Vayamos por partes:

Hemos usado dos "flujos" (streams) de datos: un flujo de entrada de datos (cin, que corresponderá al teclado) y un flujo de salida de datos (cout, la pantalla). Pero también existe otro flujo estándar, que no hemos empleado todavía, pero que es interesante conocer: es "cerr", que corresponde a la salida de los mensajes de error. Esta suele ser la pantalla (de modo que normalmente coincidirá con “cout”), pero en algunos sistemas operativos (como los Unix, entre ellos Linux) se pueden redirigir los mensajes de error a un fichero, de modo que se pueda analizar los errores independientemente de la salida habitual del programa por pantalla.

Por ejemplo, si manejamos bajo Unix una utilidad llamada “prueba”, podemos hacer las siguientes cosas:

  • Si tecleamos “prueba”, la utilidad se pondrá en funcionamiento, y mostrará la información “normal” en pantalla, y los mensajes de error también.
  • Si tecleamos “prueba > salida”, la utilidad se pondrá en funcionamiento, pero la información que normalmente aparecería en pantalla no lo hará, sino que será “redirigida” a un fichero de texto llamado “salida”.
  • Si tecleamos “prueba 2> errores”, la utilidad se pondrá en funcionamiento, y mostrará la información “normal” en pantalla, pero los mensajes de error serán “redirigidos” a un fichero de texto llamado “errores”, que nosotros podríamos analizar después.

Este mismo funcionamiento estándar se puede conseguir desde los programas que nosotros creemos si enviamos los mensajes de error al flujo llamado “cerr” (eso sí, insisto en que esta característica debe permitirla el sistema operativo: lo permiten todos los Unix -Linux, FreeBSD, AIX, SCO, etc- pero no lo permite MsDos ni Windows).

Sigamos. La salida en pantalla se puede personalizar mediante una serie de indicadores ("flags", en inglés) o modificadores. Por ejemplo, uno de estos indicadores nos sirve para decir que queremos que los números enteros se muestren en hexadecimal: si escribimos

cout << 90 << "\n";
cout.setf(ios::hex);
cout << 90 << "\n";

el resultado de estas líneas de programa sería:

90
5A

En este caso hemos utilizado el indicador llamado "hex". Todos estos modificadores deberemos precederlos, como se ve en el ejemplo, por "ios::" (porque pertenecen a una clase llamada "ios", que es de la que derivan las clases istream y ostream que estamos usando "sin darnos cuenta" con cin y cout).

Los indicadores que tenemos disponibles son:

  • dec: salida decimal para enteros (defecto).
  • oct: salida octal para enteros.
  • hex: salida hexadecimal al para enteros.
  • left: la salida se alinea a la izquierda.
  • rigth: la salida se alinea a la derecha.
  • internal: se alinea el signo y los caracteres indicativos de la base por la izquierda y las cifras por la derecha.
  • showpos: se muestra el signo (+) en los valores positivos.
  • scientific: notación científica para coma flotante.
  • fixed: notación normal para coma flotante.
  • skipws: se descartan los blancos iniciales a la entrada.
  • showbase: se muestra la base de los valores numéricos.
  • showpoint: se muestra el punto decimal.
  • uppercase: los caracteres de formato aparecen en mayúsculas.
  • unitbuf: salida sin buffer (se vuelca cada operación).
Cuando queremos fijar uno de ellos, se hace como ya hemos visto:

cout.setf(ios::hex);

Si queremos fijar varios de ellos a la vez, podemos enlazarlos con la barra vertical (|) que representa la operación OR (suma a nivel de bits):

cout.setf(ios::left | ios::oct);

Esto es porque realmente cada uno de los flags corresponde a un bit de un cierto número entero:

enum {
  skipws=0x0001, left=0x0002, rigth=0x0004, internal=0x0008,
  dec=0x0010, oct=0x0020 hex=0x0040, showbase=0x0080,
  showpoint= 0x0100 uppercase=0x0200, showpos=0x0400, scientific=0x800,
  fixed=0x1000, unitbuf=0x2000
};

Nota: el valor que devuelve la función “setf” es la configuración que tenían anteriormente estos indicadores (un número de tipo “long”), de modo que podemos guardar dicho valor en una variable auxiliar, para después dejar las opciones de salida en pantalla tal y como estaban:

long configAnterior;

configAnterior = cout.setf(ios::hex); // Pasamos a hexadecimal, guardando config.
cout << 215;                          // Escribimos lo que queramos
...
cout.setf(configAnterior);            // Finalmente, dejamos config. antigua

Para desactivar un modificador concreto, se usa “unsetf” en lugar de “setf”:

cout.unsetf(ios::hex);

También se puede usar la función “flags” para ver la condiguración actual (o para fijarla toda “de golpe”, de modo que resulta más arriesgada que usar “setf”):

configActual = cout.flags();

Algunos de estos indicadores se pueden usar también en forma de manipuladores. Es el caso de hex:

cout << hex << 90 << "\n";

mientras que otros manipuladores disponibles no tienen correspondencia con ningún “flag”. Por ejemplo, existe un manipulador llamado “endl” que da un salto de línea, de modo que la línea anterior también se podría haber escrito así:

cout << hex << 90 << endl;

Los manipuladores más habituales son:

  • dec, hex y oct: fijar la base en que se mostrarán los enteros (decimal, hexadecimal u octal).
  • ws: saltar los espacios en blancos iniciales.
  • endl: saltar de línea (‘\n’) y vacíar el buffer de salida.
  • flush: se vacía el buffer de salida.
También tenemos varias funciones miembro interesantes:
  • “width” permite fijar la anchura mínima para un dato de salida (si el dato ocupa más, se toma mayor anchura automáticamente), y devuelve la anchura que estaba anteriormente prefijada. Esta anchura sólo es válida para el siguiente dato que se imprima.
  • “precision” permite indicar el número de cifras de los datos númericos (por defecto son 6 cifras)y devuelve el valor de precisión anterior.
  • “fill” elige el carácter de relleno para un dato de salida (que normalmente será un espacio en blanco) y devuelve el carácter de relleno anterior.

Un ejemplo de estas 3 funciones miembro sería:

cout << 123.43 << endl;
cout.width(12);
cout.precision(4);
cout.fill('@');
cout << 123.43  << endl;

que mostraría lo siguiente en pantalla

123.43
@@@@@@@123.4


Curso de C. OOP y C++. Introducción a C++ (9).

Ficheros en C++.

Al igual que ocurre con la escritura en pantalla, a la hora de manejar los ficheros desde C++, podemos emplear las funciones que ya conocíamos de C, o bien emplear otras nuevas posibilidades que aporta C++. También al igual que ocurría con la pantalla, el manejo de ficheros se basará en flujos de entrada y salida.

Tenemos las clases fstream (fichero, en general), ifstream (fichero de entrada) y ofstream (fichero de salida), todas ellas definidas en “fstream.h”. Leeremos y escribiremos con << y >>, al igual que para la pantalla. Cerraremos un fichero con “close” (tanto si lo hemos abierto como para leer o para escribir) y comprobaremos si se ha terminado un fichero de entrada con “eof”.

Vamos a ver un primer ejemplo que lo aplique, creando un fichero de texto:

 //
 //  fich01.cpp
 //
 //  Introduccion a C++
 //  Ejemplo de ficheros (escritura)
 //  Probado con BC++ 5.5 FCLT
 //
 //  Curso de C
 //  Jose Ignacio Cabanes
 //
 /////////////////////////////////////////////

 #include <fstream.h>

 main()
 {
   ofstream fichero("ejemplo.txt");
   fichero << "Hola" << endl;
   fichero << "Adios" << endl;
   fichero.close();
 }

Debería ser muy fácil de seguir:

  • Incluimos el fichero de cabecera “fstream.h”.
  • Definimos un fichero de salida, que tendrá por nombre físico “ejemplo.txt”
  • Escribimos dos líneas de texto en el fichero.
  • Cerramos en el fichero.
En un caso general, puede ocurrir que no sepamos el nombre físico del fichero en el momento de definir la variable “fichero”, sino más tarde (por ejemplo, porque el usuario sea el que vaya a teclear el nombre del fichero con el que trabajar, o porque vaya a escoger dicho nombre en una ventana de diálogo). En ese caso, podemos usar la función miembro “open”. Como ejemplo, vamos a leer el fichero que acabamos de crear:

 //
 //  fich02.cpp
 //
 //  Introduccion a C++
 //  Ejemplo de ficheros (lectura)
 //  Probado con BC++ 5.5 FCLT
 //
 //  Curso de C
 //  Jose Ignacio Cabanes
 //
 /////////////////////////////////////////////

 #include <fstream.h>
 #include <iostream.h>

 main()
 {
   fstream fichero;
   char texto[200];

                               // Abro para lectura
   fichero.open("ejemplo.txt", ios::in);

   fichero >> texto;
// Leo una primera linea
while (!fichero.eof())
// Mientras se haya podido leer algo
{
cout << texto << endl;
// Muestro lo que lei
fichero >> texto;
// Y vuelvo a intentar leer
}

   fichero.close();
// Finalmente, cierro
}

La estructura es ligeramente distinta, pero aun así, debería resultar fácil de seguir. Esta vez, el fichero lo hemos declarado como “genérico”, sin especificar si va a ser para lectura o escritura, de modo que este dato lo indicamos cuando realmente abrimos el fichero. Los modos de apertura que tenemos disponibles son:

  • ios::in abre el fichero para lectura
  • ios::out abre el fichero para escritura
  • ios::append abre el fichero para añadir datos (al final, después de los que ya contenga)
Si hubiéramos declarado el fichero como “ifstream”, se daría por sentado que lo abrimos para leer, y no sería necesario indicarlo:

   ifstream fichero;

                               // Abro para lectura
   fichero.open("ejemplo.txt");

No hemos comprobado si el fichero realmente se ha podido abrir. Para conseguirlo, añadiríamos después de “open” algo parecido a esto, similar a lo que hacíamos en C estándar:

 if (!fichero)
{
cerr << "No se ha podido abrir el fichero."
<< endl;
exit(1);
}

Estas son las ideas básicas. Pero hay más posibilidades, que voy a comentar con menos detalle, pero prefiero que se sepa que existen, porque pueden ser útiles en muchos casos. Por ejemplo, podemos leer un bloque de datos de una determinada longitud, lo que será útil cuando manejemos ficheros binarios, o escribir una serie de bytes, o leer un dato de un flujo pero sin avanzar de posición.

Estas son algunas funciones miembro de iostream que nos servirán para cosas como esas:

  • put(char c) escribe un carácter en un flujo de salida
  • write(const char* s, int n) escribe n bytes de la cadena s en un flujo de salida (normalmente se usará para salida binaria).
  • get(char& c) lee un carácter de un flujo de entrada.
  • get(char* s, int n, char c=’\n’) lee como máximo n caracteres del flujo de entrada (incluyendo el ‘\0’) y los introduce en la cadena s, o hasta que encuentre el carácter de terminación (por defecto ‘\n’, salto de línea), o el fin de fichero. No retira el carácter de terminación del flujo de entrada.
  • getline(char* s, int n, char c=’\n’) lee como máximo n-1 caracteres del flujo de entrada, o hasta que encuentre el carácter de terminación (por defecto un final de línea) o hasta el fin de fichero. Retira el carácter de terminación del flujo de entrada, pero no lo almacena en la cadena s.
  • read(char* s, int n) lee n bytes del flujo de entrada y los deposita en la cadena s (normalmente se usará para entrada binaria).
  • ignore(int n=1, int delim=EOF) ignora o descarta los n caracteres siguientes de un flujo de entrada (o un solo carácter, si no se indica el valor de n), o hasta que encuentra un cierto carácter de terminación (por defecto el fin de fichero EOF).
  • peek() lee un carácter del flujo de entrada pero sin retirarlo de dicho flujo.
  • putback(char c) devuelve el carácter c al flujo de entrada (de modo que sería lo primero que se leería en la próxima operación de entrada).

Por otra parte, en la clase “fstream” tenemos otras funciones miembro que nos ayudarán a comprobar errores en la lectura o escritura:

  • good () devuelve un valor distinto de cero si no ha habido ningún error.
  • eof() devuelve un valor distinto de cero si se ha llegado al fin del fichero, como ya hemos visto.
  • bad() devuelve un valor distinto de cero si ha habido un error grave de entrada/salida grave. No se puede continuar en esas condiciones.
  • fail() devuelve un valor distinto de cero si ha habido cualquier error de E/S distinto de EOF. Después podemos llamar a bad() para comprobar si el error es grave o si se puede intentar proseguir la lectura. Si bad() devuelve 0, el error no es grave y la lectura puede proseguir después de llamar a la función clear().
  • clear() resetea la situación de error (siempre que no sea grave), para poder seguir leyendo.
Además, también podemos comprobar si ha habido algún error en la forma que hemos empleado en el ejemplo anterior: cada función (open, read, etc) devolverá un valor distinto de cero cuando exista algún error, por lo que es habitual emplear construcciones como

 if (!fichero)
 {
    // No se ha podido abrir el fichero
 }

o como

 if
 (!fichero.get(ch)) {
    // No se ha podido leer el siguiente dato.
 }


Curso de C. Los "strings" en C++.

Una de las últimas características que se ha añadido al estándar de C++ es la existencia de “strings” (cadenas de texto) como parte del lenguaje, con un manejo tan sencillo como lo es en otros lenguajes como Pascal o Basic.

Es una “adquisición” tan reciente que pocos de los compiladores que he probado lo permiten: en estas fechas (agosto de 2000), sólo DJGPP 2.03 y Borland C++ 5.5 bajo Dos, así como las versiones recientes de GCC bajo Linux. Eso sí, es posible que algún otro incluya alguna clase “string” no totalmente estándar, como las “AnsiString” de Borland C++ Builder.

Veamos su manejo primero con un ejemplo sencillo que nos introduzca los conceptos básicos:

  //
  //  string1.cpp
  //
  //  Introducción a C++
  //  Cadenas en el nuevo estandar de C++
  //  Probado con:
  //    Borland C++ 5.5 FCLT
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

#include <string>
#include <iostream>
using namespace std;

main()
{
 string mensaje;

 mensaje = "Hola";
 cout << mensaje;
}

  • La primera diferencia (y casi la única de importancia) es la existencia de “named spaces” (espacios con nombre) dentro del nuevo estándar de C++. Se trata de una nueva forma de organizar los fichero de cabecera (“ficheros .h”), con la intención de que no sea un auténtico caos cuando tenemos muchos de estos ficheros. Así, los ficheros de cabecera estándar están en el espacio llamado “std”.
  • Se sigue pudiendo utilizar los fichero de cabecera de la forma clásica: podríamos escribir
    #include <iostream.h>

    aunque la forma recomendada (y que quiere decir lo mismo) es

#include <iostream>
using namespace std;
  • El manejo en sí de las cadenas no reviste mayor dificultad: se declaran como cualquier otra variable en C y C++, y se le asigna valor al igual que se hace con los números enteros o reales (o como las cadenas en otros lenguajes).
Así que veamos otro ejemplo un poco más complejo:

  //
  //  string2.cpp
  //
  //  Introducción a C++
  //  Cadenas en el nuevo estandar de C++
  //  Probado con:
  //    Borland C++ 5.5 FCLT
  //
  //  Curso de C
  //  Jose Ignacio Cabanes
  //
  /////////////////////////////////////////////

#include <string>
#include <iostream>
using namespace std;

main()
{
 string texto1, texto2 = "Hola ", texto3("Que tal");

 texto1 = texto2 + texto3 + " estas? ";
 cout << texto1 << "\n";

 string subcadena (texto1, 2, 6);  // 6 letras de texto1, desde la tercera
 cout << subcadena << "\n";

 string subcadena2;
 subcadena2 = texto1.substr(0, 5); // 7 letras de texto1, desde el comienzo

 texto1.insert(5, "Juan ");  // Inserto un texto en la posicion 6
 cout << texto1 << "\n";

 texto2.replace(1, 2, "ad");  // Cambio 2 letras en la posicion 2
 cout << texto2 << "\n";

 cout << "La longitud de texto1 es " << texto1.size() << "\n";

 cout << "La tercera letra de texto1 es " << texto1[2]
   << " o bien " << texto1.at(2) << "\n";

if (texto2 == "Hada ")
  cout << "Texto 2 es Hada\n";

}

Creo que todo se explica por sí solo. El resultado de este ejemplo es:

Hola Que tal estas?
la Que
Hola Juan Que tal estas?
Hada
La longitud de texto1 es 25
La tercera letra de texto1 es l o bien l
Texto 2 es Hada

Esto es lo básico de las cadenas. Hay más, como un método “find” para ver si pertenece una cierta subcadena, pero eso queda propuesto como “ejercicio de investigación” para quien quiera profundizar más.


Curso de C. Introducción a C++ Builder.

Escoge un tema:

1.- Primer ejemplo: botones y textos; uso básico.
2.- Comprendiendo (y mejorando) lo hecho.
3.- El inspector de objetos.
4.- Cuadros de texto y listas.
5.- Menús.
6.- Cuadros de seleccion.
7.- Más detalles sobre los botones. Otros tipos de botones.
8.- Ventanas estándar de Windows.
9.- Varias ventanas “nuestras”.
10.- Introducción a los gráficos con C++Builder.


Curso de C. C++ Builder. 1: Primer ejemplo: botones y textos; uso básico.

Vamos a ver una introducción a la programación visual bajo Windows en lenguaje C++, empleando el compilador de Borland (ahora Inprise) llamado C++ Builder. En concreto, la versión que yo utilizaré es “C++ Builder Standard 1.0”, que se incluía con el CdRom de portada de la revista PcPlus número 24, de fecha Diciembre de 1998. En el CdRom de portada de la revista PcWorld España de marzo de 1999 se incluía la versión profesional de C++ Builder 1.0, que tiene algunas mejoras sobre la versión estándar: una utilidad de instalación de aplicaciones, los fuentes de la biblioteca de componentes, etc. Si no tiene acceso a ninguna de estas versiones, o si desea obtener versiones más modernas de C++ Builder (eso sí, ya a su precio normal de mercado), lo mejor es ponerse en contacto directamente con Borland (Inprise) o algún distribuidor autorizado.

Seguiré exactamente la misma estructura que la introducción a Delphi que forma parte del curso de Pascal, para facilitar la comparación entre ambas herramientas a quien quiera probar a usar las dos, y de paso para ayudarme a crear esta introducción con más rapidez.

Nota: doy por sentado que quien haya llegado hasta aquí es porque ya ha seguido el resto del curso, o al menos los temas básicos y el de introducción a C++ y a la Programación Orientada a Objetos. Serán necesarios para seguir todo esto con un poco de soltura. También será imprescindible conocer Windows, al menos a nivel de usuario, y entender expresiones como “hacer click”, “doble click”, “pinchar y arrastrar”, “barra de desplazamiento”, etc.
Comencemos por ver la apariencia de la pantalla:

En esta pantalla hay varios elementos que conforman el área de trabajo de C++ Builder:

  • En la parte superior de la pantalla tenemos el menú, al igual que en todos los programas de Windows.
  • Debajo de este menú, a la izquierda tenemos una barra de herramientas, con las opciones más habituales, como las de guardar o recuperar un archivo o todo el proyecto, o poner el programa en funcionamiento.
  • A la derecha aparece otra barra de herramientas, en la que tenemos los distintos “componentes” que podemos incorporar a nuestros programas: botones, etiquetas de texto, listas de valores, barras de desplazamiento, etc.
  • Ocupando la parte más grande de la pantalla tenemos una ventana vacía, que es la ventana inicial de nuestro proyecto de programación (en inglés “Form”, que muchos autores traducen por “formulario”).
  • A su izquierda tenemos el “Inspector de Objetos” (Object Inspector), desde el que podremos cambiar las “propiedades” de los elementos que vayamos colocando en nuestro programa (por ejemplo, el color o el texto de un botón).

Ahora ya vamos a pasar a crear nuestro primer proyecto...

Vamos a empezar por algo muy sencillo: una ventana que sólo tenga una línea de texto y un botón. Al pulsar el botón, cambiará el texto.

Para ello, empezaremos por colocar la línea de texto en una parte de la ventana: pulsamos el botón que representa una etiqueta de texto (en inglés, “Label”), que es el que muestra una letra A (se puede ver a la derecha)

Este botón se quedará “hundido” hasta que le digamos dónde queremos que aparezca el texto, para recordarnos que hemos escogido ese componente. Hacemos click con el ratón en medio de nuestra ventana, y ahí aparecerá un cartel “Label1” (etiqueta 1, en inglés):

Podemos hacer lo mismo para colocar un botón en nuestra ventana: pulsamos el componente que muestra un botón con el texto “OK”, y después pulsamos en la posición de la ventana en que queremos que aparezca.

Ya tenemos los dos componentes que nos interesan. Ahora vamos a cambiar el texto que muestra cada uno de ellos, y luego veremos cómo poder cambiar el texto cuando se pulse el botón.

Para cambiar el texto del botón, debemos comprobar que éste se encuentra seleccionado (se verán unos puntos gruesos alrededor suyo). Debe estarlo, si no hemos tocado nada; si no lo está, pinchamos una vez sobre él con el botón izquierdo (principal) del ratón. Entonces miramos la apariencia del “Inspector de objetos”.

Dentro de las muchas propiedades que aparecen relativas a los Botones (y que iremos viendo), de momento nos interesa una, que posiblemente ya aparece seleccionada: la propiedad “Caption”, que es el texto que aparece escrito en el botón. Ahora mismo, esa propiedad debe tener el valor “Button1”; pues bien, lo borramos y escribimos “Saludar”. Ahora nuestro botón muestra el texto “Saludar”.

Podemos hacer lo mismo con la etiqueta de texto: hacemos “click” en ella para seleccionarla, y en el “Inspector de objetos” cambiamos su “Caption”, para que sea “hola” en vez de “Label1”.

Ahora la apariencia de nuestra pantalla debería ser ésta:

Para decir a C++ Builder lo que queremos que suceda cuando se pulse el botón, basta hacer doble click sobre dicho botón, y entonces aparece una ventana de edición de texto:

Leamos cuidadosamente lo que ha aparecido: vamos a teclear el interior (“el cuerpo”) de un procedimiento (una función que no devuelve ningún valor -void-). Este procedimiento es realmente un método de un objeto: El objeto se llama TForm1 (nuestra ventana 1), y el procedimiento se llama “Button1Click”, algo así como “cuando se pulse el botón 1”. Además, este procedimientos tiene un parámetro, llamado “Sender” (remitente), que es un puntero a un dato de tipo TObject: se trata del objeto que está llamando a este método, por si nos interesase asegurarnos de quién ha sido (lo usaremos muy pocas veces).

Lo de “fastcall” nos lo saltamos por ahora. Comentaré solamente de pasada que sirve para que podamos enlazar nuestros programas con otros creados en otros lenguajes. Quien ya conozca conceptos como el paso de parametros, la pila, etc., puede mirar la ayuda de C++Builder para saber más sobre este modificador.

Pues lo que podríamos hacer es: si el texto de la etiqueta es “hola”, lo cambiaremos por “adios”, y si es “adios”, lo cambiaremos por “hola”. Esto se podría conseguir así:

if (Label1->Caption == "Hola")
    Label1->Caption = "Adios";
  else
    Label1->Caption = "Hola";

En estas líneas hay cosas nuevas... e interesantes. Normalmente, para comprobar si una cadena de texto es igual a un cierto valor no usaríamos == sino “strcmp”, y para asignar un valor no usaríamos = sino “strcpy”. Pero resulta que C++ Builder incopora un nuevo tipo de cadenas de texto, llamadas “AnsiString”, mucho más intuitivas de manejar (muy similares a los String de Pascal). Pues bien, los “caption” de los componentes visuales no son cadenas de texto al estilo de C, sino “AnsiString”.

La otra cosa que quizá desconcierte es eso de Label1->Caption en vez de Label1.Caption. Label1 es un puntero a tipo TLabel, y para acceder a las propiedades y métodos de un puntero a objeto no se usa “.” sino “->“.

Una última consideración es que debemos recordar que se llama Label1, no label1, y que el texto se llama Caption, no caption: no debemos olvidar que existe diferencia entre mayúsculas y minúsculas.

Tecleamos estas líneas de programa, y ahora nuestra ventana debería tener esta apariencia:

Ahora sólo falta poner el programa a funcionar, para probarlo. Pulsamos el botón de “ejecutar” (en inglés “Run”).

Si nuestros pasos están bien dados, debería aparecer la ventana del programa, y al pulsar el botón “Saludar”, debería cambiar entre el mensaje “Hola” y “Adios”.

Antes de dar por terminado este primer proyecto, vamos a guardarlo.

Primero comenzamos pulsando la X de la esquina superior izquierda de la ventana, para terminar la ejecución de nuestro programa. Después pulsamos el botón de “Guardar el proyecto” (en inglés, “Save Project”)

Nos pedirá un nombre para la “Unit” y luego para el conjunto del proyecto. Esta es la ventana que nos pide el nombre de la unit:

Y esta es la ventana que nos pide el nombre del proyecto y su situación:

Nota: lo correcto sería crear un directorio (carpeta) distinto para cada proyecto que vayamos realizando.

Ahora vamos a profundizar un poco más en los componentes Botón (“TButton”) y Etiqueta de Texto (“TLabel”) y en el uso del inspector de objetos, para mejorar este ejemplo y fundamentar nuestros conocimientos antes de descubrir componentes nuevos.


Curso de C. C++ Builder. 2: Comprendiendo (y mejorando) lo hecho.

Antes de seguir adelante, lo más correcto será primero profundizar un poquito más, y ver lo que hemos hecho (y cómo) con algo más de detalle.

Comencemos por mirar el texto de la “unit”. Para llegar hasta él, podemos hacer varias cosas:

  • Hacer doble click en el botón, de modo que aparezca nuevamente el desarrollo del procedimiento “Button1Click”.
  • Pulsar F12, que cambia de la vista en modo formulario a la vista en modo texto.
  • En el menú “View”, elegir la opción “Toggle Form/Unit” (cambiar entre formulario y Unit)
En el primer caso, en pantalla nos aparece lo que habíamos tecleado

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  if (Label1->Caption == "Hola")
    Label1->Caption = "Adios";
  else
    Label1->Caption = "Hola";
}

Todo esto ya lo conocemos, pero si miramos lo que hay por encima (por ejemplo, subiendo con las flechas del teclado, o con las barras de desplazamiento de la pantalla, o si directamente hubiésemos pulsado F12), veremos que hay muchas más cosas, que C++ Builder ha escrito por nosotros:

//---------------------------------------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
 : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  if (Label1->Caption == "Hola")
    Label1->Caption = "Adios";
  else
    Label1->Caption = "Hola";
}
//---------------------------------------------------------------------------

Vayamos viéndolo línea por línea... o casi:

#include <vcl\vcl.h>

Esta línea se encarga de acceder a los componentes del VCL (Visual Component Library, biblioteca de componentes visuales).

#pragma hdrstop

Las líneas que comienzan con #pragma son instrucciones directas al compilador, que no veremos... pero que tampoco debemos borrar ni modificar.

#include "Unit1.h"

Como ya vimos en la introducción a C++, es frecuente que cada clase se desglose en dos ficheros: un fichero “.h” que contiene la definición de la clase y un fichero “.cpp” que contiene el desarrollo de la misma. En nuestro caso, estamos viendo el fichero “unit1.cpp”, y con esta línea se accede a la definición de la clase, que está contenida en el fichero “unit1.h”, que veremos más adelante.

TForm1 *Form1;

Con esta línea definimos nuestra ventana principal (la única que tenemos por ahora)

__fastcall TForm1::TForm1(TComponent* Owner)
 : TForm(Owner)
{
}

Esta es la definición del método constructor de la clase (que ho hace nada... aún).

//---------------------------------------------------------------------------

Las líneas como ésta no deberían suponer ningún problema: simplemente son comentarios, que el propio C++ Builder inserta en nuestro programa, para mejorar su legibilidad.

Y el resto es el método Button1Click, que ya hemos visto.

En cuanto al fichero “unit1.h”, su contenido es el siguiente:

//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <vcl\Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
 TLabel *Label1;
 TButton *Button1;
 void __fastcall Button1Click(TObject *Sender);
private: // User declarations
public:  // User declarations
 __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Es muy similar a las definiciones de clases que ya hemos visto. Eso sí, con varios “include” para acceder a los componentes que hemos utilizado (el propio C++ Builder se ha encargado de colocar ahí esos “include”) y con un tipo de acceso que no conocíamos: __published, que es específico de Borland C++ Builder, y que indica las propiedades que aparecerán en el inspector de objetos. En cualquier caso, tampoco tenemos que preocuparnos de ello, C++ Builder los hará automáticamente.

Ahora que ya hemos entendido un poco más lo que se puede leer de nuestro programa, vamos a pasar a mejorar un poco su apariencia:

  • Para mover la etiqueta de texto a otra posición, basta “pincharla y arrastrarla con el ratón”.
  • Igual podemos hacer para mover el botón hasta la posición que nos interese.
  • Para cambiar su tamaño: al pinchar una vez, aparecen “puntos gordos” rodeando el botón. Al acercar el ratón a estos “puntos gordos”, cambia de forma y pasa a mostrar una flecha de dos puntas. Pinchando con esta flecha de dos puntos y arrastrando con el ratón podemos cambiar el tamaño del botón, como es habitual en Windows (se cambia de igual modo el tamaño de las ventanas, por ejemplo).
  • Y de la misma manera podemos cambiar el tamaño de la ventana o formulario, acercándonos a sus esquinas,
  • Ya que estamos, podemos aprovechar para cambiar el título de la ventana. Pinchamos en el fondo de la misma (en cualquier parte menos la etiqueta de texto o el botón), y vamos al “Inspector de objetos”, desde donde cambiamos la propiedad “Caption” a otra más apropiada que el “Form1” que actualmente tiene.

Con estos cambios, nuestra ventana se vería así:


Curso de C. C++ Builder. 3: El inspector de objetos.

Si queremos más precisión a la hora de cambiar posiciones y tamaños, o si queremos acceder a otras características de los botones y de las etiquetas de texto, deberemos emplear el Inspector de objetos.

Por ejemplo, al mover el botón, veremos que no lo hace suavemente, sino “a saltos”; igual ocurre si cambiamos su tamaño.

Vamos a mejorarlo: Si seleccionamos la etiqueta de texto que habíamos añadido a nuestra ventana, y miramos lo que aparece en el inspector de objetos, veremos lo siguiente:

(Nota: he ampliado la ventana al máximo, para que se vieran todas las opciones disponibles; lo habitual será que todas ellas no quepan en pantalla a la vez, y habrá que usar la correspondiente barra de desplazamiento).

Aquí vemos las distintas propiedades que puede tener una etiqueta de texto (el componente que C++ Builder llama TLabel). Vamos a irlas comentando, aunque no en orden alfabético, sino en orden de complejidad, y saltando algunas que utilizaremos más adelante:

  • Left es la posición horizontal en que se encuentra la etiqueta de texto, comenzando desde la parte izquierda del elemento que la alberga (en nuestro caso, a 72 puntos del comienzo de la ventana).
  • Top es la posición vertical, tomada a partir de la parte superior de su “contenedor” (16 puntos en nuestro caso).
  • Autosize es un valor de tipo “bool” (el equivalente al “boolean” de Pascal), que indica si la etiqueta cambiará su tamaño automáticamente cuando nosotros cambiemos el texto (por defecto, su valor es true).
  • Height es la altura de la etiqueta de texto (16).
  • Width es su anchura (30).
  • ParentColor nos permite indicar si queremos que el color de esta etiqueta (el de fondo; el del texto lo veremos un poco más adelante) sea el color del “contenedor”.
  • Color es el color con que se escribirá (nuevamente el de fondo), en caso de que no queramos que sea el mismo que el de su “contenedor”. Tenemos definidas una serie de constantes para facilitarnos el trabajo. Unas indican colores “físicos”, como ClBlue (azul) o ClYellow (amarillo), mientras que otras se apoyan en la paleta que hayamos elegido en Windows, como ClBtnFace (el color de la cara de los botones o de los elementos 3D) y ClActiveBorder (el color del borde de la ventana activa).
  • ParentFont es para decir si queremos que use el mismo tipo de letra que su “contenedor·.
  • Font es el tipo de letra (la “fuente”) con que se escribirá esa etiqueta de texto. Para modificarla, podemos hacer doble click en la palabra “Font”, y aparecerán una serie de “subpropiedades” como Name (el nombre de la fuente), Size (el tamaño en puntos), Color (el color del texto) o Style (la apariencia: cursiva, negrita o subrayado, a la que se accede también con doble click). Otra forma más sencilla puede ser pinchar en la palabra Font del Inspector de Objetos, y después en el botón de “puntos suspensivos” que aparece junto a esta palabra. Veremos las ventana habitual en Windows para elegir tipo de letra y apariencia, en la que podemos elegir todas las características de un paso:

Pero el Inspector de Objetos tiene aún más cosas interesantes: abajo aparecen dos pestañas. ahora mismo estamos en la pestaña de “Properties” (propiedades, o variables miembro del objeto). Vamos a seleccionar el botón y después a pinchar en la ventana de “Events” (sucesos a los que es capaz de responder).

Veremos que un botón será capaz de reaccionar ante los siguientes sucesos:

  • OnClick: se activará cuando se pulse el botón.
  • OnEnter: se activar cuando el botón pase a ser control activo (por ejemplo, cuando se llegue a él pulsando la tecla del tabulador).
  • OnExit: cuando el botón deje de ser el control activo (por ejemplo, si se pulsa el tabulador cuando el botón está activo).
  • OnKeyPress, OnKeyDown, OnKeyUp: cuando se presione una tecla, o justo en el momento de empezar a pulsarla, o justo en el momento de terminar de pulsarla.
  • OnMouseMove, OnMouseDown, OnMouseUp: cuando el ratón se mueva por encima del botón (o del control que sea), o cuando se empieza a pulsar el botón, o cuando se deja de pulsar el botón.
  • OnDragDrop, OnDragOver, OnEndDrag: tienen que ver con la posibilidad de arrastrar y soltar (drag and drop), muy frecuente en Windows.

Al hacer doble click en un cierto componente, pasamos directamente a modificar la rutina correspondiente al suceso más habitual (por ejemplo, el “OnClick” para un botón). Si queremos modificar otro suceso (por ejemplo “OnKeyDown”), deberemos seleccionarlo en el Inspector de Objetos y hacer doble click sobre el nombre de dicho suceso. C++ Builder escribirá la cabecera por nosotros y nos dejará el hueco para completar los detalles:

En este caso, vemos que C++ Builder ya ha preparado la cabecera, y en ella tenemos dos parámetros que no aparecían en “OnClick”:

  • Key, de tipo Word. Es la tecla pulsada, que normalmente deberemos comprobar (tendremos ciertas constantes predefinidas, que podrán ayudarnos)
  • Shift, de tipo TShiftState, que nos permitirá saber si se han pulsado también ciertas teclas especiales (como mayúsculas, Ctrl o Alt).

Pero como ya hemos visto unas cuantas cosas, vamos a intentar aplicarlas para resolver un ejercicio.

Ejercicio propuesto: Mejorar este ejemplo para que la etiqueta de texto esté escrita con tipo de letra Times New Roman, de 18 puntos de tamaño, en color azul. El botón deberá tener un tamaño de 30 puntos de alto y 80 de ancho. Cuando el ratón pase por encima de la etiqueta de texto, el color de fondo de ésta deberá verse amarillo; cuando no pase por encima (esté en el resto del formulario), el fondo deberá ser verde.

(Puedes ver una propuesta de solución)


Curso de C. C++ Builder. Solución al primer ejercicio.

Ejercicio propuesto: Mejorar este ejemplo para que la etiqueta de texto esté escrita con tipo de letra Times New Roman, de 18 puntos de tamaño, en color azul. El botón deberá tener un tamaño de 30 puntos de alto y 80 de ancho. Cuando el ratón pase por encima de la etiqueta de texto, el color de fondo de ésta deberá verse amarillo; cuando no pase por encima (esté en el resto del formulario), el fondo deberá ser verde.

La forma de cambiar el tipo de letra, el tamaño o el color no debería suponer ningún problema: basta con modificar en el inspector de Objetos las propiedades correspondientes.

En cuanto a lo de que cambie el color de fondo del texto, deberemos usar el suceso MouseMove de la etiqueta de texto (para saber cuando el ratón se mueve sobre el texto), pero también el de la ventana o Formulario (para saber cuando no está sobre el texto).

Así, estos dos sucesos quedarían:

void __fastcall TForm1::Label1MouseMove(TObject *Sender, TShiftState
Shift,
int X, int Y)
{
Label1->Color = clYellow;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState
Shift, int X,
int Y)
{
Label1->Color = clGreen;
}

Dos observaciones:

  • Se puede ver que C++ Builder también nos permitiría saber en qué coordenadas X e Y se encuentra el ratón, y si estamos pulsando a la vez alguna tecla especial (TShiftState), pero en nuestro caso no nos hace falta ninguna de estas posibilidades.
  • Los colores están definidos como constantes, cuyo nombre empieza por “cl” y sigue con el nombre del color en inglés (con su primera letra escrita en mayúsculas). Por ejemplo, podemos esperar que el color “rojo” estándar de Windows sea “clRed” (y efectivamente, así es), o que el negro sea clBlack, o el azul sea clBlue. Tenemos también colores basados en la paleta de colores de pantalla que hayamos definido en el Panel de Control de Windows. Por ejemplo, clMenu es el color que se haya escogido desde el Panel de Control para el texto de los menús. La lista completa sería ésta:

Valor              Significado

clBlack            Negro
clMaroon           Granate
clGreen            Verde
clOlive            Verde aceituna
clNavy             Azul marino
clPurple           Púrpura
clTeal             Azul verdoso
clGray             Gris
clSilver           Plata
clRed              Rojo
clLime             Verde lima
clBlue             Azul
clFuchsia          Fucsia
clAqua             Agua
clWhite            Blanco
clBackground       Color de fondo del escritorio de Windows
clActiveCaption    Barra de titulo de la ventana activa
clInactiveCaption  Barra de titulo de la ventana inactiva
clMenu             Color de fondo de los menús
clWindow           Color de fondo de las ventanas
clWindowFrame      Color del borde de las ventanas
clMenuText         Color del texto de los menús
clWindowText       Color del texto en las ventanas.
clCaptionText      Color del texto de la ventana activa.
clActiveBorder     Borde de la ventana activa.
clInactiveBorder   Borde de la ventana activa.
clAppWorkSpace     Color del espacio de trabajo de la ventana.
clHighlight        Color de fondo del texto seleccionado.
clHightlightText   Color del texto seleccionado.
clBtnFace          Color de la “cara” de los botones.
clBtnShadow        Color de la “sombra” de los botones.
clGrayText         Color del texto “engrisecido”
clBtnText          Color del texto de los botones.
clInactiveCaptionText  Texto de la barra de título de ventanas inactivas.
clBtnHighlight     Color de un botón destacado.

Para poner en funcionamiento el programa, podemos pulsar el botón de “Run” (el que muestra un símbolo como el de “Play” en los equipos de música, como ya vimos), o elegir la opción “Run” del menú “Run”, o pulsar la tecla F9. Lo que obtendremos debería ser algo parecido a


Curso de C. C++ Builder. 4: Cuadros de texto y listas.

Ahora vamos a ver otros dos componentes que son muy frecuentes en la programación en Windows: los cuadros para introducir o modificar textos, y las listas de textos.

Lo haremos con un proyecto muy sencillo: en una línea de introducción de texto escribiremos una cierta palabra o frase. Al pulsar un botón, ese texto se añadirá a la lista ya existente.

Vamos allá. Lo haremos con un cierto orden. El primer paso será crear el nuevo proyecto. Primero crearemos un directorio donde lo guardaremos (una carpeta, para quien sólo conozca la terminología empleada por Windows, y no la de Dos, Unix y otros). Este directorio o carpeta lo podemos crear desde el explorador de Windows, desde Mi Pc, desde la línea de comandos de MsDos, etc.

Una vez que tenemos creada la carpeta, entramos a C++ Builder. Aparecerá en pantalla un proyecto vacío.

Lo guardaremos con el nombre que nos interesa: pulsamos el botón de “Guardar proyecto” (Save Project”). Primero nos pedirá un nombre para la unidad principal (nos propone el nombre “Unit1”, que podemos cambiar; en mi caso lo he llamado “PPAL”, como abreviatura de “principal”, por eso de que es la ventana principal -realmente la única- de nuestro proyecto). También nos pedirá un directorio donde grabarlo (nos propone el directorio de trabajo de C++ Builder, que deberíamos cambiar, e indicarle el directorio que nosotros hemos creado). Después nos pregunta un nombre para el proyecto (propone “Project1”, pero podría ser más correcto algo como “Prueba3”).

El segundo paso será añadir (visualmente) los componentes que nos interesan. Comenzamos por la casilla de edición de texto (al acercar el ratón, la línea de ayuda nos mostrará la palabra “Edit”; el nombre de este tipo de componente es TEdit).

Ahora ponemos la casilla de lista (en la ayuda aparece “ListBox” y como nombre de componente es “TListBox”) y cambiamos su tamaño hasta conseguir el que más nos guste. Finalmente incluimos un botón. Obtendríamos algo parecido a esto:

Para que quede un poco mejor, deberíamos cambiar el texto del botón por “Añadir”. Podríamos cambiar el tamaño de la ventana para que no haya mucho espacio perdido. También podríamos añadir una etiqueta de texto junto al “Edit” que diga algo como “Texto a añadir”.

Antes de seguir haciendo más cosas, es conveniente guardar los últimos cambios, pulsando nuevamente el botón “Save Project”. Es una operación que deberíamos repetir con una cierta frecuencia, especialmente antes de hacer cambios importantes que no sepamos con certeza que van a funcionar. Si hemos guardado los cambios recientemente, nos será fácil volver atrás si nos equivocamos y nuestro programa deja de funcionar o no lo hace correctamente.

Sólo nos queda decir lo que queremos que ocurra cuando se pulse el botón. Deseamos que se añada a la lista el texto que aparece en la línea de edición. Hacemos doble click en el botón, para acceder a su suceso “OnClick”.

Las listas (ListBox) tienen una propiedad llamada “Items”, que son los textos que aparecen en la lista. Es del tipo “TStringList”, una lista de cadenas (strings), con método llamado Add, que permite añadir una nueva cadena de texto después de las ya existentes.

Por su parte, las líneas de edición (TEdit) tienen una propiedad llamada Text, que es el texto que hemos escrito en su interior (podemos leerlo, o podemos cambiarlo desde el programa si nos interesa).

En nuestro caso, como nuestra lista se llama ListBox1 y nuestra línea de edición se llama Edit1 (los nombres que C++ Builder poner por defecto), deberíamos escribir lo siguiente:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  ListBox1->Items->Add(Edit1->Text);
}

El resultado de nuestro programa sería algo así:

Pero, ¿de dónde he sacado yo eso del método Add y de la propiedad Text?. Para descubrir las propiedades de un cierto componente y los sucesos a los que responde, nos puede ayudar el Inspector de Objetos. Pero para los métodos eso no sirve. Hay una solución muy sencilla: consultar la ayuda en línea de C++ Builder, que es una referencia muy extensa. Si hacemos click sobre el “ListBox” y pulsamos la tecla F1 (el estándar para solicitar ayuda en Windows), nos aparecerá una descripción de la clase TListBox, con la lista de sus propiedades, métodos y sucesos a los que puede responder. Para cada uno de éstos tendremos su descripción, y en muchas ocasiones incluso ejemplos de su uso.

Tenemos otra variante de los “ListBox”: es el “ComboBox”, que tiene características de los “Edit” y de los “ListBox”: permite teclear un valor en una línea de edición o escogerlo de la lista.

C++ Builder. 4b: Cuadros combinados.

El cuadro combinado (“ComboBox”) se podría considerar una variante de los “ListBox”, que tiene características de los “Edit” y de los “ListBox”: permite teclear un valor en una línea de edición o escogerlo de la lista.

Pero no voy a poner un ejemplo detallado de su uso, sino a dejarlo como ejercicio propuesto (eso sí, incluiré la solución y daré alguna pista): se deberá crear una ventana que contenga un ComboBox con las palabras “Uno”, “Dos” y “Tres”. También existirá una etiqueta de texto que diga “(Sin texto aún)” y un botón con el mensaje “Escoger texto”. Cuando se pulse el botón, en la etiqueta de texto aparecerá la palabra que se haya seleccionado (o escrito) en el ComboBox.

Como pista: ya sabemos como hacer que un programa reaccione a la pulsación de un botón; la única dificultad puede ser para que los textos “Uno”, “Dos” y “Tres” aparezcan en el ComboBox. Hay dos formas: una sencilla y otra más rebuscada, pero más adecuada para aprender cosas nuevas. La sencilla es modificar la propiedad “Items” en el Inspector de Objetos. La “buena” es que se creen automáticamente, que añadan (add) a los elementos (items) del ComboBox en el momento en que se crea (OnCreate) nuestro formulario (Form1).

Suena casi como un trabalenguas, ¿verdad? Pues... ¡a experimentar!

(Puedes ver una propuesta de solución)


Curso de C. C++ Builder. 5: Solución al segundo ejercicio.

Ejercicio propuesto: Se deberá crear una ventana que contenga un ComboBox con las palabras “Uno”, “Dos” y “Tres”. También existirá una etiqueta de texto que diga “(Sin texto aún)” y un botón con el mensaje “Escoger texto”. Cuando se pulse el botón, en la etiqueta de texto aparecerá la palabra que se haya seleccionado (o escrito) en el ComboBox.

La apariencia de la ventana debe ser algo así:

Lo de que cuando se pulse el botón aparezca en la etiqueta de texto el mismo mensaje que muestra el ComboBox es fácil:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
  Label1 -> Caption = ComboBox1 -> Text;
}

Lo de incluir esas líneas desde el Inspector de Objetos funciona, pero la opción elegante (y la más práctica, porque nos permitiría leer esos datos desde un fichero de texto, por ejemplo) es añadirlas cuando el programa ya está en funcionamiento. En nuestro caso, lo haremos en el momento en que se crea nuestro formulario (suceso OnCreate). Añadimos las líneas con “items.Add”, e incluso modificamos el texto si nos interesa:

void __fastcall TForm1::FormCreate(TObject *Sender)
{
  // Añadimos 3 líneas
  ComboBox1 -> Items -> Add ("Uno");
  ComboBox1 -> Items -> Add ("Dos");
  ComboBox1 -> Items -> Add ("Tres");
  // Y modificamos el texto de la línea de edición
  ComboBox1 -> Text = "Cuatro";
}

Y la apariencia final de nuestro programa, ya en funcionamiento, podría ser algo así


Curso de C. C++ Builder. 5: Menús.

Vamos a añadir a nuestro programa un “menú de persiana” o “menú desplegable”. Para ello, primero añadimos el componente “MainMenu”, y luego hacemos doble click para modificarlo.

Nos aparece la pantalla de creación de menús, pero todavía casi vacía:

Vamos a añadir apenas cuatro o cinco opciones a este menú, para que se vea el manejo básico:

Primero modificamos el “caption” de ese único MenuItem (elemento de menú) que se ve en pantalla, escribiendo “&Archivo”. Veremos que aparecen dos nuevos “MenuItem” vacíos: uno por debajo del nuestro (para añadir elementos al menú Archivo” y otro a su derecha, para seguir añadiendo nuevos menús (como Edición, Ver, etc). También ocurre otra cosa interesante: la letra A de Archivo se ve subrayada. Eso lo hemos conseguido al poner delante suyo el símbolo &, y nos permite que podamos acceder rápidamente a ese menú, solo con pulsar Alt+A (en general, será pulsando Alt y al tecla que aparece subrayada). Si hubiesemos escrito Ar&chivo, sería la C la letra que aparecería subrayada y se podría acceder a ese menú pulsando Alt+C.

Sigamos. Vamos a añadir tres opciones al menú “Archivo”. Serán: Nuevo, Abrir, Salir. Además, queremos que entre las dos primeras y la tercera aparezca una línea de separación. Para conseguirlo, debajo de “Archivo” escribimos “&Nuevo” (en este caso, la N aparece subrayada, pero no se podrá acceder a esta opción con Alt+N, porque no está en la cabecera del menú, sino pulsando la N una vez que se ha desplegado el menú Archivo -esto es manejo básico de Windows, que ya debería saber quien haya llegado hasta aquí-). A su vez, debajo de Nuevo escribimos “&Abrir”. Para conseguir la línea de separación, ponemos en el “caption” del siguiente elemento un guión (-), ni mas ni menos. Y debajo del guión escribimos “&Salir”.

Nuestro menú debería estar quedando así:

Pues añadiremos sólo una nueva característica a nuestro menú antes de dar por zanjado el manejo básico. Hemos dicho que a la opción “&Nuevo” no se puede acceder directamente pulsando Alt+N porque está dentro del menú Archivo. Si queremos que se pueda entrar con una abreviatura de teclado, podemos seleccionar la opción “Nuevo” y cambiar en el Inspector de Objetos su propiedad “ShortCut”. Nos aparecerán combinaciones de Ctrl con teclas normales, combinaciones con las teclas de función (F1 a F12) y alguna otra especial. En nuestro caso, puede que lo más correcto sea Ctrl+N.

Nuestro formulario estaría quedando así:

Para añadir el código correspondiente a una cierta opción de un menú (lo que nuestro programa hará cuando se escoja esa opción), se deberá modificar el suceso “OnClick” (el único que permiten las opciones de menús).

Y otra cosa más. Para crear submenús lo podemos hacer desde el menú local (pulsando el botón derecho del ratón) o pulsando las teclas Ctrl + flecha derecha. Por ejemplo, si queremos que la opción “Abrir” del menú “Archivo” muestre a su vez un submenú, la señalaríamos con el ratón y pulsaríamos las teclas Ctrl+Derecha, o escogeríamos la opción “Create Submenu” del menú local.

Nota: Debemos intentar seguir el estándar que marca Windows y los programas como Explorador u Office. Los primeros menús serán “Archivo”, “Edición”, “Ver”. Los últimos serán “Ventana” y “Ayuda” (este último se suele representar con una interrogación). Dentro del menú “Archivo”, las primeras opciones serán “Nuevo”, “Abrir”, etc., y la última será “Salir”.

Otra cosa: siguiendo este mismo estándar que marca Microsoft, deberíamos poner puntos suspensivos después de cada opción que vaya a mostrar una ventana de diálogo para pedir datos adicionales. Por ejemplo, si cuando escojamos la opción “Abrir” se nos va a pedir el nombre del fichero a abrir, lo correcto sería que esta opción fuese “&Abrir...”, mientras que si la opción “Nuevo” va a crear un nuevo documento, sin preguntarnos nada más, no debería llevar puntos suspensivos: “&Nuevo”.

También podemos añadir “menús locales” o “menús contextuales”. Son otro tipo de menús cada vez más frecuentes en Windows, que aparecen cuando se pulsa el botón secundario del ratón (normalmente será el botón derecho, si somos diestros) sobre un cierto elemento.

Para crearlo, colocaremos sobre el elemento que nos interese un componente “PopupMenu”, que se maneja igual: se arrastra hasta nuestro formulario (o hasta el elemento al que queremos que corresponda ese menú contextual) y después se modifica con doble click. Sólo es necesario un paso adicional: deberemos enlazarlo con su “padre”. En nuestro caso, lo hacemos cambiando la propiedad “PopupMenu” de nuestro formulario (Form1), y diciéndole que su menú local va a ser “PopupMenu1” (o el nombre que hubiesemos dado al menú local que acabamos de crear).

Al pulsar el botón derecho del ratón en medio de nuestro formulario aparecería lo siguiente:

Dos propiedades interesantes de los elementos de un menú (menuItems) son “enabled”, que nos permite decir si queremos que una opción del menú no esté habilitada (el usuario no podrá emplearla, y se mostrará en color gris) y “checked”, que nos servirá para que aparezca una marca junto a una cierta opción, para avisar al usuario de que esa opción está activada. Ambas propiedades son de tipo “bool”.


Curso de C. C++ Builder. 6: Casillas de selección.

En Windows hay dos tipos de casillas de selección, como ya debería saber quien haya llegado hasta aquí, por eso de que estamos suponiendo un cierto conocimiento sobre Windows... }:-)

En los “CheckBox” se pueden seleccionar varias opciones a la vez (por ejemplo, ser hombre y estar casado).

En los “RadioButton” sólo se puede seleccionar una de las opciones a la vez, se trata de opciones excluyentes. Por ejemplo, ser hombre o mujer: o se es hombre o se es mujer... bueno, es un decir.

Estamos hablando de que la diferencia entre CheckBox y RadioButton es que se puedan escoger varias opciones (o no se pueda) de entre un grupo de varias opciones posibles. Por tanto, debe existir alguna forma de indicarle a nuestro programa que esas “varias opciones” van juntas formando un grupo. Esa forma son los “GroupBox”: primero podremos un “GroupBox” en nuestro formulario y después dentro de él insertaremos los RadioButtons y/o CheckBoxes que nos interesen.

Y hay una variante de los GroupBox, cuyo nombre de por sí ya nos da una pista de su cometido: los “RadioGroup”.

La apariencia de un mini-programa que los utilizase podría ser:

En cuanto al uso “en la práctica” de las casillas de selección, lo más importante será saber si el usuario de nuestro programa ha activado o no una cierta opción. Para eso se usa la propiedad “Checked”, tanto en los CkeckButtons como en los RadioBoxes, que nos devuelve un valor de tipo “bool” (“true” si la opción está activada, “false” si no está activada):

if (RadioButton2->Checked)
  ...


Curso de C. C++ Builder. 7: Más detalles sobre los botones. Otros tipos de botones.

Hay algunas posibilidades de los botones que no hemos comentado. Al menos un par de ellas son interesantes y merecen un poco de atención:
  • Podemos asignar abreviaturas de teclado a un botón, igual que hicimos con los menús: si como “caption” de un botón escribimos “A&yuda”, la Y aparecerá subrayada, y podremos usar la combinación de teclas Alt+Y para activar ese botón.
  • Cuando estamos en una ventana modal, normalmente nos interesará saber qué botón es el que ha pulsado el usuario (por ejemplo, si ha escogido Aceptar o Cancelar, Si, No o Todos, etc. Para eso tenemos definida la propiedad “ModalResult”, y una serie de valores predefinidos para ella, como mrYes, mrNo, MrOK, mrCancel y mrNone (Si, No, Aceptar, Cancelar y ninguno).
  • Al igual que ocurre con las opciones de los menús (y con las casillas de selección, y con buena parte de los componentes visuales de C++ Builder), los botones tienen una propiedad llamada “Enabled”, de tipo boolean, que nos permite deshabilitarlos para que el usuario de nuestro programa no pueda emplear un cierto botón momentáneamente.
  • También al igual que la mayoría de los componentes visuales de C++ Builder, los botones pueden mostrarnos una línea de ayuda cuando acerquemos el ratón a ellos. Para ello, escribimos en la propiedad “Hint” (ayuda, pista) el texto que queremos que aparezca, y le decimos que lo muestre dando el valor True a la propiedad “ShowHint”.
Los botones que hemos visto son un tanto “sosos”, y más viendo los últimos programas que se realizan para Windows. Si queremos que nuestros botones sean más vistosos y/o más prácticos, tenemos una opción sencilla: no usar los botones estándar de C++ Builder, sino otros que aparecen en la barra de componentes, pero un poco más escondidos, en la pestaña llamada “Additional”.

El primero de ellos es un botón que es capaz de mostrar una imagen (o BitMap), llamado BitBtn. Con la propiedad “kind” le podemos indicar qué tipo de botón es: si es de los habituales, C++ Builder tiene ya una imagen estipulada para ellos (por ejemplo, una X roja para Cancelar -bkCancel-, una interrogación azul para Ayuda -bkHelp-, etc); si queremos poner la imagen que nos interesa, escogeremos como “kind” el “bkCustom” (a medida) y cambiaremos la propiedad “Glyph”, en la que lo podemos indicar la imagen que queremos que aparezca. Por defecto, dicha imagen se muestra a la izquierda del texto; si queremos que se vea en otra posición, deberemos modificar la propiedad “Layout”, que en principio tiene el valor “blGlyphLeft”.

El segundo es el llamado “SpeedButton”, pensado para trabajar junto con otros botones, formando barras de botones (las clásicas “barras de herramientas” de Windows). Es muy similar al BitBtn, pero no permite ModalResult.

Un ejemplo muy sencillito:


Curso de C. C++ Builder. 8: Ventanas estándar de Windows.

Dentro de la pestaña “Additional” de la barra de componentes tenemos botones para acceder a ciertas ventanas estándar de Windows, como la de ”Abrir”, la de “Guardar”, la de elegir tipo de letra, etc. Los iconos que las representan son los siguientes:

OpenDialog es la ventana de diálogo estándar para “Abrir” un archivo.

SaveDialog es la ventana de diálogo estándar para “Guardar” un archivo.

FontDialog es la ventana de diálogo estándar para escoger un tipo de letra.

ColorDialog es la ventana de diálogo estándar para escoger un cierto color.

Para mostrar cualquiera de estas ventanas estándar se usa su método Execute (realmente se trata de una función, que devuelve un valor “bool”: false si se cancela la ventana o true si se acepta). Por ejemplo, podríamos crear un mini-programa que permitiese acceder a estas cuatro ventanas, pulsando ciertos botones.

La respuesta a la pulsación de cada una de estas teclas sería simplemente así:

void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
  OpenDialog1->Execute();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BitBtn2Click(TObject *Sender)
{
  SaveDialog1->Execute();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BitBtn3Click(TObject *Sender)
{
  FontDialog1->Execute();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BitBtn4Click(TObject *Sender)
{
  ColorDialog1->Execute();
}

Al pulsar el primero de los botones aparecería la ventana de “Abrir” habitual en Windows:

(Nota: convendría que escribiésemos, desde el Inspector de Objetos, en la propiedad “FileName” de nuestro “OpenDialog” el valor “*.*”, para que en la lista se nos muestren todos los ficheros; también podríamos cambiar la lista de tipo de archivos permitidos, con la popiedad “Filter”).

Si queremos leer el nombre del fichero que se ha elegido (o tecleado) en esta ventana, usaríamos construcciones como:

nombreFichero = OpenDialog1 -> Filename;

De formas similares podríamos leer la fuente (Font) que se ha escogido en un FontDialog o el color de un ColorDialog.

Pero hay otro tipo de ventanas que son también muy frecuentes en Windows. Son las ventanas de aviso, información, etc., que muestran un mensaje y uno o varios botones.

(Nota: para quien conozca Delphi, en C++ Builder no existe la más sencilla, una ventana que muestra un texto, y en cuya barra de título aparece el nombre de nuestra aplicación -ShowMessage-).

La forma más habitual de hacerlo será con MessageBox. Eso sí, esta es una función del API de Windows, que utiliza “pchar” (punteros a carácter, las cadenas de texto típicas de C) en vez de AnsiString. Un ejemplo de su uso es:

MessageBox(0, "Confirme que desea cambiar el color",
    "Confirmación",
    MB_OKCANCEL + MB_ICONQUESTION
  );

y la apariencia que obtendríamos sería:

Los parámetros casi se explican por sí solos: el segundo es el texto que aparece en la ventana, el tercero es el título de la ventana, el cuarto es el tipo de ventana (botones e iconos adicionales que contendrá) y... ¿el primero? Es una referencia a la ventana contenedora de ésta; basta con un cero, y de hecho tenemos una versión de “MessageBox” incluida dentro del objeto TApplication (la aplicación en sí), que no necesita este dato:

Application->MessageBox("Confirme que desea cambiar el color",
    "Confirmación",
    MB_OKCANCEL + MB_ICONQUESTION
  );

En la ayuda sobre el API de Windows tenemos la descripción detallada: el formato de MessageBox es

int MessageBox(

    HWND hWnd, // handle of owner window
    LPCTSTR lpText, // address of text in message box
    LPCTSTR lpCaption, // address of title of message box
    UINT uType  // style of message box
   );

Dentro de los “flags” que indican el tipo de botón, tenemos los siguientes: MB_ABORTRETRYIGNORE, (botones de Abortar, Reintentar y Cancelar), MB_OK (botón de aceptar), MB_OKCANCEL, MB_RETRYCANCEL, MB_YESNO, MB_YESNOCANCEL.

Dentro de los que indican los iconos adicionales a mostrar: MB_ICONEXCLAMATION,
MB_ICONWARNING, MB_ICONINFORMATION, MB_ICONASTERISK, MB_ICONQUESTION, MB_ICONSTOP, MB_ICONERROR, MB_ICONHAND

Y los valores que devuelve, según el valor que se pulse, son: IDABORT, IDCANCEL, IDIGNORE, IDNO, IDOK, IDRETRY, IDYES.

Como ya he dicho antes, quien quiera saber más puede recurrir a la ayuda que incorpora C++ Builder sobre el API de Windows.


Curso de C. C++ Builder. 9: Varias ventanas nuestras.

Hay tres puntos importantes en el manejo de múltiples ventanas (o formularios): cómo crearlas, cómo mostrarlas y cómo leer el valor que nos devuelven o lo que se ha introducido en ellas.

Crear una nueva ventana (o formulario) dentro de nuestro programa es muy fácil: desde el menú “File” elegir la opción “New Form” (o pulsar el botón de “New Form”).

Tenemos también “expertos” que nos ayudarán a crear rápidamente otros formularios más aplicados: ventanas de diálogo con los botones en horizontal o en vertical, ventanas de introducción de passwords, de acceso a bases de datos, incluso para crear distintos tipos de informes (listados).

Por ejemplo, si escogemos la opción “New...” del menú “File” nos aparece esa ventana:

Dentro de las opciones relativas a diálogos, tenemos para crear diálogos con los botones en la parte inferior o lateral, ventanas de introducción de passwords, etc:

Y dentro de “Forms” tenemos expertos que nos ayudarán a crear ventanas de acceso a bases de datos y listados:

Pero vamos al caso sencillo. Crearemos un formulario sencillo con la opción “New Form”

Una vez que tenemos las dos ventanas creadas, nos interesará que desde la primera (principal) se pueda entrar a la segunda (secundaria). Lo que haremos incluir una línea como ésta en la parte de nuestro programa que corresponda (por ejemplo, cuando el usuario pulse un cierto botón): Form2.Show();

void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
  Form2->Show();
}

(He supuesto que nuestra segunda ventana se llama “Form2”, que es el nombre que C++ Builder le da por defecto).

Si cuando probemos a poner el programa en funcionamiento nos dice que no sabe lo que es “Form2” (con un mensaje de error “undefined symbol Form2”), deberemos indicarle dónde se encuentra, añadiendo al principio de la “implementation” de la unit que corresponde la ventana principal algo así:

#include "Unit2.h"

(Nuevamente, he supuesto que hemos conservado para la unit el nombre que C++ Builder le da por defecto). Esta línea se podría añadir justo debajo de la que incluye “Unit1.h”, por ejemplo.

Nuestras dos ventanas se verían así:

Cuando terminemos de usar la ventana, lo sencillo es “ocultarla” en vez de destruirla (porque no la hemos creado nosotros en el momento de mostrarla, sino que automáticamente C++ Builder la prepara como “AutoCreate” para que se cree ella sola en el momento de empezar, y nosotros nos hemos limitado a hacerla visible). Esto se consigue con el método Hide: Automáticamente, la ventana principal volverá a ser la ventana activa.

void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
  Hide();
}

Para acceder a datos que están en otra ventana, la idea es muy sencilla para quien recuerde cómo se manejan las clases de objetos y/o los registros en Pascal (espero que todo el mundo): para leer el texto de una línea de edición llamada Edit1 que se encuentra en una ventana llamada Form1 se podría hacer:

respuesta = Form1 -> Edit1 -> Text;

Para manejar más de dos ventanas, no reviste ninguna dificultad extra.

Si la segunda ventana es una ventana modal, una ventana de diálogo que nos devuelva pocos valores, como Si o No, Cancelar o Aceptar, la mostraremos con “ShowModal()” en lugar de “Show”, y nos devolverá valores como mrYes, mrNo, mrOK o mrCancel, según el botón que pulsemos para cerrar la ventana:

if (frmConfirmacion->ShowModal() == mrCancel)
  ...


Curso de C. C++ Builder. 10: Introducción a los gráficos con C++ Builder.

Windows es un entorno gráfico. Si todo Windows trabaja en modo gráfico, es de suponer que para nosotros también debería resultar fácil crear gráficos.

Así es: cada formulario tiene una propiedad llamada Canvas, que representa el área de dibujo que dicho formulario contiene.

Este área (un objeto del tipo TCanvas), tiene propiedades como Pen (un objeto del tipo TPen que nos permitirá cambiar el color y el tipo de línea, a la hora de dibujar líneas) o "Brush" (del tipo TBrush, similar a Pen, pero que se aplica a rellenos y colores de fondo), o Font, que elige el tipo de letra con el que se escribirá, o Pixels[x][y], que devuelve el color que tiene un cierto punto de ese Canvas.

Dentro de los métodos que se le aplican, hay algunos como MoveTo, que desplaza el cursor a una cierta posición, LineTo, que dibuja una línea hasta un punto dado, Rectangle, que dibuja un rectángulo dadas dos de sus esquinas, RoundRect, que dibuja un rectángulo con las esquinas redondeadas, Ellipse, que dibuja una elipse (indicando las esquinas del rectángulo que la delimita) o un círculo (si es un cuadrado en vez de un rectángulo), FloodFill, que rellena un área con un cierto color y estilo de relleno, TextOut, que escribe un texto en unas ciertas coordenadas, o Draw, que dibuja una imagen (un Bitmap por ejemplo) en una cierta posición.

Para más detalles sobre cualquiera de ellos, basta mirar la ayuda de Canvas, de sus propiedades o sus métodos.

Pero vamos a aplicarlo a algún ejemplo.

Dibujaremos una serie de circulos, cada uno ligeramente menor que el anterior y ligeramente desplazado, para dar la apariencia de un cono. El formulario tendrá dos botones: uno será el que dibuje el cono, y el otro borrará el formulario dibujando un rectángulo en el mismo color que el fondo y del mismo tamaño:

El código de esos dos botones podría ser así:

//---------------------------------------------------------------------------
void __fastcall TForm1::btDibujarClick(TObject *Sender)
{
  int i;  // Para dibujar varios circulos con "for"

  Canvas->Pen->Color = clBlack;
  Canvas->Brush->Color = clLime;
  for (i=32; i>=1; i--)
    Canvas->Ellipse(1,1,i*10,i*10);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btBorrarClick(TObject *Sender)
{
  // Para borrar, primero tomo el color de
  // fondo del formulario
  Canvas->Pen->Color = Color;
  Canvas->Brush->Color = Color;
  // Y luego dibujo un rectángulo del tamaño
  // de la zona interior del formulario
  Canvas->Rectangle(0, 0,
    ClientWidth, ClientHeight);

}

Comentarios sobre este programa

  • Los colores se definen con constantes como clBlack, clBlue y similares, que ya comentamos en la solución al primer ejercicio.
  • ClientWidth y ClientHeight son la anchura y la altura de la zona interior de la ventana, mientras que Width y Heigth son la anchura y la altura de toda la ventana (incluyendo bordes, menú si lo tuviese, etc.)
  • En la práctica, esta forma de crear gráficos sirve para cosas “sencillas” (por ejemplo, nuestras propias rutinas de gráficos de barras, de sectores, histogramas, etc), pero no para juegos ni para aplicaciones en las que la velocidad sea crítica o debamos sacar el máximo partido de la tarjeta gráfica que haya en el sistema. En este caso se suele recurrir a las “DirectX”, unas extensiones de Windows que permiten acceder directamente al hardware, y que nos proporcionan una serie de funciones para que lo hagamos de la forma más estándar posible. La programación con DirectX es compleja y queda fuera del cometido de este curso... al menos por ahora.
Bueno... como introducción ya está bien. Esto es todo por ahora. Espero que haya servido para quitar un poco el miedo a la programación bajo Windows (con C++ Builder, porque con un compilador de C o incluso de C++ que no sea visual resulta un trabajo muy duro) y para despertar la curiosidad a alguien...

Quien quiera profundizar más, tiene a su disposición los fuentes de ejemplo que incluye C++ Builder, los que se pueden localizar por Internet, etc.


Curso de C. Compiladores.

Los ejemplos de este curso están comprobados con al menos un compilador de lenguaje C. En este apartado voy a comentar brevemente dónde se pueden conseguir esos compiladores gratis o a bajo precio (sin ningún tipo de piratería, por supuesto), así como algunas notas básicas sobre su instalación y uso.
  • Turbo C 2.01
  • Turbo C++ 2nd Edition
  • DJGPP 2.03
  • Borland C++ 5.5 Free command-line tools
  • Watcom C++ 11.0c
  • Dev C++ 4.0
  • Symantec C++ 6.0
  • Digital Mars 8.22
  • MinGW 2.0
  • PCC 2.1c
  • Compilador de C y C++ bajo Unix y Linux


Curso de C. Compiladores - Turbo C 2.01.

Este es un compilador de C (no de C++) antiguo, que hoy en día se puede descargar gratuitamente desde la página Web de Borland, en la dirección:
http://community.borland.com/article/images/20841/tc201.zip

Se trata de un único fichero ZIP, de unos 1.100 Kb de tamaño.

Nota: Este compilador, por ser gratis, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web http://www.pobox.com/users/ncabanes si quieres más información sobre este Cd.

Este compilador no permite trabajar con múltiples ventanas a la vez, ni realza la sintaxis en colores, pero puede ser una buena opción para empezar.

Los pasos de instalación serían los siguientes:

  • Descomprimimos el fichero ZIP. Existen 3 carpetas (o directorios) llamadas DISK1, DISK2, DISK3, que corresponden a los 3 diskettes que formaban la distribución original, pero no es necesario conservar esta estructura de carpetas. Al contrario, resulta más rápida la instalación si descomprimimos todo en una misma carpeta.
  • Una vez está descomprimido, tecleamos INSTALL (si estamos en MsDos; si estuvieramos en Windows, haríamos doble clic sobre este fichero) y aparece una pantalla como esta:
  • No es más que una bienvenida, y nos pide que pulsemos Enter (Intro) para continuar. Entonces veremos:
  • Nos pide que introduzcamos la letra que corresponde al disco de origen. Si lo hubieramos volcado en diskettes, confirmaríamos que se trata de la unidad A. En nuestro caso, como lo hemos conservado en el disco duro, teclearemos C (o D, o la letra que corresponda a la unidad de disco duro en la que lo hemos guardado) y pulsaremes Intro. Nos hará otra pregunta:

  • Esta vez quiere saber, ya que estamos en el dico duro, desde qué carpeta queremos instalar. Normalmente aparecerá escrito el nombre de la carpeta en la que nos encontramos, de modo que bastará con confirmarlo pulsando Intro.

  • Esta vez nos pregunta dónde queremos instalar. Daremos por sentado que tenemos disco duro (de modo que la última opción, la de instalar en diskettes queda descartada) y que no poseemos Turbo C 1.5, con lo que tampoco nos interesa la segunda opción. Entonces está claro, escogemos la primera opción, para instalar en disco duro.

  • Esta vez quiere saber en qué carpeta (directorio) de nuestro disco duro queremos instalar. En mi caso, tengo todos los lenguajes de programación en la carpeta LENG de mi disco C, de modo que corregía la primera línea para indicar que prefería usar D:\LENG\TC201. El resto de líneas se modifican automáticamente para adaptarse a lo que "ordene" la primera, de modo que deberíamos obtener algo así:

  • Podría haber modificado cualquier otra carpeta, desplazándome con ella hasta las flechas y pulsando Intro, pero no es necesario. De modo que bajamos hasta la opción "Start Installation" y pulsamos Intro. Al cambo de unos instantes, aparece la ventana que nos avisa de que todo ha terminado correctamente:

  • Nos avisa de que deberíamos hacer ciertos cambios en CONFIG.SYS y AUTOEXEC.BAT de nuestro ordenador. El primer cambio no será necesario (normalmente) en las últimas versiones de MsDos ni en Windows; el último paso tampoco será "estrictamente necesario" por ahora...
  • Ya está casi todo listo. Para usarlo, basta ir hasta la carpeta en la que lo hemos instalado, y teclear TC (o hacer doble clic en TC si usamos Windows), y ya podremos comenzar a teclear nuestros programas:

  • Cuando hayamos tecleado un fuente, podemos pulsar Ctrl+F9 (o la opción Run del menú Run) para ponerlo en funcionamiento. En caso de error, nos diría qué es lo que no ha funcionado correctamente; si va todo bien pero "no llegamos a ver nada" porque desaparece demasiado rápido, podemos pulsar Alt+F5 para comprobar qué era lo que había aparecido en la pantalla.


Curso de C. Compiladores - Turbo C++ 2nd Edition.

Este es un compilador de C y C++ para MsDos (en modo real), creado por la casa Borland (ahora llamada Inprise). Incluye el compilador "de línea de comandos" (TCC) y un entorno integrado de desarrollo (IDE), TC.EXE.

Ocupa dos diskettes de 1.44 Mb, y fue regalado con los números 3 (Enero de 1995) y 4 (Febrero de 1995) de la revista RPP ("Revista profesional para programadores"), editada por Anaya Multimedia.

El precio de portada de la revista era de 795 pesetas, luego quien quiera pedir estos dos ejemplares para conseguir el compilador, deberá preparar unas 1.500 pesetas (más gastos de envío, supongo).

Para instalarlo, hay que introducir el primer diskette, teclear A:INSTALL y seguir las instrucciones en pantalla (muy similares a la de Turbo C 2.01).

Para conseguir versiones más modernas (eso sí, ya no serán gratis), póngase en contacto con cualquier distribuidor oficial de Borland, o mire en Internet la página http://www.borland.com

Noticias de última hora (agosto 2000):

Esta versión de Turbo C++ (Second Edition, cuyo número de versión es 1.01) está disponible para descarga gratuitamente en la página Web de Borland, en la dirección

http://community.borland.com/article/images/21751/tcpp101.zip

Se trata de un único fichero ZIP (TCPP101.ZIP), de unos 2.750 K de tamaño. Al descomprimirlo, aparecen 4 directorios llamados DISK1 a DISK4, que corresponden al contenido de los diskettes originales (4 diskettes de 720K).

También debería funcionar si se descomprime todo en un mismo directorio y se accede a INSTALL.EXE (con doble clic, desde Windows, o tecleando INSTALL desde MsDos), al igual que hemos comentado para Turbo C 2.01.

Nota: Este compilador, por ser gratis ahora, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web http://www.pobox.com/users/ncabanes si quieres más información sobre este Cd.


Curso de C. Compiladores - DJGPP 2.03.

DJGPP es la versión para MsDos del compilador de C y C++ desarrollado por GNU, adaptada para este sistema operativo por DJ Delorie.

Se trata de un compilador gratis, que crea ejecutables para MsDos en modo protegido. Comentaré los pasos que hay que dar para obtener los ficheros necesarios para instalar la versión 2.03, así como lo que hay que hacer para completar dicha instalación.

La versión más reciente se podrá conseguir en Internet, en la página Web de su creador, DJ Delorie, www.delorie.com/djgpp

Nota: Este compilador, por ser gratis, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web http://www.pobox.com/users/ncabanes si quieres más información sobre este Cd.

Una vez entramos a la Web de DJ Delorie, existe una opción llamada "Zip File Picker", que nos permitirá escoger que´ficheros necesitamos según el tipo de instalación que nos interese.

Se nos preguntará con qué sistema operativo queremos trabajar (supondré que Windows 95/98), con qué lenguajes (de entre C, C++, Objective C, Asm y algún otro - yo supondré que C y C++), si queremos ciertos extras como librerías de gráficos y de creación de juegos (supondré que sí), de qué servidor queremos descargar los ficheros (por ejemplo SIMTEL), etc.

Se nos responderá con una página parecida a ésta, que reproduzco casi "tal cual", y que comentaré a continuación:

DJGPP Zip File Picker Results

FTP Site: ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/

Note: Don't install djgpp in /dev as djgpp treats that directory specially.

Note: If your FTP site doesn't have the selected files, please choose another one, like ftp.simtel.net

Note: If you are using Windows 95/98/ME, please check the file djgpp.env to make sure LFN is set to Y and not N, and make sure you use an unzip program that supports Win95 long file names, like djgpp's unzip32.

Note: Do not install DJGPP in a directory that requires long file name support (i.e. no spaces, 8.3 characters)

Note: The FAQ has information on adjusting the registry so that one installation works with both long and short filenames (Win 95/98/NT and DOS)

Note: To view online documentation and help, run the "info" program.

Note: RHIDE has an online help viewer also.

Note: Start RHIDE in the directory with your sources, so that it puts the .exe there also.

Note: In RHIDE, press F1 twice to get help on help

Note: Sample compile command: gcc hello.c -o hello.exe

Note: Use gxx (or append -lstdcx to your gcc command line) to compile and link C++ programs (*.cc)

Note: If you get errors about cout, iostream.h, streambuf.h, or libiostream.a, please read the FAQ

Note: Assember files should have a .S extension.

Note: You need to build Allegro before you can use it. Use "make"

Read the file README.1ST before you do anything else with DJGPP! It has important installation and usage instructions.

unzip32.exe                to unzip the zip files               91 kb

v2/copying.dj              DJGPP Copyright info                  3 kb
v2/djdev203.zip            DJGPP Basic Development Kit         1.5 mb
v2/faq230b.zip             Frequently Asked Questions          664 kb
v2/readme.1st              Installation instructions            20 kb

v2apps/rh1478b.zip         RHIDE                               2.0 mb

v2gnu/bnu2112b.zip         Basic assembler, linker             2.6 mb
v2gnu/fil40s.zip           File Utils (for building Allegro)   1.4 mb
v2gnu/gcc302b.zip          Basic GCC compiler                  2.2 mb
v2gnu/gdb500b.zip          GNU debugger                        1.1 mb
v2gnu/gpp302b.zip          C++ compiler                        2.4 mb
v2gnu/mak3791b.zip         Make (processes makefiles)          263 kb
v2gnu/txi40b.zip           Info file viewer                    632 kb

v2tk/allegro/alleg312.zip  Allegro game library                1.4 mb

v2tk/grx243s.zip           GRX Graphics                        1.8 mb
v2tk/pdcur24s.zip          Curses Emulator                     453 kb
v2tk/rsxdj151.zip          RSX (Windows GUI Lib)               3.5 mb
Total bytes to download: 22,796,511

Installation Instructions for win95

Use Explorer to create a folder for DJGPP. My Computer, Drive C, File->New->Folder, "DJGPP".

Use djgpp's unzip32.exe to unzip all the zips you downloaded into the C:\DJGPP folder. Don't unzip them each into separate folders - unzip them all to the same place.

You need to update your C:\AUTOEXEC.BAT to include the following lines:

set PATH=C:\DJGPP\BIN;%PATH%
set DJGPP=C:\DJGPP\DJGPP.ENV
Note that the PATH statement should follow any other PATH statements, or you may edit an existing PATH statement.

You'll need to restart your computer for these changes to take effect.

Rather than edit your autoexec files and/or global environment, you may wish to create a djgpp shortcut instead. To do this, create a c:\djgpp\djgpp.bat that has lines like this:

@echo off
set PATH=c:\djgpp\bin;%PATH%
set DJGPP=c:\djgpp\djgpp.env
chdir c:\djgpp\mystuff   (or any other directory)
command
(you can replace that last line with any other shell you'd like)

A pesar de que está en inglés, todo ello debería resultar bastante intuitivo. Además, los nombres exactos de los ficheros pueden variar con el paso del tiempo, si se van creando versiones más modernas.

Aun así, vamos a comentar esta respuesta que hemos obtenido. Los ficheros que necesitamos son:

Para compilar fuentes en C:

  • djdev203.zip    (soporte para MsDos, creado por DJ Delorie, del directorio DJGPP\V2)
  • gcc302b.zip     (compilador GCC versión 3.02, del directorio DJGPP\V2GNU)
  • bnu2112b.zip    (ensamblador, enlazador y otros, del directorio DJGPP\V2GNU)

Para C++, además

  • gpp302b.zip    (compilador de C++, del directorio DJGPP\V2GNU)

Para leer los manuales en línea

  • txi40b.zip     (del directorio DJGPP\V2GNU)

Para las preguntas más frecuentes y sus respuestas

  • faq230b.zip     (del directorio DJGPP\V2)

Si queremos tener un entorno de desarrollo con realce de sintaxis en colores y otras ventajas

  • rhide1478b.zip    (del directorio DJGPP\V2APPS)

Si queremos fuentes adicionales para crear gráficos, juegos, etc:

  • grx243s.zip       (librería gráfica GRX, del directorio v2tk)
  • alleg312.zip      (librería de creación de juegos Allegro, del directorio v2tk/allegro)
  • rsxdj151.zip      (para crear entornos con ventanas, del directorio v2tk)
  • pdcur24s.zip      (un emulador de las librerías "curses" de Unix, del directorio v2tk)
También tenemos un depurador, una herramienta, make, etc.

En total, estos ficheros ZIP ocupan unos 23 Mb.

Los pasos a seguir para instalar el compilador son los siguientes:

  • Crear el directorio en el que queremos instalar DJGPP, por ejemplo C:\DGPPP
      C:\> MD DJGPP
  • Copiar en ese directorio los ficheros ZIP que nos interese, el UNZIP32, y descomprimir estos ficheros ZIP (hay que conservar su estructura de subdirectorios, lo que puede obligar a usar la opción "-d" de utilidades como PKUNZIP). Si usamos el UNZIP32 que incorpora DJGPP bastaría con escribir:
      C:\DJGPP> UNZIP32 *.ZIP
  • Borrar estos ficheros ZIP que ya hemos descomprimido.
      C:\DJGPP> DEL *.ZIP
  • Preparar las variables de entorno que le dicen al sistema dónde encontrar DJGPP y sus ficheros auxiliares:

      SET DJGPP=C:\DJGPP\DJGPP.ENV
      SET PATH=C:\DJGPP\BIN;%PATH%

Estas dos líneas se pueden teclear antes de empezar a usar DJGPP (cada día que lo usemos), o mejor incluirlas al final del fichero AUTOEXEC.BAT.
En mi caso, yo prefiero no cargar mucho el AUTOEXEC.BAT, y me creo un fichero BAT por cada compilador, por ejemplo DJINI.BAT. Cuando voy a usar DJGPP accedo primero a ese fichero BAT, y así no desperdicio memoria (espacio de entorno, para ser más exactos) cuando uso mi ordenador pero no voy a acceder a DJGPP.

La forma de crear este fichero BAT sería teclear lo siguiente (la última línea quiere decir que se pulse la tecla Z mientras se mantiene apretado CTRL, y después soltar ambas y pulsar Intro, para que el fichero quede guardado).

      copy con djini.bat
      SET DJGPP=C:\DJGPP\DJGPP.ENV
      SET PATH=C:\DJGPP\BIN;%PATH%
      ^Z

  • Si estas dos variables las hemos incluido en AUTOEXEC.BAT, deberemos reiniciar el equipo para que los cambios tengan efecto. Si hemos creado otro fichero BAT, bastará con ejecutarlo, tecleando DJINI.

Creamos el clásico programa HOLA.C o similar
      #include <stdio.h>

      main()
      {
        printf("Hola\n");
      }

y lo compilamos tecleando

      gcc hola.c -o hola.exe

(La opción -o sirve para indicar el nombre del fichero ejecutable; si no incluimos esa opción, el fichero se llamará A.OUT y/o A.EXE).
En mi caso, este programa tan sencillo se convierte en un fichero EXE de 80K de tamaño.
  • Si queremos trabajar con el entorno de desarrollo RHIDE, basta con teclear RHIDE. En el menú File tenemos las opciones de Abrir ficheros ya existentes (Open) o crear un fichero nuevo (New).

Un comentario: si creamos un fichero con la opción New y empezamos a teclear, veremos que no realza la sintaxis en colores. Para que lo haga, necesita saber que se trata de un fuente en C, y lo sabrá en cuanto grabemos el fichero. Aunque no esté terminado, pulsamos F2 (o elegimos la opción Save del menú File), le damos un nombre como HOLA.C y a partir de entonces ya tendremos sintaxis en colores, que hace más legible el fuente y más fácil descubrir errores.

(También tienes información relativa a la versión anterior: DJGPP 2.01).


Curso de C. Compiladores - DJGPP 2.01.

(Nota: existe una versión más moderna: mira la información sobre DJGPP 2.03)

DJGPP es la versión para MsDos del compilador de C y C++ desarrollado por GNU, adaptada para este sistema operativo por DJ Delorie.

Se trata de un compilador gratis, que crea ejecutables para MsDos en modo protegido. Voy a tratar de describir los ficheros necesarios para instalar la versión 2.01, así como los pasos que hay que dar para completar dicha instalación.

Esta versión se regalaba en CdRom con el número 63 de la revista PcManía (Diciembre 97), que tenía un precio de portada de 1.200 pts. La versión más reciente se podrá conseguir en Internet, en la página Web de su creador, DJ Delorie:

http://www.delorie.com

Nota: Este compilador, por ser gratis, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web http://www.pobox.com/users/ncabanes si quieres más información sobre este Cd.

Vamos a hacer una "instalación típica". Para ello necesitaremos los siguientes ficheros:

Para compilar fuentes en C:

  • djdev201.zip (del directorio DJGPP\V2)
  • gcc2721b.zip (del directorio DJGPP\V2GNU)
  • bnu27b.zip (del directorio DJGPP\V2GNU)

Para C++, además

  • gpp2721b.zip (del directorio DJGPP\V2GNU)
  • lgp271b.zip (del directorio DJGPP\V2GNU)

Para leer los manuales en línea

  • txi390b.zip (del directorio DJGPP\V2GNU)

Si no tenemos servidor DPMI (para acceder al modo protegido del DOS)

  • csdpmi3b.zip (del directorio DJGPP\V2MISC) (No es necesario si trabajamos de Windows, OS/2, Linux DOSEmu u OpenDOS, entre otros, será imprescindible casi en el único caso de que trabajemos con el “MsDos de verdad”, pero tampoco está de más tenerlo por si acaso).
Para las preguntas más frecuentes y sus respuestas
  • faq210b.zip (del directorio DJGPP\V2)

Si queremos tener un entorno de desarrollo con realce de sintaxis en colores y otras ventajas

  • rhide14b.zip (del directorio DJGPP\V2APPS)
En total, estos ficheros ZIP ocupan unos 7.900 Kb (como aviso para aquellos que tengan el valor de traerlos desde Internet).

Los pasos a seguir son los siguientes:

  • Crear el directorio en el que queremos instalar DJGPP, por ejemplo C:\DGPPP

      C:\> MD DJGPP

  • Copiar en ese directorio los ficheros ZIP que nos interese y descomprimirlos con su estructura de subdirectorios, por ejemplo así:

      C:\DJGPP> PKUNZIP -D DJDEV201.ZIP
      ...

  • Borrar estos ficheros ZIP que ya hemos descomprimido.

      C:\DJGPP> DEL DJDEV201.ZIP
      ...

  • Preparar las variables de entorno que le dicen al sistema dónde encontrar DJGPP y sus ficheros auxiliares:

      SET DJGPP=C:\DJGPP\DJGPP.ENV
      SET PATH=C:\DJGPP\BIN;%PATH%

Estas dos líneas se pueden teclear antes de empezar a usar DJGPP (cada día que lo usemos), o mejor incluirlas al final del fichero AUTOEXEC.BAT.
En mi caso, yo prefiero no cargar mucho el AUTOEXEC.BAT, y me creo un fichero BAT por cada compilador, por ejemplo DJGPPINI.BAT. Cuando voy a usar DJGPP accedo primero a ese fichero BAT, y así no desperdicio memoria (espacio de entorno, para ser más exactos) cuando uso mi ordenador pero no voy a acceder a DJGPP.
  • Si estas dos variables las hemos incluido en AUTOEXEC.BAT, deberemos reiniciar el equipo para que los cambios tengan efecto. Si hemos creado otro fichero BAT, bastará con ejecutarlo.

    • Después de hacer una de ambas cosas, tecleamos GO32-V2 para saber cuanta memoria tenemos disponible para el servidor. Nos dirá dos cantidades: la memoria disponible y el espacio de intercambio, algo parecido a esto:

      DPMI memory available: 4339 Kb
      DPMI swap space available: 3469 Kb
Si la memoria es inferior a 4 Mb, deberemos acudir a las FAQs a buscar alguna solución alternativa. Si nos indica más de esta cantidad, estamos listos para compilar un programa de prueba.
Creamos el clásico programa HOLA.C o similar
      #include <stdio.h>

      main()
      {
        printf("Hola\n");
      }

y lo compilamos tecleando

      gcc hola.c -o hola.exe

(La opción -o sirve para indicar el nombre del fichero ejecutable; si no incluimos esa opción, el fichero se llamará A.OUT y/o A.EXE).
En mi caso, este programa tan sencillo se convierte en un fichero EXE de 80K de tamaño.
  • Si queremos trabajar con el entorno de desarrollo RHIDE, basta con teclear RHIDE. En el menú File tenemos las opciones de Abrir ficheros ya existentes (Open) o crear un fichero nuevo (New).
Un comentario: si creamos un fichero con la opción New y empezamos a teclear, veremos que no realza la sintaxis en colores. Para que lo haga, necesita saber que se trata de un fuente en C, y lo sabrá en cuanto grabemos el fichero. Aunque no esté terminado, pulsamos F2 (o elegimos la opción Save del menú File), le damos un nombre como HOLA.C y a partir de entonces ya tendremos sintaxis en colores, que hace más legible el fuente y más fácil descubrir errores.
Como comentario final, esta versión de DJGPP (2.01), con esta instalación típica, supone instalar en nuestro disco duro ficheros hasta un total de más de 17.000 Kb. En sí mismo no es un tamaño exagerado, pero hay muchos ficheros pequeños, lo que hace que en un disco duro de 1.6 Gb (con clusters de 32Kb) realmente nos estará ocupando más de 47.000 Kb !!!!

En un disco duro más pequeño, o si se emplea un sistema de ficheros que no haga clusters tan grandes, ocupará un valor intermedio entre esos 17.000 y 47.000.

(Nota: la mayor parte de esta información se puede encontrar en un fichero llamado README.1ST, en el directorio DJGPP\V2; para versiones posteriores a la 2.01, es casi seguro que habrán cambiado los nombres de los ficheros, así que convendrá mirar README.1ST para ver las diferencias).


Curso de C. Compiladores - Borland C++ 5.5 Free CommandLine Tools.

La versión “Free CommandLine Tools” de Borland C++ 5.5 fue incluida en el CdRom de portada de la revista PcActual de Abril de 2000.

Por tratarse de una versión de libre distribución, está también disponible para descarga en la página Web de Borland (ahora llamada Inprise): http://www.borland.com o bien http://www.inprise.com. En concreto, se puede acceder a él en la página:

http://www.borland.com/bcppbuilder/freecompiler/

Nota: Este compilador, por ser gratis, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web http://www.pobox.com/users/ncabanes si quieres más información sobre este Cd.

Se trata, como su propio nombre indica, de un compilador “de linea de comandos”, es decir, sin entorno de desarrollo, diseñado para ser utilizado desde el “modo MsDos” de Windows 95 o superiores.

La forma básica de instalarlo sería:

  • Descomprimirlo haciendo doble clic en el fichero de instalación, llamado “FreeCommandLineTools.exe”. Aparece primero la pantalla de presentación:
  • Después aparece la licencia de usuario, y finalmente nos pregunta en qué directorio queremos instalarlo:

  • Al cabo de un momento quedará instalado y (en teoría) listo para utilizar.
  • Como primera comprobación de que funciona, saldríamos al modo MsDos de Windows 95 (o 98, o el que estemos manejando), y nos desplazaríamos al directorio en el que hemos instalado el compilador:
      CD \BORLAND\BCC55
  • Los “ejecutables” del compilador están dentro del directorio BIN, de modo que entramos a este directorio.
      CD BIN
  • Por tratarse de un compilador “de línea de comandos”, no dispone de editor, de modo que crearemos el clásico HOLA.C empleando el editor de MsDos o cualquier otro:
      EDIT HOLA.C
  • El contenido de este fichero podría ser
       #include <stdio.h>
       main()
       {
         printf("Hola\n");
       }
  • Para compilar este fichero deberíamos teclear esto...
      BCC32 HOLA.C
  • Pero me temo que no funcionará, porque el compilador no sabe todavía dónde se encuentran los ficheros de cabecera ni las librerías, de modo que mostrará un mensaje de error parecido a éste
      Borland C++ 5.5 for Win32 Copyright (c) 1993, 2000 Borland
      hola.c:
      Error E2209 hola.c 1: Unable to open include file 'stdio.h'
      Warning W8065 hola.c 5: Call to function 'printf' with no prototype
       in function main
      *** 1 errors in Compile ***
  • Hay dos formas de indicarle dónde están las librerias (directorio “lib”) y los ficheros de cabecera (directorio “include”). La incómoda es ampliando la orden que hemos tecleado, así:
      bcc32 -I"c:\borland\bcc55\include"  -L"c:\borland\bcc55\lib"  hola.c
  • La forma cómoda es creando un fichero de configuración, que el compilador utilizará siempre a partir de entonces. Para ello, creamos con nuestro editor de texto un fichero llamado BCC32.CFG, que deberá estar en el mismo directorio “bin” en el que estamos trabajando, y que deberá contener estas dos líneas:
      -I"c:\Borland\Bcc55\include"
      -L"c:\Borland\Bcc55\lib"
  • Para que el uso termine de ser cómodo (todo lo cómodo que puede ser un compilador de “línea de comandos”), sólo nos falta que nuestros fuentes puedan estar en cualquier directorio, no necesariamente en el apartado “bin” de nuestro compilador. Esto lo conseguimos modificando el “path” de nuestro ordenador. Nuevamente, hay varias formas de hacerlo.
  • La forma “mala” es modificar el PATH a mano, después de entrar al “modo MsDos” y antes de usar nuestro compilador (habrá que repetir este paso cada vez que cerremos el “modo MsDos” y volvamos a entrar a él), tecleando
       PATH = %PATH%;C:\BORLAND\BCC55\BIN
  • La forma “buena” sería modificar el fichero de configuración de arranque de nuestro ordenador, llamado AUTOEXEC.BAT, añadiendo la línea anterior al final de él. Así conseguimos que no haya que teclearlo cada vez que entremos a modo MsDos.
  • Hay más opciones. Por ejemplo, yo prefiero no recargar mucho el AUTOEXEC.BAT, así que me creo otro fichero BAT, llamado BCCINI.BAT, que contiene la línea anterior. Así, cuando entro a modo MsDos sólo debo teclear BCCINI. Es más lento que si hubiera modificado AUTOEXEC.BAT y más rápido que teclear la línea completa, y elimina los problemas secundarios de modificar AUTOEXEC.BAT (poder llenar el llamado “espacio de entorno”, o ralentizar ligeramente el funcionamiento del ordenador por tener muchos directorios de búsqueda que realmente no utilizamos).

Ya está correctamente instalado el compilador en nuestro ordenador. Cada vez que queramos probar un fuente que hayamos escrito, entraremos a modo MsDos y teclearemos

BCC32 NombreFichero.C


Curso de C. Compiladores - Watcom C++ 11.0c.

Watcom C++ ha sido siempre uno de los compiladores de C y C++ más prestigioso. De hecho, es uno de los más utilizados (por ejemplo) para programar juegos. Cualquier juego que incluyese un fichero llamado DOS/4GW (muchos) estaba creado con Watcom C++.

Recientemente, Sybase ha decidido hacerlo "Open Source", de modo que sea accesible a todos los usuarios, y que incluso estos puedan modificar el código fuente del propio compilador.

¿Cómo obtenerlo?

La versión 11.0c del compilador Watcom C++ se puede descargar de la página Web del proyecto "Open Watcom":

http://www.openwatcom.org

En esta página aparecen las últimas novedades, y también un apartado llamado "Downloads", desde podemos descargar una versión "beta" (sin terminar de probar) del compilador. Podemos rellenar un breve cuestionario, o bien saltar directamente a la zona de descargas haciendo clic en la opción "Browse FTP site".

En mi caso, he descargado un fichero de unos 48 Mb de tamaño, llamado “watcom-c-11.0c-b1.exe”, que corresponde a la "beta 1" de Watcom C++ versión 11.0c.

Nota: Este compilador, por ser gratis, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web http://www.pobox.com/users/ncabanes si quieres más información sobre este Cd.

Instalación.

La forma básica de instalarlo sería:

  • Haciendo doble clic en el fichero de instalación, llamado “watcom-c-11.0c-b1.exe”. Aparece en primer lugar una pantalla que nos indica que se trata de una versión diseñada para ser utilizada como actualización de una versión anterior, de modo que ciertos pasos de la instalación puede ser que tengamos que realizarnos nosotros manualmente:
  • En cuanto digamos que estamos de acuerdo, comienza la instalación en sí:
  • También se nos avisará de que ciertas posibilidades no se encuentran disponibles en esta versión. Por ejemplo, no incluye parte de las utilidades para programar bajo Windows (las que corresponden a las MFC -Microsoft Foundation Classes- ni al SDK, ya que ambos pertenecen a Microsoft). Tampoco incluye el "extensor" DOS/4GW para crear programas de MsDos en modo protegido, si bien existe uno alternativo (y podemos emplear otros que se pueden descargar gratuitamente desde Internet).
  • Después aparecen las condiciones de uso. Cuando las hayamos leído y hayamos dicho que estamos conformes con ellas, se nos pregunta en qué carpeta queremos instalar el compilador:
  • Después se nos pregunta para qué sistemas operativos queremos instalar soporte (bajo cuales vamos a trabajar):
  • Yo he escogido Dos y Windows 95/98/NT/2000 (ya no uso OS/2 ni Windows 3.1). A continuación indicaremos para qué sistemas operativos nos puede interesar compilar nuestros programas:
  • En mi caso, yo he instalado soporte para Windows 32 bits (lo mínimo razonable hoy en día), pero también para DOS, tanto en 16 bits como en 32 bits. La siguiente pregunta es qué componentes del compilador queremos utilizar:
  • Creo que lo razonable es instalar todo, a no ser que nuestro espacio en disco duro sea muy limitado. Nos avisará de que realizará una copia de seguridad de los ficheros que reemplace (no es mi caso, puesto que no poseo ninguna versión anterior de Watcom C++) y comienza la copia de archivos:

  • Al cabo de unos instantes, termina la instalación y Watcom C++ queda listo para usar... o casi

Compilando desde línea de comandos.

Vamos a empezar por ver la forma de usar Watcom C++ desde la "línea de comandos", es decir, tecleando órdenes desde el "modo MsDos".

Si queremos crear un ejecutable, lo que cualquier usuario de GCC (Linux), DJGPP (MsDos), Turbo C o Symantec C++ probaría a teclear es algo como

wcc ej01.c

pero obtenemos la siguiente respuesta:

Watcom C16 Optimizing Compiler  Version 11.0c
Copyright by Sybase, Inc., and its subsidiaries, 1984, 2000.
All rights reserved.  Watcom is a trademark of Sybase, Inc.
ej01.c(15): Error! E1055: Unable to open 'stdio.h'
ej01.c: 22 lines, 0 warnings, 1 errors

No es capaz de encontrar "stdio.h" (ni ningún otro fichero .h). Pero esto lo podemos solucionar añadiendo la opción /i= seguido del directorio en el que se encuentran los ficheros de cabecera. En mi caso sería:

wcc ej01.c /i=d:\leng\watcom\h

Esta vez ya "no protesta", pero tampoco crea el fichero "ejecutable" capaz de funcionar por sí mismo, sino sólo un fichero objeto llamado "EJ01.OBJ", que deberíamos enlazar posteriormente con las librerías correspondientes.

Pero resultará más cómodo hacerlo todo (compilar y enlazar) en un único paso, usando "wcl" en vez de "wcc":

wcl ej01.c /i=d:\leng\watcom\h

Pero esta vez tampoco todo funciona correctamente, sino que obtenemos otro error, esta vez a la hora de enlazar:

Watcom C/C++16 Compile and Link Utility Version 11.0c
Copyright by Sybase, Inc., and its subsidiaries, 1988, 2000.
All rights reserved.  Watcom is a trademark of Sybase, Inc.
       wcc EJ01.C  -i=d:\leng\watcom\h
Watcom C16 Optimizing Compiler  Version 11.0c
Copyright by Sybase, Inc., and its subsidiaries, 1984, 2000.
All rights reserved.  Watcom is a trademark of Sybase, Inc.
EJ01.C: 22 lines, included 477, 0 warnings, 0 errors
Code size: 17

WATCOM Linker Version 11.0c
Copyright by Sybase, Inc., and its subsidiaries, 1985, 2000.
All rights reserved.  Watcom is a trademark of Sybase, Inc.
Warning! W1115: file wlink.lnk: line(15): environment name watcom not found
Error! E2093: file wlink.lnk: line(15): cannot open \binw\wlsystem.lnk
Error: Linker returned a bad status

La línea más significativa es la antepenúltima: dice que no ha encontrado una "variable de entorno" llamada WATCOM y que le diga dónde se encuentra el compilador y todas sus utilidades. Esto lo podríamos conseguir tecleando lo siguiente antes de empezar a compilar nuestros programas:

SET WATCOM=D:\LENG\WATCOM

(Como siempre, en vez de D:\LENG\WATCOM se debería indicar la carpeta en la que hemos instalado el compilador).

Pero en la práctica, hay una forma más rápida de conseguir todo esto. Podemos crear un fichero BAT (por ejemplo, WCINI.BAT), que contenga lo siguiente:

@ECHO OFF
SET WATCOM=D:\LENG\WATCOM
SET PATH=%WATCOM%\BINW;%PATH%
SET EDPATH=%WATCOM%\EDDAT
SET INCLUDE=%WATCOM%\H

para que este fichero BAT cree esta variable de entorno WATCOM, actualice el PATH para que podamos compilar nuestros fuentes desde cualquier carpeta, y le avise también de dónde se encuentra los ficheros de cabecera.

Después de cargar WCINI (o el nombre que hubiéramos dado al fichero BAT), ya podríamos compilar nuestros fuentes desde cualquier otra carpeta y simplemente tecleando

wcl ej01.c


Curso de C. Compiladores - Dev C++ 4.0.

Este es un compilador de C y C++ para Windows, de libre distribución, basado a en el compilador MingW, el cual a su vez es una adaptación del compilador GCC de Gnu. Eso sí, la distribución de Dev C++ incluye un entorno de desarrollo que funciona bajo Windows y que incluye realce de sintaxis en colores.

Se puede descargar desde la página

http://www.bloodshed.net

Para instalarlo, basta hacer doble clic en SETUP, y aparecerá una primera pantalla con los términos de la licencia de uso. Después, se nos pregunta qué tipo de instalación nos interesa:

Yo suelo cambiar la carpeta de destino (tengo todos los lenguajes de programación agrupados bajo una única carpeta llamada LENG). Y también me gusta elegir la instalación "personalizada" (Custom), al menos para ver qué opciones hay disponibles y cuales de ellas considero interesantes para mí. Esto nos lleva a esta otra pantalla:

Si todo nos parece correcto, comienza la copia de ficheros. Al cabo de un instante, aparecerá la carpeta de Dev-C++, que incluye básicamente el entorno y los ficheros de ayuda:

La primera vez que empleamos este entorno de desarrollo, se nos pregunta el tipo de entorno que queremos usar y si queremos asociar los ficheros con extensión C, CPP, H y similares a Dev-C++, de modo que al hacer doble clic sobre cualquiera de ellos, sea abierto automáticamente con Dev-C++ (esta posibilidad puede ser interesante para quien no emplee ningún otro compilador bajo Windows, pero no tanto para quien tenga instalado C++ Builder o algún otro).

El entorno es sencillo de manejar, y realza la sintaxis en colores, para que resulte más fácil leer nuestros funtes y detectar errores:

Cuando compilemos nuestros fuentes, aparecerá una ventana que nos indica de los errores que han aparecido, y que nos permite ejecutar el programa, en caso de que no existiera ningún error:

Eso sí, los fuentes muy sencillos, como el clásico "Hola", desaparecen tan rápido de la pantalla que no da tiempo a leer lo que ha aparecido. No he encontrado ninguna forma de hacer "desde el compilador" que esta pantalla permanezca más tiempo, de modo que tendremos que hacerlo desde nuestros programas, incluyendo al final del mismo alguna orden como "getch", para que espere a que pulsemos una tecla. Con estos cambios, el fuente anterior quedaría así:

Ahora ya sí que tendremos tiempo de ver el resultado en pantalla:.


Curso de C. Compiladores - Symantec C++ 6.0.

Este es un compilador de C y C++ para MsDos (en modo real y en modo protegido) y para Windows, creado por la casa Symantec (anteriormente era conocido como Zortech C++). Tiene algún fallo, pero aun así puede ser de mucha utilidad. Incluye el compilador "de línea de comandos" (SC, para MsDos) y un entorno integrado de desarrollo y depuración (IDDE), para Windows, con realce de la sintaxis en colores.

Los ficheros de instalación ocupa cerca de 80 Mb, y fue regalado en CdRom con el número 4 (Diciembre de 1994) de la revista Solo Programadores editada por Tower Communications. El precio de portada de la revista era de 1250 pesetas.

Para instalarlo, hay que introducir el CdRom y ejecutar desde Windows (con el Administrador de Archivos de Windows 3.1x o con el Explorador de Windows 95, por ejemplo) el fichero SETUP.EXE.

Hay al menos dos versiones más modernas: la 6.1, que básicamente corrige los fallos descubiertos, y la 7.0, pero no sé si se sigue distribuyendo esta versión o alguna más reciente. Puede consultar en Internet la página http://www.symantec.com

Nota: En noviembre de 2001 he encontrado en Internet una versión "menos bonita" (no incluye entorno de desarrollo) pero que parece ser una evolución de Symantec C++, y poder descargarse libremente. Se llama Digital Mars.


Curso de C. Compiladores - Digital Mars 8.22.

Digital Mars es un compilador de C y C++, de libre distribución, desarrollado por los autores de Symantec C++ (al menos por Walter Bright), que no incluye entorno de desarrollo pero permite compilar para Dos, Dos en modo protegido y Windows, y que se puede descargar desde la dirección www.digitalmars.com. Todavía está a medio desarrollo, pero parece compilar bien los ejemplos básicos del curso y alguno de los no tan básicos.
Nota: Este compilador, por ser gratis, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web si quieres más información sobre este Cd.

Vamos a ver los pasos que hay que seguir para instalar la versión 8.22 (que era la última a principios de noviembre del 2001), con la esperanza de que no sean muy distintos en versiones sucesivas. Tendremos que descargar los siguientes ficheros:

  • dm822c.zip, el compilador en sí (pero que sólo incluye las librerías para crear programas de Windows), de unos 2.8 Mb de tamaño.
  • dm812dos.zip, las librerías para compilar para MsDos 16 bits (recomendable para crear programas sencillos en modo texto), de unos 605 Kb de tamaño.
  • dm812util.zip, utilidades adicionales (como conversores de OBJ a ASM o volcadores de ficheros, recomendable): unos 608 Kb.
  • dm815x.zip, si queremos compilar para MsDos en modo protegido (recomendable, de unos 540 Kb de tamaño). En este caso, nos interesará descargar también el "extensor" del Dos llamado X32 (un fichero de unos 184 Kb)
Para la instalación, basta con descomprimir esos ficheros conservando la estructura de directorios (casi todos ellos contienen además las subcarpetas BIN y LIB). El único problema es que el primero y el cuarto crean una carpeta llamada DM, mientras que el segundo y el tercero crean una llamada DM812. Deberíamos corregir esto, de modo que todos queden dentro de la carpeta DM.

Y entonces basta con entrar a la carpeta DM\BIN, teclear un fuente (o copiarlo a esa carpeta) y escribir

sc hola.c

Como siempre, si queremos poder usar el compilador desde cualquier otra carpeta, deberemos modificar AUTOEXEC.BAT para añadir ;c:\dm\bin (o la carpeta en la que lo hayamos instalado) al final de la línea que indica el PATH.

Yo prefiero no hacerlo, para no sobrecargar mucho el AUTOEXEC.BAT, sino que prefiero crear un fichero BAT (por ejemplo, DMINI.BAT) que contenga

PATH=C:\DM\BIN;%PATH%

y cargarlo justo antes de empezar a usar el compilador.

Si también quisiéramos crear ficheros para DOS en Modo Protegido, deberíamos haber descargado también X32.ZIP, y haber volcado el contenido de su carpeta LIB a la carpeta LIB de DM. Entonces, para compilar un fuente aprovechando toda la memoria disponible, escribiríamos:

sc hola.c -mx X32.LIB
(la opción -mx indica que compile para obtener un programa en modo protegido, y X32.LIB es el nombre de la "librería" que se debe enlazar para que funcione correctamente).


Curso de C. Compiladores - MinGW 2.0.

MinGW es un compilador de C y C++ para Windows, de libre distribución, basado en el compilador Gcc de GNU (de hecho, el nombre es la abreviatura de "Minimalist GNU for Windows"), y que se puede descargar desde la dirección www.mingw.org.
Nota: Este compilador, por ser gratis, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web si quieres más información sobre este Cd.

Vamos a ver las pautas para instalar la versión 2.0. Descargamos un único fichero llamado MinGW-2.0.0.3 (las últimas cifras indican el número de versión, que cambiará en las posteriores). El tamaño de este fichero es cercano a los 13 Mb.

Al hacer doble clic, comenzará por pedirnos confirmación antes de instalar:

Si decimos que sí queremos instalar, aparece la pantalla de bienvenida:

y después el acuerdo de licencia, con las condiciones de uso:

A continuación nos informa de la versión del compilador (GCC) y otras utilidades que incluye esta versión que hemos descargado:

y nos pregunta en qué directorio (carpeta) lo queremos instalar (personalmente, yo tengo todos los lenguajes de programación dentro de una carpeta llamada "leng"):

y los componentes que queremos instalar (en esta versión, sólo hay una opción posible):

Finalmente, nos muestra las opciones que hemos escogido, para que confirmemos que todo es correcto:

Y comienza a copiar ficheros:

Finalmente, aparece un mensaje de bienvenida

y se acabó la instalación.

Ahora el manejo será similar al de todos los compiladores que no incluyen entorno de desarrollo, sino que se manejan desde linea de comandos: basta con entrar a la carpeta MING\BIN (o donde lo hayamos instalado), teclear un fuente (o copiarlo a esa carpeta) y escribir

gcc hola.c -o hola.exe

(La opción -o indica el nombre que tendrá el fichero ejecutable resultante; si no la indicamos, se llamará A.EXE).

(GCC es el compilador de C, el de C++ se llama G++, y también se incluye un compilador de Fortran llamado G77).

Como siempre, si queremos poder usar el compilador desde cualquier otra carpeta, deberemos modificar AUTOEXEC.BAT para añadir ;c:\mingw\bin (o la carpeta en la que lo hayamos instalado) al final de la línea que indica el PATH.

Yo prefiero no hacerlo, para no sobrecargar mucho el AUTOEXEC.BAT, sino que me gusta más crear un fichero BAT (por ejemplo, MINGINI.BAT) que contenga

PATH=C:\MINGW\BIN;%PATH%

y cargarlo justo antes de empezar a usar el compilador.


Curso de C. Compiladores - PCC 2.1c.

Este es un compilador de C (no de C++) para MsDos , muy elemental (limitado a 64K de código y 64K de datos, sin entorno de desarrollo), distribuido como shareware, creado en junio de 1989 por:

      C Ware Corporation
         P.O. Box 428
     Paso Robles, CA 93446

No sé si existen versiones más recientes, ni siquiera si sigue existiendo la empresa que lo crea. Esta versión se puede conseguir en mi página Web www.pobox.com/users/ncabanes

Nota: Este compilador, por ser gratis, está incluido en mi CdRom recopilatorio sobre programación. Mira en mi página Web si quieres más información sobre este Cd.

Para instalarlo no hay más que descomprimir el fichero PCC21C.ZIP. tecleando

   PKUNZIP PCC21C

o bien

   UNZIP PCC21C

según el descompresor que poseamos.

Para crear los fuentes necesitamos un editor de textos, ya que no incluye entorno de desarrollo. Para compilar un programa, hay que teclear dos líneas: una para crear el código objeto y otra para enlazar y crear el ejecutable:

   PCC NombreDeFuente
   PCCL NombreDeFuente

donde NombreDeFuente es el nombre del fichero en lenguaje C, sin extensión. Por ejemplo, si tenemos un fichero llamado HOLA.C, teclearíamos

   PCC HOLA
   PCCL HOLA

El primer paso crea HOLA.O a partir de HOLA.C, y el segundo crea HOLA.EXE a partir de HOLA.O.

Podemos automatizar estos dos pasos creando un fichero BAT. En mi caso, uso uno llamado NCC.BAT que contiene

   PCC %1
   PCCL %1

Este fichero se puede crear con cualquier editor de textos, como el propio EDIT del DOS. Entonces, para dar los dos pasos de compilación bastará hacer NCC HOLA.


Curso de C. Compiladores - Compilador de C y C++ bajo Unix y Linux.

Es muy frecuente que las distintas versiones del sistema operativo Unix incluyan un compilador de C y C++ como estándar. Además suele ser un BUEN compilador. No en cuanto a comodidad de manejo, porque no suele incorporar entorno integrado de desarrollo (aunque no es difícil encontrarlos creados por terceras partes), sino en cuanto a fiabilidad (suelen ser compiladores fiables, muy probados, y que se suelen actualizar con frecuencia).

En algunas versiones de Unix este compilador se llama “cc”, pero en el caso concreto de Linux suele llamar “gcc” (la G hace referencia a GNU, aunque es un detalle en el que no entraremos por ahora). Yo me centraré en el caso de Linux, ya que es la versión de Unix más facil de conseguir (gratis si se descarga de Internet o con un precio simbólico si se adquiere en tiendas), existen varias recopilaciones entre las que elegir, creadas por distintas casas, y cada nueva versión va siendo más fácil de instalar y de configurar.

Su instalación no suele tener ningún problema, porque o bien se instala automáticamente con el resto de Linux, o en caso contrario basta con marcar la opción de que queremos que se instale. En ambos casos, el resto del proceso será totalmente automático, y quedará listo para usar.

Su uso habitual será tecleando el fuente con algún editor (como joe, jed, vi, o los que existen para el entorno gráfico Xwindows) y después indicando al compilador que lo convierta:

gcc hola.c -o hola

La opción “-o” es necesaria, para indicar el nombre del fichero de salida (si no lo indicamos, se llamaría “a.out”, que es un nombre demasiado poco intuitivo...)


Curso de C. Ejercicios frecuentes.

Esta opción está destinada a que todos los usuarios del curso puedan aprender de las consultas que más frecuentemente reciba a través de Internet.

Por ahora, todavía no hay incluido ningún ejercicio.


Índice global

Curso de C. Mini-referencia de C y C++

Desde aquí se podrá acceder a información resumida sobre algunas de las funciones más utilizadas de C y C++. Los temas disponibles por ahora son:

- C -

clrscr
cprintf

- D -

Drivers gráficos

- G -

getch
getche
gotoxy

- K -

initgraph

- K -

kbhit

- M -

Modos gráficos

- P -

printf
putpixel
puts

(También puede ver los cambios realizados en esta referencia)

Índice global


Curso de C. Referencia: clrscr.

Función: | Borra la pantalla en modo texto
Sintaxis: | void clrscr(void);
Prototipo: | conio.h
Valor devuelto: | Ninguno.
Observaciones: | Ninguna.
Ejemplo: | clrscr();
Portabilidad: | Turbo C y superiores (Turbo C++, Borland C++) bajo DOS. Puede estar disponible en algún otro compilador que siga la sintaxis de estos, como DJGPP. (En las aplicaciones en modo gráfico bajo Windows, no se debería utilizar).
Relacionados: | clreol, cprintf

Índice global | Índice de la referencia


Curso de C. Referencia: cprintf.

Función: | Manda salida formateada a la pantalla en modo texto, teniendo en cuenta los colores y los bordes de la ventana de texto (si se han definido).
Sintaxis: | int cprintf(const char *formato [, argumento,.]);
Prototipo: | stdio.h
Valor devuelto: | Devuelve el número de caracteres que se han escrito en pantalla.
Observaciones: | Hay una diferencia fundamental con "printf": cprintf no convierte los caracteres de avance de línea (\n) en combinaciones retorno de carro / avance de línea (\r\n). Las tabulaciones (\t) no se expanden a espacios.
Ejemplo: | cprintf("La edad es de %d años, aproximadamente %d meses", edad, edad*12);
Portabilidad: | Turbo C y superiores (Turbo C++, Borland C++). Puede estar disponible en algún otro compilador que siga la sintaxis de estos, como DJGPP. (En las aplicaciones en modo gráfico bajo Windows, no se debería utilizar).
Relacionados: | cadenas de formato, printf, clrscr

Índice global | Índice de la referencia


Curso de C. Referencia: drivers gráficos.

Función: | Hay ciertas constantes predefinidas que indican los drivers gráficos que se pueden utilizar con initgraph, para que resulte más fácil memorizarlos.
Valores posible: | constante ¦ valor numérico ----------------------+---------------------------------- DETECT ¦ 0 (autodetección) CGA ¦ 1 MCGA ¦ 2 EGA ¦ 3 EGA64 ¦ 4 EGAMONO ¦ 5 IBM8514 ¦ 6 HERCMONO ¦ 7 ATT400 ¦ 8 VGA ¦ 9 PC3270 ¦ 10
Prototipo: | graphics.h
Ejemplo: | -
Portabilidad: | Turbo C y superiores (Turbo C++, Borland C++) bajo DOS.
Relacionados: | initgraph

Índice global | Índice de la referencia


Curso de C. Referencia: getch, getche.

Función: Leer un carácter desde el teclado, mostrándolo en pantalla (getche) o sin mostrarlo (getch).
Sintaxis: int getch(void);
int getche(void);
Prototipo: conio.h
Valor devuelto: Devuelve el carácter leido del teclado.
Observaciones: -
Ejemplo: char tecla;
tecla = getch();
Portabilidad: Turbo C y superiores (Turbo C++, Borland C++). Puede estar disponible en algún otro compilador que siga la sintaxis de estos, como DJGPP.
Relacionados: getchar, gets, kbhit, scanf

Índice global | Índice de la referencia


Curso de C. Referencia: gotoxy.

Función: | Sitúa el cursor en cierta posición de la pantalla de texto.
Sintaxis: | void gotoxy(int x, int y);
Prototipo: | conio.h
Valor devuelto: | Ninguno
Observaciones: | Si las coordenadas no son válidas, se ignora la llamada a GotoXY
Ejemplo: | gotoxy (45, 12);
Portabilidad: | Turbo C y superiores (Turbo C++, Borland C++) bajo DOS. Puede estar disponible en algún otro compilador que siga la sintaxis de estos, como DJGPP.
Relacionados: | clrscr, cprintf, printf

Índice global | Índice de la referencia


Curso de C. Referencia: initgraph.

Función: Inicializa el modo gráfico.
Sintaxis: void far initgraph(int far *driver, int far *modo, char far *camino);
Prototipo: graphics.h
Valor devuelto: Ninguno.
Observaciones: Se ha de indicar el driver gráfico y el modo gráfico, o los resultados pueden ser imprevisibles. Si el driver es DETECT (autoedetección), en ese caso no es necesario indicar el modo gráfico. El último parámetro (camino) indica el directorio en que se encuentran los ficheros BGI necesarios para entrar al modo gráfico indicado. Se puede indicar un camino vació ("") si están en la misma carpeta que el fichero ejecutable.
Hay disponible una lista de drivers gráficos y de modos gráficos.
Ejemplo: int driverGraf = DETECT, modoGraf;
initgraph(&driverGraf, &modoGraf, "");
Portabilidad: Turbo C y superiores (Turbo C++, Borland C++) bajo DOS.
Relacionados: -

Índice global | Índice de la referencia


Curso de C. Referencia: kbhit.

Función: Comprueba si se ha pulsado alguna tecla
Sintaxis: int printf(const char *formato [, argumento,.]);
Prototipo: conio.h
Valor devuelto: Devuelve 0 si no se ha pulsado ninguna tecla, un valor distinto de cero si se ha pulsado alguna.
Observaciones: -
Ejemplo: cprintf("Pulse una tecla para seguir");
while (!kbhit()) ;  /* Espero sin hacer nada */
tecla = getch();    /*Leo la tecla pulsada */
Portabilidad: Turbo C y superiores (Turbo C++, Borland C++). Puede estar disponible en algún otro compilador que siga la sintaxis de estos, como DJGPP.
Relacionados: getch, scanf

Índice global | Índice de la referencia


Curso de C. Referencia: modos gráficos.

Función: Hay ciertas constantes predefinidas que indican los modos gráficos que se pueden utilizar con initgraph, para que resulte más fácil memorizarlos. Los modos gráficos disponibles dependen de qué driver se esté usando:
Valores
posible: 		   Driver ¦ Modos          ¦ Value ¦ Ancho*alto¦ Paleta     ¦ Páginas
 ---------+----------------+-------+-----------+------------+----------
   CGA    ¦ CGAC0          ¦  0    ¦  320 x 200¦    C0      ¦  1
          ¦ CGAC1          ¦  1    ¦  320 x 200¦    C1      ¦  1
          ¦ CGAC2          ¦  2    ¦  320 x 200¦    C2      ¦  1
          ¦ CGAC3          ¦  3    ¦  320 x 200¦    C3      ¦  1
          ¦ CGAHI          ¦  4    ¦  640 x 200¦     2 color¦  1
  MCGA    ¦ MCGAC0         ¦  0    ¦  320 x 200¦    C0      ¦  1
          ¦ MCGAC1         ¦  1    ¦  320 x 200¦    C1      ¦  1
          ¦ MCGAC2         ¦  2    ¦  320 x 200¦    C2      ¦  1
          ¦ MCGAC3         ¦  3    ¦  320 x 200¦    C3      ¦  1
          ¦ MCGAMED        ¦  4    ¦  640 x 200¦     2 color¦  1
          ¦ MCGAHI         ¦  5    ¦  640 x 480¦     2 color¦  1
  EGA     ¦ EGALO          ¦  0    ¦  640 x 200¦    16 color¦  4
          ¦ EGAHI          ¦  1    ¦  640 x 350¦    16 color¦  2
  EGA64   ¦ EGA64LO        ¦  0    ¦  640 x 200¦    16 color¦  1
          ¦ EGA64HI        ¦  1    ¦  640 x 350¦     4 color¦  1
  EGA-MONO¦ EGAMONOHI      ¦  3    ¦  640 x 350¦     2 color¦  1*
          ¦ EGAMONOHI      ¦  3    ¦  640 x 350¦     2 color¦  2**
  HERC    ¦ HERCMONOHI     ¦  0    ¦  720 x 348¦     2 color¦  2
  ATT400  ¦ ATT400C0       ¦  0    ¦  320 x 200¦    C0      ¦  1
          ¦ ATT400C1       ¦  1    ¦  320 x 200¦    C1      ¦  1
          ¦ ATT400C2       ¦  2    ¦  320 x 200¦    C2      ¦  1
          ¦ ATT400C3       ¦  3    ¦  320 x 200¦    C3      ¦  1
          ¦ ATT400MED      ¦  4    ¦  640 x 200¦     2 color¦  1
          ¦ ATT400HI       ¦  5    ¦  640 x 400¦     2 color¦  1
  VGA     ¦ VGALO          ¦  0    ¦  640 x 200¦    16 color¦  2
          ¦ VGAMED         ¦  1    ¦  640 x 350¦    16 color¦  2
          ¦ VGAHI          ¦  2    ¦  640 x 480¦    16 color¦  1
  PC3270  ¦ PC3270HI       ¦  0    ¦  720 x 350¦     2 color¦  1
  IBM8514 ¦ IBM8514HI      ¦  1    ¦ 1024 x 760¦   256 color¦
          ¦ IBM8514LO      ¦  0    ¦  640 x 480¦   256 color¦

"Páginas" se refiere al número de páginas que se pueden almacenar simultaneamente en pantalla.
"Paleta" indica el número de colores; en CGA los colores estaban prefijados en tres plaetas predefinidas:

  Núm. de ¦    Color asignado al valor de cada pixel
  paleta  ¦     1               2               3
----------+----------------------------------------------
    0     ¦ Verde claro ¦    Rojo claro    ¦  Amarillo
    1     ¦ Ciano claro ¦    Magenta claro ¦  Blanco
    2     ¦ Verde       ¦    Rojo          ¦  Marrón
    3     ¦ Ciano       ¦    Magenta       ¦  Gris claro

Prototipo: graphics.h
Ejemplo: -
Portabilidad: Turbo C y superiores (Turbo C++, Borland C++) bajo DOS.
Relacionados: initgraph

Índice global | Índice de la referencia


Curso de C. Referencia: printf.

Función: | Manda salida formateada al dispositivo de salida estándar (stdout).
Sintaxis: | int printf(const char *formato [, argumento,.]);
Prototipo: | stdio.h
Valor devuelto: | Devuelve el número de bytes que se han escrito en pantalla; en caso de error, devuelve EOF.
Observaciones: | El primer parámetro es una "cadena de formato". Dentro de esta cadena existirán ciertos "especificadores de formato", que se aplicarán cada uno de los argumentos. Debe haber la misma cantidad de especificadores de formato que de argumentos (si hay no hay suficientes argumentos, los resultados son impredecibles; si sobran argumentos, se desprecian). El texto que forma parte de la cadena de formato (y que no son especificadores) se muestra tal cual.
Ejemplo: | printf("La edad es de %d años, aproximadamente %d meses", edad, edad*12);
Portabilidad: | ANSI C, ANSI C++. Disponible en plataformas DOS, Win32, Unix, OS/2, etc. (En las aplicaciones en modo gráfico bajo Windows, la salida debería estar redirigida a un fichero o a otro dispositivo).
Relacionados: | cadenas de formato, cprintf, fprintf, sprintf, puts, putc, scanf

Índice global | Índice de la referencia


Curso de C. Referencia: putpixel.

Función: | Dibuja un punto en la pantalla gráfica
Sintaxis: | void far putpixel(int x, int y, int color);
Prototipo: | graphics.h
Valor devuelto: | Ninguno
Observaciones: | -
Ejemplo: | putpixel (320, 250, 15);
Portabilidad: | Turbo C y superiores (Turbo C++, Borland C++) bajo DOS.
Relacionados: | initgraph

Índice global | Índice de la referencia


Curso de C. Referencia: puts.

Función: | Envía una cadena de texto al dispositivo de salida estándar (stdout).
Sintaxis: | int puts(const char *texto);
Prototipo: | stdio.h
Valor devuelto: | Devuelve un valor no negativo; en caso de error, devuelve EOF.
Observaciones: | Después de escribir la cadena, avanza a la línea siguiente (envía un carácter de avance de línea).
Ejemplo: | puts("Este es un texto de ejemplo");
Portabilidad: | ANSI C. Disponible en plataformas DOS, Win32, Unix, OS/2, etc. (En las aplicaciones en modo gráfico bajo Windows, la salida debería estar redirigida a un fichero o a otro dispositivo).
Relacionados: | cputs, gets, printf, putc

Índice global | Índice de la referencia


Índice global

Curso de C. Mini-referencia de C y C++ - Cambios

Versión del curso | Cambios
2.27h | Segunda versión. Incluye: drivers gráficos, getch, getche, gotoxy, initgraph, kbhit, modos gráficos, putpixel.
2.26h | Primera versión de la referencia. Incluye: clrscr, cprintf, printf, puts

Índice global


Curso de C. Indice alfabético, de ejemplos, de tablas.

  • Indice alfabético.
  • Lista de ejemplos.
  • Lista de tablas.
  • Cambios entre versiones.


Curso de C. Índice alfabético.

En este apartado se van a resumir alfabéticamente los temas tratados, así como las palabras clave o funciones de biblioteca que se han visto, y el tema en el que han aparecido.

Los nombres que empiecen en mayúsculas indicarán que se trata de un tema genérico, mientras que las palabras reservadas o las funciones comenzarán con minúsculas:

- A -
Adivinar un número, fuentes
Agenda de direcciones, fuentes
Arboles binarios, tema 10.4
arc, ampliaciones
Arrays en C, tema 2
Arreglos, tema 2
ASCII, otros temas
asm (orden), ampliaciones
ASM (lenguaje) desde C, ampliaciones
Asignación de valores a variables, tema 2
Asignaciones abreviadas, tema 5
Asignaciones múltiples, tema 5

- B -
BGI, ampliaciones
bloques, tema 1
Booleanos en C, tema 2
break, tema 6 y tema 7.5d
Bucles, tema 7

- C -
Cadena de formato (printf), tema 4
Cadenas de texto, tema 2 y tema 12
Campos de bits, tema 2.7b
case, tema 6
char, tema 2
circle, ampliaciones
closegraph, ampliaciones
clreol, ampliaciones
clrscr, ampliaciones
Códigos de control, tema 4
Colas, tema 10.4
Colores en modo texto, ampliaciones
Comentarios, tema 1
Comillas dobles ( " ), tema 2
Comillas simples ( ' ), tema 2
Compact (modelo de memoria), ampliaciones
Comparaciones, tema 5
Comparar dos ficheros, fuentes
Concatenar cadenas, tema 5 y tema 12
Condiciones, tema 6
conio.h, ampliaciones
const, tema 8
Constantes, tema 8
continue, tema 7.5c
Conversión de tipos, tema 4

- D -
Declaración de variables, tema 2
Decremento, tema 5
default, tema 6
define, tema 8
Definición de tipos, tema 8
Desplazamiento en un segmento, ampliaciones.
detectgraph, ampliaciones
dfree, ampliaciones
dir.h, ampliaciones.
Directorios desde Turbo C, ampliaciones.
do, tema 7
Dos desde Turbo C, ampliaciones.
dos.h, ampliaciones.
DPMI (DOS en modo protegido), ampliaciones

- E -
else, tema 6
Ensamblador desde C, ampliaciones
Entrada/Salida básica, tema 4
enum, tema 8
Escape (códigos de escape), tema 4
Espacio en disco, ampliaciones

- F -
fclose, tema 11
fecha y hora de un fichero, ampliaciones
fecha y hora del sistema, ampliaciones
feof, tema 11
fgetc, tema 11
fgets, tema 11
Ficheros, tema 11
Figuras que se mueven por la pantalla, fuentes
FILE, tema 11
findfirst, ampliaciones
findnext, ampliaciones
float, tema 2
floodfill, ampliaciones
fopen, tema 11
for, tema 7
Formato (printf y scanf), tema 4
fprintf, tema 11
fputc, tema 11
fputs, tema 11
fread, tema 11
free, tema 10
fscanf, tema 11
fseek, tema 11
ftell, tema 11
Funciones, tema 3 y tema 9
Funciones matemáticas (representar gráficamente, fuente de ejemplo), fuentes
fwrite, tema 11

- G -
Generalidades, tema 1
getch, ampliaciones
getchar, tema 4 y tema 6
getche, ampliaciones
getdate, ampliaciones.
getpalette, ampliaciones
getpixel, ampliaciones
gets, tema 4
gettime, ampliaciones.
getx, ampliaciones
gety, ampliaciones
goto, tema 7.5e
gotoxy, ampliaciones
Gráficos desde Turbo C, ampliaciones
Gráficos desde Watcom C++, ampliaciones
Gráficos accediendo a memoria, ampliaciones
graph.h, ampliaciones
graphics.h, ampliaciones

- H -
.h (ficheros de cabecera), tema 3
Huge (modelo de memoria), ampliaciones

- I -
Identificadores, tema 2
if, tema 6
Imágenes que se mueven, , fuentes
Impresora desde C, ampliaciones
include, tema 1 y tema 3
Incremento, tema 5
initgraph, ampliaciones
int, tema 2
int86, ampliaciones
Interrupciones del Dos desde Turbo C, ampliaciones

- K -
kbhit, ampliaciones

- L -
Large (modelo de memoria), ampliaciones
line, ampliaciones
Línea de comandos, ampliaciones
Listas dinámicas simples, tema 10.3
Listas dobles, tema 10.4
long, tema 2

- M -
main(), tema 1 y tema 3
malloc, tema 10
Matrices, tema 2
MAXINT, tema 2
MAXLONG, tema 2
Mayúsculas y minúsculas, tema 2
Medium (modelo de memoria), ampliaciones
Menús (fuente de ejemplo), fuentes
MK_FP, ampliaciones
Modelos de memoria, ampliaciones
Modificadores de los tipos base, tema 2
Modo protegido de MsDos, ampliaciones
Modo real de MsDos, ampliaciones
Modo texto (pantalla), ampliaciones
Mouse desde MsDos, ampliaciones
move, ampliaciones
Mover figuras por la pantalla, fuentes

- N -
NULL, tema 10

- O -
Operaciones matemáticas, tema 5
Operaciones lógicas, tema 5
Operaciones entre bits, tema 5
Operador condicional, tema 6
outtext, ampliaciones
outtextxy, ampliaciones

- P -
Pantalla en modo texto, ampliaciones
Pantalla gráfica desde Turbo C, ampliaciones
Pantalla gráfica accediendo a memoria, ampliaciones
Parámetros de una función, tema 9
Parámetros de un programa, ampliaciones
Pilas, tema 10.4
printf, tema 1, tema 4 y referencia
Protegido (modo de trabajo de MsDos), ampliaciones
Punteros, tema 9 y tema 10
putchar, tema 4
putpixel, ampliaciones
puts, tema 4

- R -
Ratón desde MsDos, ampliaciones
Real (modo de trabajo de MsDos), ampliaciones
rectangle, ampliaciones
Recursividad, tema 9
Referencia (paso de parámetros), tema 9
Registros en C, tema 2
Representar funciones matemáticas (fuente de ejemplo), fuentes
Reservar memoria, tema 10
return, tema 9

- S -
scanf, tema 2 y tema 4
Secuencias de escape, tema 4
Segmentos, ampliaciones.
Sentencias compuestas, tema 6
short, tema 2
signed, tema 2
Sintaxis del C, tema 1
sizeof, tema 10
Small (modelo de memoria), ampliaciones
Sprites (figuras que se mueven), fuentes
stdio.h, tema 1
strcat, tema 12
strcmp, tema 12
strcpy, tema 12
string.h, tema 12
strncpy, tema 12
struct, tema 2
strupr, tema 12
SuperVGA, ampliaciones
switch, tema 6

- T -
Teclado desde C, ampliaciones
textattr, ampliaciones
textbackground, ampliaciones
textcolor, ampliaciones
textmode, ampliaciones
Texto en modo gráfico (outtext), ampliaciones
Tiny (modelo de memoria), ampliaciones
Tipos (definición), tema 8
Typecast, tema 4
typedef, tema 8

- U -
union, tema 2.7b
unsigned, tema 2

- V -
Valor (paso de parámetros), tema 9
Variables, tema 2
Variables locales y globales, tema 9
Variables dinámicas y estáticas, tema 10
Vectores, tema 2
VESA, ampliaciones
void, tema 3
volatile, tema 8

- W -
wherex, ampliaciones
wherey, ampliaciones
while, tema 7
window, ampliaciones

- Otros -
#include, tema 1 y tema 3
#define, tema 8
/* y */ (comentarios), tema 1
{ y (bloques, sentencias compuestas), tema 1 y tema 6
() (funciones), tema 1
; (fin de orden), tema 1
[] (arrays: matrices o vectores), tema 2
& (dirección de memoria), tema 2
'' y "" (comillas), tema 2
<> y "" (include), tema 3
\ (secuencias de escape), tema 4
+ - / * % (operadores), tema 5
++ -- (incremento y decremento), tema 5
+= -= *= /= %=, tema 5
== != < > <= >= (comparaciones), tema 5
&& || ! (y,o,no), tema 5
& ! ^ << >> (operadores de bits), tema 5
? : (operador condicional), tema 6
* (puntero) y & (dirección), tema 9 y tema 10


Curso de C. Índice de ejemplos.

Total de ejemplos en esta versión: 82.

Los ejemplos básicos (51) que acompañan a este curso son:

01: Programa elemental (Escribe "Hola", tema 1)
02: Introd. a variables (suma de dos números, tema 2)
03: Inicialización de variables (casi igual que el anterior, tema 2.2)
04: Escribir más de una variable (algo más de detalle sobre printf, tema 2.3)
05: Leer valores para variables (ejemplo de scanf, tema 2.4)
06: Primer ejemplo de arrays (suma de 5 números enteros, tema 2.5)
07: Inicialización de arrays (basado en anterior, tema 2.6)
08: Registros (struct, tema 2.7)
09: Introducción a las cadenas de texto (inicializar, leer y mostrar, tema 2.8)
10: "main" como int, tema 3
11: "main" como void, tema 3.2
12: Formatos con "printf" (varios ejemplos, tema 4.2)
13: "gets" y "puts" (cadenas de texto, tema 4.4)
14: Operaciones con números enteros y reales, tema 5
14b: Operaciones de bits con números enteros, tema 5.3
15: "if" elemental, tema 6
16: "if" abreviado, tema 6.2
17: "if" y sentencias compuestas, tema 6.3
18: "if" y "else", tema 6.4
19: "if..else" encadenados, tema 6.5
20: "if" incorrecto, tema 6.6
21: Operador condicional ?, tema 6.7
22: Problemas de "getchar", tema 6.8
23: Uso de "switch", tema 6.9
24: Escribe del 1 al 10, con "for", tema 7
25: Escribe las tablas de multiplicar del 1 al 5, con "for", tema 7.2
26: Tablas de multiplicar del 1 al 5, con "for". Deja espacios intermedios. tema 7.3
27: Escribe las letras de la 'a' a la 'z', con "for",tema 7.4
28: Escribe las letras de la 'z' a la 'a', (una sí y otra no), con "for" tema 7.5
28b: Ejemplo de "for" controlado por 2 variables, tema 7.5b
28c: Saltar pasos de un "for" con "continue", tema 7.5c
28d: Salir de un "for" con "break", tema 7.5d
28e: Salir de varios "for" con "goto", tema 7.5e
29: Escribe el triple de los números tecleados, con "while", tema 7.6
30: Clave de acceso numérica, con "do..while", tema 7.7
31: Clave de acceso numérica, con "do..while" y "const", tema 8
32: Clave de acceso numérica, con "do..while" y "#define", tema 8.2
33: Clave de acceso numérica retocada, con typedef y #define, tema 8.3
34: Uso de "typedef" con "struct", tema 8.4
35: Función "potencia", tema 9
35b: Función "potencia"; función "main", tema 9.2
36: Función "hola", tema 9.3
37: Función "modif1" Parámetros por valor, tema 9.4
38: Función "modif2" Parámetros por ref, tema 9.5
39: "factorial" recursivo, tema 9.6
40: Introducción a los punteros, tema 10
41: Punteros y arrays, tema 10.2
42: Ficheros de texto (1), tema 11
43: Ficheros de texto (2), tema 11.2
44: Ficheros sin tipo: Copiador elemental, tema 11.4
45: Cadenas de texto, tema 12

Otros sin numerar entre los temas básicos (3):

- Primer ejemplo de listas simples.
- Segundo ejemplo de listas simples.
- Arboles binarios de búsqueda.

Entre las ampliaciones del curso (15):

- Pantalla de texto en Turbo C.
- Pantalla de texto en Symantec C++.
- "conio.h" para Symantec C++.
- Pantalla de texto en Symantec C++ al estilo de Turbo C.
- Pantalla de texto en Linux.
- Pantalla gráfica en Turbo C.
- Programa elemental de dibujo con Turbo C.
- Pantalla gráfica accediendo a memoria.
- Servicios del DOS en Turbo C.
- Interrupciones del DOS en Turbo C.
- Contenido de un directorio en Turbo C.
- Imprimir desde C.
- Prototipos de funciones.
- Ver la tabla ASCII.
- Acceder al lenguaje ensamblador en C.

Y como programas algo más completos (7):

- Adivinar un número.
- Adivinar un número (2).
- Mostrar un fichero de texto.
- Comparar dos ficheros.
- Figuras que se mueven por la pantalla.
- Agenda de direcciones.
- Sistema de menús para MsDos.

Ejemplos de C++ (6):

- Clase "pantalla de texto", en un solo fuente.

- Clase "pantalla de texto", en dos fuentes: cabecera.
- Clase "pantalla de texto", en dos fuentes: detalle.
- Clase "pantalla de texto", en dos fuentes: ejemplo que la usa.

- Clase "pantalla de texto mejorada", cabecera.
- Clase "pantalla de texto mejorada", detalle.
- Clase "pantalla de texto mejorada", ejemplo que la usa.

- Clase "ventana de texto", cabecera.
- Clase "ventana de texto", desarrollo.
- Clase "ventana de texto", ejemplo que la usa.

- Clase "pantalla de texto mejorada" para Symantec C++.

- Entrada y salida estándar en C++.


Curso de C. Índice de tablas.

Las tablas incluidas en este curso son:

1.- Códigos de formato (tema 4).
2.- Códigos de control (tema 4.3).
3.- Modos de apertura de un fichero (tema 11.3).


Curso de C. Notas finales.

Sólo algunos comentarios para quien haya conseguido llegar hasta aquí:

Este curso tiene más de 80 fuentes de ejemplo, y el texto del curso ocupa más de 12.000 líneas y más de 400K. Pero...
--=== Esto no es "el C" ===--

Me refiero a que es no es "todo el C", sino sólo una ínfima parte: hay más ordenes de entrada y salida, más para manejo de ficheros, más posibilidades de punteros, etc. Por ejemplo, si alguien busca la ayuda sobre "malloc" en Turbo C++ 1.01, le aparecerán como temas relacionados, entre otros:

    allocmem   calloc   farcalloc   farmalloc   realloc

Igualmente, al mirar la ayuda sobre "printf" aparecen referencias a

     cprintf    fprintf   sprintf   vprintf   vsprintf

Es decir, es muy frecuente que haya muchas "variantes" de una orden dada. En el caso de printf puede ser para escribir en color, o en un fichero, o mandar la salida a una cadena de texto...

Lo que pretendo no es describir todas y cada una de las órdenes que uno se pueda encontrar delante de un compilador de C (muchas de las cuales ni siquiera existirán en otros compiladores), sino quitar un poco el miedo, dar una base, y abrir una puerta para quien quiera investigar más.

Nada más. Espero que este curso le haya resultado útil, al menos para coger una base y perder un poco el miedo.

N.


Curso de C. ¿Por qué ha cambiado el formato?

Las primeras versiones del curso fueron textos "separados", que se distribuían junto con un lector (para MsDos). Este lector fue evolucionando y en sus últimas versiones incluía enlaces entre temas relacionados, texto en varios colores, permitía exportar los ejemplos para poder probarlos sin tener que teclearlos, etc.

Pero había una asignatura pendiente: poder incluir imágenes, e incluso distintos tipos de letra. Por ello, cuando Windows 95 ya estaba suficientemente asentado (e incluso Windows 98 empezaba a aparecer), decidí crear una versión en formato Ayuda de Windows. A mí me permitía seguir con la estructura "hipertexto" de las últimas versiones de MsDos, pero tamibén incluir imágenes, usar distintos tipos de letra, mayor cantidad de colores, etc., a la vez que a los usuarios les permitía buscar textos, imprimir con más facilidad, etc.

Pero los tiempos cambian. Hoy en día (octubre de 2001) me resulta incómodo mantener la versión en formato Ayuda de Windows, por extrañas incompatibilidades entre las distintas herramientas de Microsoft (!!!!), pero hay un claro sustituto: el formato HTML empleado en las páginas Web. Este formato me permite elegir entre distintas herramientas para crear y modificar los temas del curso, y hacer los cambios con más rapidez y menos posibilidades de error. Además, este formato se puede consultar desde casi cualquier ordenador, con casi cualquier sistema operativo.

Pero todo es mejorable: la versión 1.60h de este curso ocupa unos 800 Kb y está integrada por casi 100 ficheros, entre HTML e imágenes... a pesar de ser sólo una "versión de transición", que voy a ir ampliando rápidamente. Eso resulta incómodo de manejar para quien quiere tener todo el curso en su disco duro. Por eso, también existe una versión pensada para quien quiere descargar el curso, en vez de consultarlo en Internet. Se trata de una versión en formato "Ayuda de Windows 98" (CHM). Esta versión es muy fácil de elaborar para mí a partir de la HTML, permite que cualquiera pueda descargar (y consultar) un único fichero de menos 600 Kb, en vez de pelear con cerca de 100 ficheros con un total de unos 800 Kb, y además permite imprimir de una forma muy rápida varios temas que estén relacionados entre sí (eso me supone a mí más trabajo, pero tampoco es ningún gran problema), de modo que si alguien quiere imprimir los temas básicos (por ejemplo), no necesita andar de página en página imprimiendo una tras otra. Si quiere probarlo, sólo tiene que señalar una carpeta de la mitad izquierda de la ventana (pulse el icono "Mostrar" si no ves esta parte izquierda) y entonces pulsar el botón "Imprimir". El ordenador le preguntará si quiere imprimir sólo el tema que se está viendo o todo lo que está dentro de esa carpeta.

Eso sí, un aviso: si quiere imprimir todo el curso, prepara bastante papel. La versión 1.70 ya ocupa más de 200 páginas... y todavía quedan muchas cosas por añadir...

En fin, espero que considere que mis decisiones en cada momento no han sido totalmente desacertadas.


Curso de C. Cambios entre versiones.

Las próximas mejoras previstas son:
  • Añadir nuevos temas y ampliar el contenido de los ya existentes. Entre los próximos temas previstos están:
    • Rutinas de ordenación.
    • Detallar algunas peculiaridades más de la programación bajo Unix/Linux.
    • Diferencias al acceder a funciones del DOS, gráficas, etc. con otros compiladores que no sean de la familia de Turbo C.
    • Ampliar la parte de C++, especialmente con más ejemplos y hablar más sobre el nuevo estándar de C++.
    • Ampliar la parte dedicada a la programación bajo Windows con C++ Builder.
  • Hacer que los índices realmente salten a los temas correspondientes y ampliar el índice alfabético.
  • Ampliar la "referencia" del lenguaje C.
Los cambios en la versión de libre distribución del curso han sido (hasta ahora):

(Nota: también puedes consultar los cambios entre versiones ampliadas)

  • 2.27h, de 04-01-2003. Volvemos tras un largo paréntesis:
    • Ampliado el tema sobre la pantalla gráfica, para hablar de cómo crear gráficos con DJGPP empleando dos de las bibliotecas de funciones más extendidas: GRX y Allegro.
    • Ampliado el apartado de compiladores, con nociones sobre el compilador MinGW.
    • Ampliado el índice alfabético (45 entradas más). Aumentado el número de enlaces que contiene dicho índice (45 en las palabras nuevas, 39 en palabras ya existentes).
    • Ampliada la referencia de funciones disponibles en C y C++ (8 nuevos apartados; total: 12 - nuevos: drivers gráficos, getch, getche, gotoxy, initgraph, kbhit, modos gráficos, putpixel).
    • Reducido el espacio ocupado por algunos de los fuentes que se incluyen en el curso (reducción total: 52 Kb).
    • Incluida una versión más moderna del diccionario básico de informática (1.02, con 718 definiciones).
    • Corregida una pequeña errata en el tema 2: el espacio ocupada por los números reales.
    • Total de esta versión: 235 ficheros y 2.274 Kb en formato HTML; 947 Kb y 373 páginas impresas en formato CHM.
  • 2.26h, de 19-03-2002
    • Ampliado el apartado de "otros temas complementarios", con nociones sobre el sistema de numeración octal y el hexadecimal.
    • Ampliado el tema sobre la pantalla gráfica, para hablar de cómo acceder desde Watcom C++.
    • Reescrita parte de la ampliación sobre Modelos de Memoria.
    • Mejorada la apariencia de las tablas del tema 5.
    • Algo ampliado el índice alfabético (25 entradas más). Desde el índice alfabético se empieza a poder saltar a los temas que se mencionan (enlaces incluidos en esta versión: 82).
    • Comenzado un apartado con una breve referencia de funciones disponibles en C y C++ (temas incluidos en esta versión: 4 - clrscr, cprintf, printf, puts).
    • La lista de cambios (ésta) incluye enlaces a los temas que se mencionen (a partir de ahora).
    • La lista de contenido (versión CHM) muestra más detalle sobre los temas básicos.
    • Los cambios a partir de la versión 2.25 se detallan también en la Web, para que cada uno pueda juzgar si le parecen interesantes, antes de descargar todo el curso.
    • Total de esta versión: 208 ficheros y 1.876 Kb en formato HTML; 830 Kb y 364 páginas impresas en formato CHM.
  • 2.25h, de 06-01-2002
    • Añadido en el apartado de ampliaciones, el tema sobre cómo incluir ensamblador en los fuentes en C (que sólo estaba en la versión ampliada).
    • Corregida una pequeña errata en el ejemplo 35.
    • Añadidos, en la introducción a C++, dos apartados ("Más sobre entrada y salida", "Ficheros", que sólo estaban en la versión ampliada). Corregido un enlace incorrecto en las páginas de este tema.
    • Añadido, en el apartado sobre C++ Builder, el décimo tema ("Introducción a los gráficos") y las soluciones a los dos ejercicios propuestos (que sólo estaban en la versión ampliada).
    • Inaugurado un apartado de "otros temas complementarios", con nociones sobre unidades de medida usadas en informática, sobre el sistema binario y un diccionario básico de informática.
    • Corregido algún enlace incorrecto.
    • Añadida también la lista de los cambios que ha habido entre versiones ampliadas.
    • Total de esta versión: 195 ficheros y 1.789 Kb en formato HTML; 820 Kb y 349 páginas impresas en formato CHM.
  • 1.95h, de 20-12-01
    • Añadido en el apartado de ampliaciones, el tema sobre el código ASCII (que sólo estaba en la versión ampliada), y ampliado ligeramente este tema.
    • Añadido, en la introducción a C++, un apartado ("Sobrecarga de operadores. Funciones amigas", que sólo estaba en la versión ampliada).
    • Añadido, en el apartado sobre C++ Builder, el noveno tema ("Varias ventanas nuestras", que sólo estaba en la versión ampliada).
    • Añadido, en el apartado de "fuentes de ejemplo", un ejemplo de cómo hacer una agenda de direcciones., que sólo estaba en la versión ampliada.
    • Total de esta versión: 145 ficheros y 1.381 Kb en formato HTML; 525 Kb y 311 páginas impresas en formato CHM.
  • 1.90h, de 14-12-01
    • Añadida información sobre dónde conseguir y cómo instalar Turbo C 2.01.
    • Añadido en el apartado de ampliaciones, el tema sobre las funciones matemáticas que incluye el lenguaje C (que sólo estaba en la versión ampliada).
    • Añadido, en la introducción a C++, un apartado ("Modificaciones para otro compilador", que sólo estaba en la versión ampliada).
    • Añadido, en el apartado sobre C++ Builder, el octavo tema ("Ventanas estándar de Windows", que sólo estaba en la versión ampliada).
    • Añadido, en el apartado de "fuentes de ejemplo", el ejemplo de cómo comparar ficheros, que sólo estaba en la versión ampliada.
    • Ligeramente reescrito el tema 1 ("Generalidades del C"), buscando mayor legibilidad.
    • Total de esta versión: 136 ficheros y 1.241 Kb en formato HTML; 455 Kb y 283 páginas impresas en formato CHM.
  • 1.85h, de 07-12-01
    • Añadida información sobre dónde conseguir y cómo instalar el compilador Watcom C++ 11.0c.
    • Añadido en el apartado de ampliaciones, el tema sobre cómo crear gráficos accediendo a la memoria de pantalla (que sólo estaba en la versión ampliada).
    • Añadido, en la introducción a C++, un apartado ("Dos objetos que interaccionan", que sólo estaba en la versión ampliada).
    • Añadido, en el apartado sobre C++ Builder, el séptimo tema ("Más detalles sobre botones. Otros tipos de botones", que sólo estaba en la versión ampliada).
    • Añadido, en el apartado de "fuentes de ejemplo", el ejemplo de cómo crear imágenes que se mueven en pantalla, que sólo estaba en la versión ampliada.
    • Total de esta versión: 126 ficheros y 1.194 Kb en formato HTML; 436 Kb y 272 páginas impresas en formato CHM.
  • 1.80h, de 28-11-01
      • Añadida información sobre dónde conseguir y cómo instalar el compilador de C y C++ DJGPP 2.03.
      • Añadida información sobre dónde conseguir y cómo instalar el compilador de C y C++ llamado Digital Mars 8.22 (que parece una evolución de Symantec C++, gratuita).
      • Añadido en el apartado de ampliaciones, el tema sobre cómo leer la línea de comandos (que sólo estaba en la versión ampliada).
      • Añadidos, en la introducción a C++, dos apartados ("Constructores y destructores" y "Herencia", que sólo estaban en la versión ampliada).
      • Añadido, en el apartado sobre C++ Builder, el sexto tema ("Casillas de selección", que sólo estaba en la versión ampliada).
      • Añadido, en el apartado de "fuentes de ejemplo", el ejemplo de cómo crear un "sistema de menús" para MsDos, que sólo estaba en la versión ampliada.
      • Total de esta versión: 121 ficheros y 1.125 Kb en formato HTML; 397 Kb y 254 páginas impresas en formato CHM.
    • 1.75h, de 13-11-01
      • Ampliado el tema sobre la pantalla en modo gráfico con Turbo C, para que contenga todo lo que ya estaba en la versión ampliada.
      • Añadidos, en el apartado de ampliaciones, los temas sobre cómo crear programas a partir de varios fuentes y sobre modelos de memoria en MsDos (que sólo estaban en la versión ampliada).
      • Añadido, en el apartado sobre C++ Builder, el quinto tema ("Menús", que sólo estaba en la versión ampliada).
      • Añadido, en el apartado de "fuentes de ejemplo", los temas sobre cómo "mostrar ficheros de texto" y sobre cómo "representar gráficamente funciones matemáticas", que sólo estaban en la versión ampliada,
      • Faltaba un enlace para acceder correctamente al fuente de ejemplo que muestra cómo adivinar números; corregido.
      • Total de esta versión: 110 ficheros y 1.040 Kb en formato HTML; 377 Kb y 235 páginas impresas en formato CHM.
    • 1.70h, de 29-10-01
      • Ampliado el tema sobre variables dinámicas (10), con ejemplos de cómo crear listas enlazadas y árboles binarios en C, algo que sólo estaba en la versión ampliada.
      • Añadidos, en el apartado de ampliaciones, los temas sobre cómo acceder a la pantalla en modo texto con Symantec C++ y bajo Linux (que sólo estaban en la versión ampliada).
      • Añadido, en el apartado sobre C++ Builder, el cuarto tema ("Cuadros de texto y listas", que sólo estaba en la versión ampliada), y corregida una pequeña errata en él.
      • Cambiada la apariencia de la lista de cambios (ésta), para que se vea mejor con cualquier tipo de pantalla.
      • Total de esta versión: 93 ficheros y 891 Kb en formato HTML; 302 Kb y 209 páginas impresas en formato CHM.
    • 1.65h, de 19-10-01
      • Añadido, en el apartado de "fuentes de ejemplo", el tema sobre cómo "adivinar números", que sólo estaba en la versión ampliada.
      • Añadido, en el apartado de ampliaciones, el tema sobre cómo usar los servicios del DOS desde Turbo C (que sólo estaba en la versión ampliada) y corregida una pequeña errata en él.
      • Añadido, en el apartado sobre C++ Builder, el tercer tema ("el inspector de objetos", que sólo estaba en la versión ampliada).
      • En la versión 1.60 CHM no se aparecía el apartado sobre C++ en el índice de la parte izquierda de la pantalla; corregido.
      • Total de esta versión: 84 ficheros y 824 Kb en formato HTML; 283 Kb y 188 páginas impresas en formato CHM.
    • 1.60h, de 12-10-01
      • Primera versión en formato CHM (Ayuda de Windows 98), con todo el contenido que ya tenía la versión 1.59 en formato "Ayuda de Windows" (HLP) y además:
      • Añadido, en el apartado de ampliaciones, el tema sobre cómo acceder a la impresora desde C (que sólo estaba en la versión ampliada).
      • Añadido, en el apartado de ampliaciones, el tema sobre cómo usar el ratón en MsDos (que sólo estaba en la versión ampliada).
      • Añadido, en el apartado sobre C++ Builder, el segundo tema ("comprendiendo y mejorando lo hecho", que sólo estaba en la versión ampliada).
      • Unidas algunas páginas que antes estaban separadas, para que el curso se pueda imprimir con más facilidad.
      • Total de esta versión: 77 ficheros y 751 Kb en formato HTML; 245 Kb y 164 páginas impresas en formato CHM.
    • 1.59w, 19-03-01
      • El apartado 6 de la introducción a C++ (otras diferencias con C) está ahora disponible en la versión de libre distribución (ésta).
      • Ampliado el tema sobre la salida en pantalla desde C++, indicando como tener más control sobre el formato de salida en pantalla. (en la versión ampliada).
      • Añadido un apartado sobre el manejo de ficheros desde C++ (en la versión ampliada).
      • Ampliado el tema sobre “gráficos”, con un ejemplo de cómo representar funciones matemáticas en pantalla (en la versión ampliada).
      • Añadida información sobre el compilador de C y C++ que se incluye con Linux (y con otras versiones de Unix).
      • Añadida alguna imagen extra para mayor claridad (en el ejemplo del “sistema de menús” -versión ampliada- y en el primer ejemplo sobre gráficos).
      • Añadidos botones >> y << que permiten moverse con mayor facilidad entre los distintos temas (de momento sólo utilizables en los temas básicos, no en las ampliaciones) (en la versión ampliada).
    • 1.58w, 21-08-00
      • Añadida información sobre cómo conseguir el compilador Turbo C++ Second Edition.
      • Ampliado el tema sobre “cadenas de texto para hablar sobre “sprintf” y “sscanf”.
      • Añadido un tema sobre el nuevo estándar de C++ que sí permite cadenas (strings) como parte del lenguaje.
      • Ampliadas las definiciones de informática general que se pueden consultar: incluida laversión 0.60 de mi diccionario básico (536 definiciones), aunque por ahora sólo algunas están accesibles desde el Índice.
    • 1.57w, 09-07-00
      • Cambiada mi dirección de correo electrónico y mi página Web: e-mail “ncabanes@pobox.com”, Web “http://www.pobox.com/users/ncabanes”
      • Añadida información sobre cómo conseguir e instalar la versión “Free CommandLine Tools” de Borland C++ 5.5.
      • Añadidos algún tema más de la versión ampliada, que mucha gente me había pedido: un apartado sobre la pantalla de texto en Turbo C++, otro sobre la creación de gráficos con este mismo compilador y dos apartados de la introducción a C++.
    • 1.56w, 25-06-00
      • Revisión completa para eliminar posibles erratas (por ejemplo, faltaba alguna llave de cierre en alguno de los ejemplos, que podía hacer que no funcionase correctamente) y para ampliar o reescribir algunos temas (por ejemplo, el tema 1).
      • Sincronizada la versión gratis con la ampliada, para que las erratas que se corrijan en la “grande” repercutan también en la “pequeña” (aunque la “grande” seguirá teniendo un mayor contenido, claro).
      • Añadida la posibilidad de buscar una cierta palabra, pulsando el botón Índice. Estas palabras en las que se puede buscar incluyen algunas tomadas de mi diccionario básico de informática. (Estas dos opciones ya estaban disponibles. anteriomente en la versión ampliada).
      • La mayoría de las opciones que están disponibles en la versión ampliada pero no en ésta, se indican con (*), para facilitar el manejo.
    • 1.55w, 25-07-99
      • Incluye el primer tema de la introducción a C++ Builder, que está disponible por completo en la versión registrada.
    • 1.54w, 21-03-99
      • Creada una versión del curso con lector para Windows (en formato “Ayuda de Windows”).
      • La lista de ejemplos permite saltar a la lección en la que se encuentra.
      • Corregida alguna pequeña errata de poca importancia.
    • 1.53, 12-03-98
      • Corregido un error del lector que hacía que los ejemplos se exportasen con un símbolo raro al comienzo de cada línea.
    • 1.52, 19-01-98
      • Añadido un tema sobre los compiladores que he empleado para probar los ejemplos: dónde conseguirlos y como instalarlos.
      • Corregido un error en el ejemplo 17 y alguna errata más.
      • Otras ligeras mejoras internas al lector.
    • 1.51, 27-10-97
      • Versión "de mantenimiento": corregidas erratas (p.ej.: modidicador->modificador, concer-> conocer, etc).
      • Añadido un apartado sobre los modelos de memoria en el PC bajo MsDos al final del tema 12.
    • 1.50, 7-04-97
      • Primera versión hipertexto. Texto casi igual que la 1.00. Se puede manejar con ratón.
      • Se pueden exportar los ejemplos y las tablas.
    • 1.00, 20-12-96
      • Versión inicial.


Curso de Pascal - Nacho Cabanes - Ultima version

Curso de C. Conseguir la última versión.

Conseguir la última versión de este curso es fácil. Intentaré mantenerla siempre en mi página Web. Ahora mismo, mi página Web está en la dirección:

  http://www.pobox.com/users/ncabanes

Es fácil que se mantenga bastante tiempo en esta dirección.

En cualquier caso, si no apareciera esta página, haz una búsqueda en Terra, Ozú, Wanadoo o algún otro de los buscadores de habla hispana más conocidos y por ahí apareceré yo...


Curso de C. ¿Cómo se puede colaborar?

Este curso se puede distribuir libremente, siempre y cuando se modifique. Si se utiliza como base para algún otro curso, artículo, trabajo, etc., al menos sí rogaría que citase mi nombre, como forma de valorar el tiempo que le he dedicado.

Pero si desea colaborar "más activamente", hay varias formas:

  • Avisarme de cualquier error que descubra.
  • Sugerir temas que no se hayan tratado, o que no queden claros.
  • Enviar temas nuevos que yo pueda incorporar al curso.
  • Incluso económicamente, ¿por qué no? Una contribución simbólica de sólo 5 Euros puede hacer mucha ilusión. :-) Hoy en día, afortunadamente, no la necesito, tengo un trabajo "decente" (o casi), pero dicen que a nadie le amarga un dulce... :-)
En cualquier caso, es fácil contactar conmigo.


Curso de Pascal - Nacho Cabanes - Sobre el autor

Curso de C. Sobre el autor.

Si quieres contactar conmigo, la mejor forma es por correo electrónico, en la dirección:

Internet: ncabanes@pobox.com

o puedes acceder a mi página Web en

www.pobox.com/users/ncabanes

Si no tienes acceso a Internet, puedes escribirme por correo ordinario a mi apartado postal (si se trata de alguna consulta, por favor incluye sobre y sellos para la respuesta):

José Ignacio Cabanes
Apartado de Correos 5234
03080 Alicante

Por si lo que quieres es saber más sobre mí, aquí va un mini-resumen de mi vida:

  • Soy Ingeniero Técnico en Informática de Sistemas.
  • Actualmente sigo estudiando... cuando mi poco tiempo libre lo permite. Realizo tercer curso de Ingeniería Técnica en Informática de Gestión, por la Universidad Nacional de Educación a Distancia.
  • También tengo estudios parciales de Ingeniería Industrial, que espero completar algún día (sin prisas).
  • He trabajado como técnico de sistemas informáticos, como analista/programador y como profesor de informática... entre otros.
  • En estos momentos (septiembre 2001) soy profesor de informática en un Instituto de Educación Secundaria, en Alicante.
  • Conocimientos de programación: Es un dato que se queda anticuado con frecuencia :-) pero como muchos me lo han preguntado: he programado en C y C++ (bajo Dos, Windows y Unix), Pascal (Turbo Pascal, Delphi y otros), Java, Basic (GwBasic, QBasic, Visual Basic y otros), Ensamblador (Z80, 80x86, 8051, Mips R2000), Modula-2, Prolog, XBase (dBase, Clipper, FoxPro), Logo... y quizá olvide algún otro. También tengo conocimientos de programación de autómatas, de robots y de máquinas de control numérico.
  • Otros conocimientos de informática: Además de los que se presuponen en un ingeniero técnico en informáticas (redes, bases de datos, sistemas operativos, algorítmica, programación orientada a objetos, etc), también tengo un nivel alto de ofimática, internet, CAD y un nivel bastante aceptable de infografía (3dStudio, 3dStudio Max, PovRay y otros).


Curso de C. Otros temas complementarios.

En este apartado incluiré otros temas que no sean estrictamente parte del lenguaje C (ni de C++), pero que considere que pueden resultar útiles para los que quieran aprender a programar en estos (y otros) lenguajes.

Entre ellos habrá temas sobre el sistema binario y el hexadecimal, interrupciones de MsDos, un diccionario básico, y cualquier otro que pueda ir surgiendo.

Por ahora están disponibles los siguientes:

  • Unidades de medida: bytes, kilobytes, megabytes, gugabytes.
  • Unidades de medida (2): bits.
  • El sistema binario.
  • El sistema octal y el hexadecimal.
  • Diccionario básico de informática.


Curso de C. Otros temas complementarios: Unidades de medida.

Bits, bytes, kilobytes, megabytes.

En informática, la unidad básica de información es el byte. En la práctica, podemos pensar que un byte es el equivalente a una letra. Si un cierto texto está formado por 2000 letras, podemos esperar que ocupe unos 2000 bytes de espacio en nuestro disco.

Eso sí, suele ocurrir que realmente un texto de 2000 letras que se guarde en el ordenador ocupe más de 2000 bytes, porque se suele incluir información adicional sobre los tipos de letra que se han utilizado, cursivas, negritas, márgenes y formato de página, etc.

Un byte se queda corto a la hora de manejar textos o datos algo más largos, con lo que se recurre a un múltiplo suyo, el kilobyte, que se suele abreviar Kb o K.

En teoría, el prefijo kilo querría decir “mil”, luego un kilobyte debería ser 1000 bytes, pero en los ordenadores conviene buscar por comodidad una potencia de 2 (para saber por qué, puedes ver el tema sobre los bits, un poco más adelante), por lo que se usa 210 =1024. Así, la equivalencia exacta es 1 K = 1024 bytes.

Los K eran unidades típicas para medir la memoria de ordenadores: 640 K ha sido mucho tiempo la memoria habitual en los IBM PC y similares. Por otra parte, una página mecanografiada suele ocupar entre 2 K (cerca de 2000 letras) y 4 K.

Cuando se manejan datos realmente extensos, se pasa a otro múltiplo, el megabyte o Mb, que es 1000 K (en realidad 1024 K) o algo más de un millón de bytes. Por ejemplo, en un diskette “normal” caben 1.44 Mb, y en un Compact Disc para ordenador (Cd-Rom) se pueden almacenar hasta 650 Mb. La memoria principal (RAM) de un ordenador actual suele andar entre los 32 Mb y los 128 Mb, y un disco duro actual puede tener una capacidad superior a los 4.000 Mb.

Para estas unidades de gran capacidad, su tamaño no se suele medir en megabytes, sino en el múltiplo siguiente: en gigabytes, con la correspondencia 1 Gb = 1024 Mb. Así, son cada vez más frecuentes los discos duros con una capacdad de 10, 20 o más gigabytes.

Y todavía hay unidades mayores, pero que aún se utilizan muy poco. Por ejemplo, un terabyte son 1024 gigabytes.

Todo esto se puede resumir así:
Unidad | Equivalencia | Valores posibles
Byte | - | 0 a 255 (para guardar 1 letra)
Kilobyte (K o Kb) | 1024 bytes | Aprox. media página mecanografiada
Megabyte (Mb) | 1024 Kb | -
Gigabyte (Gb) | 1024 Mb | -
Terabyte (Tb) | 1024 Gb | -

También tienes disponible información adicional sobre:

  • Los bits.
  • El sistema binario.


Curso de C. Otros temas complementarios: Unidades de medida (2).

Los bits.

Dentro del ordenador, la información se debe almacenar realmente de alguna forma que a él le resulte "cómoda" de manejar.

Como la memoria del ordenador se basa en componentes electrónicos, la unidad básica de información será que una posición de memoria esté usada o no (totalmente llena o totalmente vacía), lo que se representa como un 1 o un 0. Esta unidad recibe el nombre de bit.

Un bit es demasiado pequeño para un uso normal (recordemos: sólo puede tener dos valores: 0 ó 1), por lo que se usa un conjunto de ellos, 8 bits, que forman un byte. Las matemáticas elementales (combinatoria) nos dicen que si agrupamos los bits de 8 en 8, tenemos 256 posibilidades distintas (variaciones con repetición de 2 elementos tomados de 8 en 8: VR2,8):

00000000
00000001
00000010
00000011
00000100
...
11111110
11111111

Por tanto, si en vez de tomar los bits de 1 en 1 (que resulta cómodo para el ordenador, pero no para nosotros) los utilizamos en grupos de 8 (lo que se conoce como un byte), nos encontramos con 256 posibilidades distintas, que ya son más que suficientes para almacenar una letra, o un signo de puntuación, o una cifra numérica o algún otro símbolo.

Por ejemplo, se podría decir que cada vez que encontremos la secuencia 00000010 la interpretaremos como una letra A, y la combinación 00000011 como una letra B, y así sucesivamente.

También existe una correspondencia entre cada grupo de bits y un número del 0 al 255: si usamos el sistema binario de numeración, en vez del sistema decimal, tenemos que:

0000 0000 (binario) = 0 (decimal)
0000 0001 (binario) = 1 (decimal)
0000 0010 (binario) = 2 (decimal)
0000 0011 (binario) = 3 (decimal)
...
1111 1110 (binario) = 254 (decimal)
1111 1111 (binario) = 255 (decimal)

(Nota: Puedes saber más sobre el sistema binario)

En la práctica, existe un código estándar, el código ASCII (American Standard Code for Information Interchange, código estándar americano para intercambio de información), que relaciona cada letra, número o símbolo con una cifra del 0 al 255: la "a" es el número 97, la "b" el 98, la "A" el 65, la "B", el 32, el "0" el 48, el "1" el 49, etc. Así se tiene una forma muy cómoda de almacenar la información en ordenadores, ya que cada letra ocupará exactamente un byte (8 bits: 8 posiciones elementales de memoria).

Aun así, hay un inconveniente con el código ASCII: sólo los primeros 127 números son estándar. Eso quiere decir que si escribimos un texto en un ordenador y lo llevamos a otro, las letras básicas (A a la Z, 0 al 9 y algunos símbolos) no cambiarán, pero las letras internacionales (como la Ñ o las vocales con acentos) puede que no aparezcan correctamente, porque se les asignan números que no son estándar para todos los ordenadores.

(Puedes consultar más detalles sobre el código ASCII).

Nota: este jaleo de los bits y de que realmente el ordenador trabaja con ceros y unos (sistema binario), por lo que le resulta más fácil manejar los números que son potencia de 2 que los que no lo son, es lo que explica que el prefijo kilo no quiera decir “exactamente mil”, sino que se usa la potencia de 2 más cercana: 210 =1024. Así, la equivalencia exacta es 1 K = 1024 bytes.


Curso de C. Otros temas complementarios: El sistema binario.

Nosotros normalmente utilizamos el sistema decimal de numeración: todos los números se expresan a partir de potencias de 10, pero normalmente lo hacemos sin pensar.

Por ejemplo, el número 3.254 se podría desglosar como:

254 = 3 · 1000 + 2 · 100 + 5 · 10 + 4 · 1

o más detallado todavía:

254 = 3 · 103 + 2 · 102 + 5 · 101 + 4 · 100

(aunque realmente nosotros lo hacemos automáticamente: no nos paramos a pensar este tipo de cosas cuando sumamos o multiplicamos dos números).

Para los ordenadores no es cómodo contar hasta 10. Como partimos de “casillas de memoria” que están completamente vacías (0) o completamente llenas (1), sólo les es realmente cómodo contar con 2 cifras: 0 y 1.

Por eso, dentro del ordenador cualquier número se deberá almacenar como ceros y unos (el llamado “sistema binario”), y entonces los números se deberán desglosar en potencias de 2:

13 = 1 · 8 + 1 · 4 + 0 · 2 + 1 · 1

o más detallado todavía:

13 = 1 · 23 + 1 · 22 + 0 · 21 + 1 · 20

de modo que el número decimal 13 se escribirá en binario como 1101.

En general, convertir un número binario al sistema decimal es fácil: lo expresamos como suma de potencias de 2 y sumamos:

0110 1101 (binario) = 0 · 27 + 1 · 26 + 1 · 25 + 0 · 24 + 1 · 23 + 1 · 22 + 0 · 21 + 1 · 20 =
= 0 · 128 + 1 · 64 + 1 · 32 + 0 · 16 + 1 · 8 + 1· 4 + 0 · 2 + 1 · 1 = 109 (decimal)

Convertir un número de decimal a binario resulta algo menos intuitivo. La forma más sencilla (relativamente) es ir dividiendo entre las potencias de 2, y coger todos los cocientes de las divisiones:

109 / 128 = 0 (resto: 109)
109 / 64 = 1 (resto: 45)
45 / 32 = 1 (resto: 13)
13 / 16 = 0 (resto: 13)
13 / 8 = 1 (resto: 5)
5 / 4 = 1 (resto: 1)
1 / 2 = 0 (resto: 1)
1 / 1 = 1 (se terminó).

Si “juntamos” los cocientes que hemos obtenido, aparece el número binario que buscábamos:

109 decimal = 0110 1101 binario

(Nota: es frecuente separar los números binarios en grupos de 4 cifras -medio byte- para mayor legibilidad, como yo he hecho en el ejemplo anterior; a un grupo de 4 bits se le llama nibble).


Curso de C. Otros temas complementarios: El sistema octal y el hexadecimal.

Hemos visto que el sistema de numeración más cercano a como se guarda la información dentro del ordenador es el sistema binario. Pero los números expresados en este sistema de numeración "ocupan mucho". Por ejemplo, el número 254 se expresa en binario como 11111110 (8 cifras en vez de 3).

Por eso, se han buscado otros sistemas de numeración que resulten más "compactos" que el sistema binario cuando haya que expresar cifras medianamente grandes, pero que a la vez mantengan con éste una correspondencia algo más sencilla que el sistema decimal.

Los más usados son el sistema octal y, sobre todo, el hexadecimal.

El sistema octal.

Este sistema de numeración trabaja en base 8. La forma de convertir de decimal a binario será, como siempre dividir entre las potencias de la base. Por ejemplo:

254 (decimal) ->

254 / 64 = 3 (resto: 62)
62 / 8 = 7 (resto: 6)
6 / 1 = 6 (se terminó)

de modo que

254 = 3 · 82 + 7 · 81 + 6 · 80

o bien

254 (decimal) = 376 (octal)

Hemos conseguido otra correspondencia que, si bien nos resulta a nosotros más incómoda que usar el sistema decimal, al menos es más compacta: el número 254 ocupa 3 cifras en decimal, y también 3 cifras en octal, frente a las 8 cifras que necesitaba en sistema binario.

Pero además existe una correspondencia muy sencilla entre el sistema octal y el sistema binario: si agrupamos los bits de 3 en 3, el paso de binario a octal es rapidísimo

254 (decimal) = 011 111 110 (binario)

011 (binario ) = 3 (decimal y octal)
111 (binario ) = 7 (decimal y octal)
110 (binario ) = 6 (decimal y octal)

de modo que

254 (decimal) = 011 111 110 (binario) = 376 (octal)

El paso desde el octal al binario y al decimal también es sencillo. Por ejemplo, el número 423 (octal) sería

423 (octal) = 100 010 011 (binario)

o bien

423 (octal) = 4 · 64 + 2 · 8 + 3 · 1 = 275 (decimal)

De cualquier modo, el sistema octal no es el que más se utiliza en la práctica, sino el hexadecimal.

El sistema hexadecimal.

El sistema octal tiene un inconveniente: se agrupan los bits de 3 en 3, por lo que convertir de binario a octal y viceversa es muy sencillo, pero un byte está formado por 8 bits, que no es múltiplo de 3.

Sería más cómodo poder agrupar de 4 en 4 bits, de modo que cada byte se representaría por 2 cifras. Este sistema de numeración trabajará en base 16 (24=16), y es lo que se conoce como sistema hexadecimal.

Pero hay una dificultad: estamos acostumbrados al sistema decimal, con números del 0 al 9, demodo que no tenemos cifras de un solo dígito para los números 10, 11, 12, 13, 14 y 15, que utilizaremos en el sistema hexadecimal. Para representar estas cifras usaremos las letras de la A a la F, así:

 0 (decimal) = 0 (hexadecimal)
 1 (decimal) = 1 (hexadecimal)
 2 (decimal) = 2 (hexadecimal)
 3 (decimal) = 3 (hexadecimal)
 4 (decimal) = 4 (hexadecimal)
 5 (decimal) = 5 (hexadecimal)
 6 (decimal) = 6 (hexadecimal)
 7 (decimal) = 7 (hexadecimal)
 8 (decimal) = 8 (hexadecimal)
 9 (decimal) = 9 (hexadecimal)
10 (decimal) = A (hexadecimal)
11 (decimal) = B (hexadecimal)
12 (decimal) = C (hexadecimal)
13 (decimal) = D (hexadecimal)
14 (decimal) = E (hexadecimal)
15 (decimal) = F (hexadecimal)

Con estas consideraciones, el expresar números en base hexadecimal ya no es difícil:

254 (decimal) ->

254 / 16 = 15 (resto: 14)
14 / 1 = 14 (se terminó)

de modo que

254 = 15 · 161 + 14 · 160

o bien

254 (decimal) = FE (hexadecimal)

Vamos a repetirlo para un convertir de decimal a hexadecimal número más grande:

54331 (decimal) ->

54331 / 4096 = 13 (resto: 1083)
1083 / 256 = 4 (resto: 59)
59 / 16 = 3 (resto: 11)
11 / 1 = 11 (se terminó)

de modo que

54331 = 13 · 4096 + 4 · 256 + 3 · 16 + 11 · 1

o bien

254 = 13 · 163 + 4 · 16 2 + 3 · 161 + 11 · 160

es decir

54331 (decimal) = D43B (hexadecimal)

Ahora vamos a dar el paso inverso: convertir de hexadecimal a decimal, por ejemplo el número
A2B5

A2B5 (hexadecimal) = 10 · 163 + 2 · 16 2 + 11 · 161 + 5 · 160 = 41653

El paso de hexadecimal a binario también es (relativamente) rápido, porque cada dígito hexadecimal equivale a una secuencia de 4 bits:

0 (hexadecimal) =  0 (decimal) = 0000 (binario)
1 (hexadecimal) =  1 (decimal) = 0001 (binario)
2 (hexadecimal) =  2 (decimal) = 0010 (binario)
3 (hexadecimal) =  3 (decimal) = 0011 (binario)
4 (hexadecimal) =  4 (decimal) = 0100 (binario)
5 (hexadecimal) =  5 (decimal) = 0101 (binario)
6 (hexadecimal) =  6 (decimal) = 0110 (binario)
7 (hexadecimal) =  7 (decimal) = 0111 (binario)
8 (hexadecimal) =  8 (decimal) = 1000 (binario)
9 (hexadecimal) =  9 (decimal) = 1001 (binario)
A (hexadecimal) = 10 (decimal) = 1010 (binario)
B (hexadecimal) = 11 (decimal) = 1011 (binario)
C (hexadecimal) = 12 (decimal) = 1100 (binario)
D (hexadecimal) = 13 (decimal) = 1101 (binario)
E (hexadecimal) = 14 (decimal) = 1110 (binario)
F (hexadecimal) = 15 (decimal) = 1111 (binario)

de modo que

A2B5 (hexadecimal) = 1010 0010 1011 0101 (binario)

y de igual modo, de binario a hexadecimal es dividir en grupos de 4 bits y hallar el valor de cada uno de ellos:

110010100100100101010100111 =>

0110 0101 0010 0100 1010 1010 0111 = 6524AA7


Diccionario de informatica - Nacho Cabanes

Diccionario básico de Informática.

Por Nacho Cabanes -Versión 1.02. (718 definiciones)

Contenidos:

  • Sobre este texto.
  • A | B | C | D | E | F | G | H | I | J | K | L | M | N | O |
  • P | Q | R | S | T | U | V | W | X | Y | Z | 0-9
  • Apéndice: versiones anteriores de este texto y próximas mejoras.

Sobre este texto.

Dentro del mundo de la informática, aparecen cada vez más abreviaturas y palabras "raras". Este texto pretende ser un diccionario básico, que cubra al menos las más habituales. No pretendo hacer una enciclopedia (al menos todavía ;-) así que la mayoría de las definiciones serán escuetas, y esta página no incluirá imágenes ni diagramas (más que nada por limitaciones de tamaño, aunque quizá más adelante realice otra versión más detallada, en formato WinHelp -ayuda de Windows- o similar).

Este texto ha sido creado por José Ignacio Cabanes (dirección e-mail: ncabanes@pobox.com; página Web: http://www.pobox.com/users/ncabanes). Por favor, conserva mi nombre si distribuyes este fichero o el texto impreso. En mi página Web podrás encontrar la última versión de este texto, así como cursos sobre varios lenguajes de programación. Si no existiera es página, puede ser porque haya cambiado de servidor; en ese caso, haz una búsqueda en Olé (http://www.ole.es), Ozú (www.ozu.es) o en cualquiera de los buscadores de habla hispana más conocidos, y por ahí apareceré yo...

Si descubres alguna errata o alguna definición incorrecta,
por favor házmelo saber,
escribiendo a: ncabanes@pobox.com.

A

  • A4: Tamaño de papel normalizado, de 210 mm x 297 mm.
  • Access: Gestor de bases de datos, realizado por Microsoft. Se puede adquirir como parte de la Suite llamada Microsoft Office o bien por separado.
  • ActiveX: Un conjunto de tecnologías de interoperabilidad independientes del lenguaje, creadas por Microsoft, que permiten que los componentes de software escritos en diferentes lenguajes funcionen juntos en entornos de red. Los elementos fundamentales de la tecnología ActiveX son el Modelo de objetos componentes (COM) y el Modelo de objetos componentes distribuido (DCOM).
  • Ada: Lenguaje de programación estructurado, que permite varias características orientadas a objetos. Es el lenguaje empleado por el Departamento de Defensa (DoD) de los Estados Unidos.
  • ADC: Conversor de analógico a digital.
  • Address: Dirección, en inglés. Normalmente se refiere a la posición dentro de la memoria en la que se encuentra un cierto dato.
  • ADSL: Línea Digital Asimétrica de Abonado, un tipo de línea digital alternativa a RDSI, en la que la velocidad de recepción de datos por parte del usuario es mayor (habitualmente 256 Kbps o 512 Kbps) que la velocidad a la que puede enviar (habitualmente 128 Kbps) (Asymetric Digital Subscriber Line).
  • Administrador: La persona que supervisa y controla el correcto funcionamiento de un sistema informático.
  • AGP: Accelerated Graphics Port: una nueva ranura de conexión para tarjetas graficas que existe en las placas base de los equipos Pentium II y superiores.
  • Agujas (impresora "de agujas"): ver matricial.
  • AI: Siglas de Inteligencia Articial (Artificial Intelligence).
  • AIX: Versión del sistema operativo UNIX realizada por IBM para sus ordenadores.
  • Alfanumérico: Conjunto de letras, números y otros símbolos, como signos de puntuación o símbolos matemáticos. Hace referencia a los caracteres del teclado y al conjunto de caracteres disponibles para las diferentes operaciones de transferencia de datos del ordenador.
  • Algoritmo: Conjunto de reglas para efectuar algún cálculo, bien sea a mano o (más frecuentemnte) a máquina. No debe implicar ninguna decisión subjetiva, ni requerir intuición o creatividad.
  • ALU: Siglas en inglés de la "Unidad Aritmético-Lógica", una de las partes de la Unidad Central de Proceso de un ordenador.
  • AMD: Conocido fabricante de procesadores, compatibles con la serie x86 de Intel (Advanced Micro Devices).
  • AMI: Conocido fabricante de BIOS para ordenadores tipo PC (American Megatrends Inc).
  • AmiPro: Procesador de textos realizado por Lotus, actualmente mejorado y llamado WordPro.
  • And: Operador a nivel de bits: producto lógico, que da como resultado 1 si los dos bits son 1 y 0 si un de los bits es 0. // Y, en inglés (se suele usar como parte de una condición en muchos lenguajes de programación).
  • ANSI: American National Standards Institute. Organización establecida para adoptar y definir estándares de diversas disciplinas técnicas.
  • Antivirus: Aplicación informática encargada de detectar y eliminar virus.
  • API: Interfaz de programación de aplicaciones (Applications Programming Interface): una serie de funciones que están disponibles para realizar programas para un cierto entorno
  • Aplicación informática: Un programa de ordenador que se compra ya realizado y listo para usar. Las hay de muy diversos tipos, según para qué propósito se hayan diseñado: procesadores de texto, bases de datos, programas de contabilidad, de facturación, etc.
  • Apple: Casa fabricante de ordenadores, creadora de ordenadores como los Apple II, Lisa, Macintosh e iMac.
  • Approach: Gestor de bases de datos, realizado por Lotus.
  • Árbol: Estructura de datos utilizada en muchos lenguajes de programación, en la cual cada elemento tiene un predecesor (excepto el primero) y uno o varios sucesores (excepto las "hojas").
  • ARC: Extensión de ficheros comprimidos. El compresor correspondiente es ARC o PkARC.
  • Archive: Archivo, en inglés.
  • Archivo: Se suele usar como sinónimo de fichero. En el mundo de los compresores, es frecuente hacer una diferenciación, diciendo que varios ficheros originales se comprimen y se empaquetan en un archivo comprimido.
  • ARJ: Extensión de ficheros comprimidos. El compresor correspondiente también se llama ARJ.
  • Arranque: Se suele usar este nombre para referirse a la acción de poner un ordenador en funcionamiento. Se suele distinguir entre "arranque en frío" (cuando el ordenador estaba apagado) y "arranque en caliente" (cuando el ordenador estaba encendido y se "reinicializa" sin llegar a apagarlo físicamente). En inglés es frecuente llamarlo "boot", y distinguir entre "cold boot" (frío) y "warm boot" (caliente) o "reset".
  • ASCII: Código estándar utilizado para representar letras, símbolos y ciertos códigos de control mediante secuencias de 7 bits, de modo que distintos equipos y sus programas puedan intercambiar información (American Standard Code for Information Interchange). En la práctica se suele usar un código ASCII ampliado, de 8 bits (256 caracteres distintos), aunque sólo los primeros 128 son estándar.
  • Asm: ver Ensamblador.
  • AS/400: Serie de grandes ordenadores creados por IBM.
  • AT: Nombre que recibió el primer ordenador creado por IBM con procesador 80286. Así, se suele hablar de ordenadores compatibles AT refiriéndose a ordenadores con procesadores 80286 o superiores. // AT: Juego de órdenes para los módem, creado por la casa Hayes y hoy en día considerado estándar.
  • Athlon: Procesador realizado por AMD, equivalente a las gamas Pentium II / Pentium III, aunque con un rendimiento ligeramente superior a iguales velocidades de reloj.
  • Atari: Casa fabricante de ordenadores y de software, hoy desaparecida, que desarrolló software de máquinas recreativas, consolas de videojuegos, ordenadores de 8 bits (como el 400 y el 800), de 16 bits (como la serie ST) y tuvo una pequeña incursión en el mercado de los compatibles PC.
  • ATAPI: Interfaz de programación para sistemas de bus AT. Es un estándar que siguen la mayoría de los lectores de CdRom
  • ATI: Conocido fabricante de tarjetas gráficas.
  • ATM: Modo de transmisión asíncrona (Asynchronous Transmision Mode). Un protocolo de comunicaciones definido para comunicaciones de datos a alta velocidad.
  • Atributos: Información que indica si un archivo es de sólo lectura, oculto, de sistema y si se ha modificado el archivo desde que se hizo una copia de seguridad del mismo.
  • AutoCad: Conocido programa de CAD realizado por Autodesk.
  • Autodesk: Casa desarrolladora de software, autora de aplicaciones como AutoCad, 3D Studio, Animator, AutoSketch, etc.
  • Autoedición: Una de las aplicaciones actuales de los ordenadores: la maquetación de periódicos, revistas, carteles, etc.
  • AVI: Formato de video comprimido para ordenador, el estándar en Windows.
  • Award: Conocido fabricante de BIOS para ordenadores tipo PC.

B

  • Babbage, Charles: Matemático inglés (1792-1871), que diseñó una máquina precursora del ordenador actual, pero que no se pudo llegar a terminar.
  • BackUp: Copia de seguridad.
  • Bajar: Es cada vez más frecuente oir frases como "Me he bajado de la red una actualización para mi antivirus". La palabra "bajar" (o "descargar", en inglés "download") indica una transferencia de información desde un ordenador remoto (por ejemplo, un ordenador que haga de anfitrión en Internet o en otra red) hasta nuestro ordenador personal. La palabra "subir" (en inglés "upload") indica el envío de información de nuestro ordenador a un ordenador anfitrión en la red.
  • Base de datos: Aplicación informática para manejar información en forma de "fichas": clientes, artículos, películas, etc. La mayoría de las bases de datos actuales permiten hacer listados, consultas, crear pantallas de visualización de datos, controlar el acceso de los usuarios, etc. También es cada vez más frecuente que las consultas se puedan hacer en un lenguaje estándar conocido como SQL.
  • Basic: Lenguaje de programación inicialmente diseñado para principiantes (Beginners All-purpose Symbolic Instruction Code).
  • BAT: Extensión de un fichero formado por un lote (batch) de órdenes de Dos.
  • Batch: Lote de órdenes que se procesarán una tras otra.
  • Baudios: Bits por segundo que en teoría es capaz de transmitir/recibir un módem o una conexión serie entre dos equipos, o entre un equipo y un dispositivo externo.
  • BBS: Tablero de Anuncios Electrónico (Bulletin Board System). Servidor de comunicaciones que proporciona a los usuarios servicios variados como correo electrónico, foros de discusión y transferencia de ficheros. Originalmente funcionaban a traves de lineas telefónicas.
  • BCD: Número decimal codificado en binario (Binary Coded Decimal). Se usan estas siglas para referirse a una forma especial de codificar números decimales en binario, usando 4 bits para cada dígito decimal, de modo que en un byte se pueden almacenar dos números decimales.
  • BeOS: Sistema operativo tipo UNIX, de reciente aparición, orientado a la multimedia, creado por Be Inc.
  • Binario: sistema de numeración en base 2, de modo que sólo hay dos dígitos posibles: el 0 y el 1. Para formar números "grandes", se usan varios dígitos binarios, que representan cada una de las potencias de 2. Por ejemplo, el número decimal 13 se representa 1101 (1x8 + 1x4 + 0x2 + 1x1).
  • BIOS: Sistema de entrada/salida básica (Basic Input Output System). Suele tratarse de uno o varios chips de memoria ROM (habitualmente EPROMs) que contienen las rutinas básicas de entrada y salida, los primeros pasos que debe dar un ordenador al encenderse, la configuración básica del sistema, etc.
  • Bit: Es la unidad mínima de información que puede almacenar y manejar un ordenador, equivalente a un 0 o un 1 (abreviatura de Binary Digit - dígito binario).
  • Bitmap: (o mapa de bits). Un tipo de imágenes para ordenador, en las que se almacena información sobre los puntos que las componen y el color de cada punto (al contrario que en las imágenes vectoriales). Esto supone que al ampliar la imagen se pierde definición, se ven "puntos gordos".
  • Bluetooth: Especificación diseñada para conectar diversos dispositivos de forma inalámbrica. La velocidad máxima prevista es de unos 720 Kbps, y el alcance de unos 10 m.
  • BMP: Es la Extensión que corresponde a un tipo de fichero gráfico de mapa de bits (el estándar en Windows): BitMaP.
  • BNC: Nombre que reciben los conectores usados en los cable coaxiales.
  • Boole, George: Matemático inglés (1815-1864), uno de los fundadores de la lógica matemática que usan los ordenadores actuales.
  • Boot: ver arranque.
  • Borland: Conocido fabricante de software, autor de lenguajes como Turbo Pascal, Turbo C++, Delphi, C++Builder, JBuilder, etc., y de aplicaciones como Paradox, Quattro Pro, etc. Actualmente ha cambiado de nombre y se llama Inprise.
  • Borland C: Ver Turbo C.
  • Borland Pascal: Ver Turbo Pascal.
  • bps: Bits por segundo: es la unidad en que se mide la velocidad de transferencia efectiva de un módem o de una conexión serie. La velocidad habitual de un módem hoy en dia es de cerca de 56000 bps. // Bps: (con la B en mayúscula) Bytes por segundo (menos utilizado).
  • Bridge: Ver Puente.
  • BSD: Berkeley Software Distribution, de la Universidad de California en Berkeley, autora de una versión de UNIX que lleva su mismo nombre.
  • Buffer: memoria intermedia para el almacenamiento de datos temporales en la comunicación entre un ordenador y un dispositivo externo (p.ej., una impresora). Cuando es un programa informático el que hace la misión de almacenamiento intermedio para los datos que se envían a la impresora, a dicho programa se le suele llamar Spooler.
  • Bug: Error en un programa, que hace que en ciertas circunstancias pueda no comportarse correctamente.
  • Bus: Nombre que reciben las "pistas" de circuito impreso por las que circula la información en el interior de un ordenador. Hay varios buses que conceptualmente son importantes: el bus de datos es por donde circula la información que se ha leido de una posición de memoria (o que se va a almacenar en ella), el bus de direcciones es por donde circula la información que indica en concreto qué posición de memoria es esa que se debe leer o modificar; por el bus de control circulan las señales de control. En un ordenador actual es frecuente encontrar otros buses que conectan con diversos dispositivos, como el bus IDE o el bus SCSI.
  • Buscador: página en Internet que permite buscar información a través de ella, bien sea tecleando nosotros mismos una serie de palabras clave, o bien empleando el sistema de menús que la página incorpora. Cada día ofrecen más servicios, entre los que se incluyen noticias, chats, etc., y se suelen llamar "portales".
  • Byte: Es la unidad básica de información. En la práctica, se puede considerar que un byte es la cantidad de espacio necesaria para almacenar una letra. Tiene múltiplos como el Kilobyte, Megabyte, Gigabyte y Terabyte. Internamente, corresponde a 8 bits.

C

  • C: Lenguaje de programación estructurado, de propósito general, cuyo uso está muy extendido. // C: Extensión de los fuentes creados en lenguaje C
  • C++: Lenguaje de programación orientado a objetos, basado en el lenguaje C.
  • C#: Lenguaje de programación orientado a objetos, evolución del lenguaje C++, desarrollado por Microsoft.
  • Cache: Es un tipo de memoria especial, más rápida que la RAM normal (y más cara), que se sitúa en el camino de los datos que van del procesador a la memoria RAM. Así, toda información que va de la RAM al procesador se deja almacenada temporalmente en la memoria caché. A la hora de volver a leer información, se comprueba primero si está en la memoria caché; si se encuentra allí, no hace falta ir a la RAM a buscar. En la práctica, es muy frecuente realizar operaciones repetitivas o trabajar con datos repetitivos, lo que hace que poseer memoria caché ayude a acelerar el funcionamiento normal del ordenador. Hoy en día es frecuente distinguir también la memoria caché de "primer nivel" y de "segundo nivel". La de primer nivel se encuentra dentro del propio procesador. La de segundo nivel se encuentra en la placa base, es de mayor tamaño y algo más lenta.
  • CAD: Diseño asistido por ordenador (Computer Aided Design).
  • CAE: Ingeniería asistida por ordenador (Computer Aided Engineering).
  • Caldera: Una de las casas especializadas en realizar distribuciones de Linux, en concreto la conocida como OpenLinux.
  • CAM: Fabricación asistida por ordenador (Computer Aided Manufacturing).
  • Campo: En el mundo de las bases de datos, cada una de los datos que forman un registro (o ficha). Por ejemplo, en la ficha de cada proveedor tendríamos campos como su nombre, su direccion postal, su teléfono, etc.
  • Canon: Conocido fabricante de impresoras, ordenadores y otros tipos de hardware.
  • Carpeta: (en inglés, "folder") Nombre que se da a los directorios en algunos sistemas, como Windows.
  • CASE: Ingeniería de Software Asistida por Ordenador (Computer Aided Sofware Engineering).
  • CCD: Dispositivo empleado para captar imágenes. En los scanners, suele tratarse de una fila de células que se desplaza, y en las cámaras digitales de una matriz bidimensional. Cada célula está formada por una fotocélula y un condensador. La carga que adquiere el condensador dependerá de la cantidad de luz recibida por la fotocélula. (Charge Coupled Device).
  • CD: Normalmente se refiere a Compact Disc. Otras abreviaturas relacionadas son: CD-ROM, CD-R y CD-RW. Un Compact Disc para ordenador será capaz de almacenar cerca de 650 Mb de información (equivalente a 74 minutos de audio). También existe un estándar de 700 Mb (80 minutos de audio), y poco a poco van apareciendo otros de mayor capacidad, pero que todavía no son reconocidos por casi ningún lector/grabador.
  • CD-R: Es un Compact Disc en el que se puede grabar información (Recordable), pero no borrar ni modificar la ya existente (la información se puede marcar como borrada, y no se podrá acceder a ella, pero no se recupera el espacio que ocupaba).
  • CD-ROM: Es un Compact Disc pregrabado, del que se puede leer información pero no borrarla ni modificarla (Read Only Memory). También se suele usar este mismo nombre para el aparato encargado de leer estos Compact Disc para ordenador (sería más adecuado llamarlo "Lector de CD").
  • CD-RW: Es un Compact Disc en el que se puede grabar información y también borrar o modificar la ya existente (ReWritable).
  • CE: Nombre que recibía la versión de Windows orientada a ordenadores de pequeño tamaño (PDAs). Recientemente, Windows CE ha cambiado su nombre por "Pocket PC".
  • Celeron: Ver Pentium.
  • Centronics: es una conexión estándar paralelo, usada para conectar un ordenador y una impresora. En el ordenador, suele ser un conector DB25 hembra.
  • CGA: Adaptador gráfico a color (Color Graphics Adapter). Una de las primeras tarjetas gráficas existentes en los PC y compatibles. Permitía resoluciones de 320x200 puntos con 4 colores y de 640x200 puntos en 2 colores.
  • CGI: Pequeños programas situados en un servidor Web, para poder ser utilizados desde las páginas Web que se hospeden en dicho servidor (Common Gateway Interface).
  • CGM: Es la Extensión que corresponde a un tipo de fichero gráfico vectorial: Computer Graphics Metafile.
  • Chat: "Charla", en inglés. Se usa para referirse a las distintas formas posibles de comunicarse en tiempo real con otras personas mediante mensajes escritos, bien sea empleando utilidades como los IRC, o bien mediante los servicios que muchos portales de Internet ponen a disposición de sus usuarios.
  • Chicago: Nombre en clave dado a Windows 95 durante su proceso de desarrollo.
  • CHM: Es la extensión que corresponde a los ficheros de ayuda de Windows 98 y superiores.
  • Chorro de tinta: ver inyección.
  • CIM: Fabricación integrada por ordenador (Computer Integrated Manufacturing).
  • CISC: Un tipo de procesadores que reconoce un conjunto amplio de órdenes, pero que normalmente realiza los pasos indicados por esas órdenes con menor velocidad que un procesador RISC. Es el caso de la serie X86 de Intel.
  • Clave: En el mundo de las bases de datos, se conoce como clave (en inglés Key) al valor de es capaz de distinguir un registro de otro de forma fiable, como podría ser el DNI o el Pasaporte para el caso de una persona (el nombre no sería una clave correcta, ya que sí puede repetirse).
  • Clic: Se suele llamar "hacer clic" a situar el ratón sobre un cierto elemento de la pantalla y pulsar el botón principal (habitualmente el izquierdo). Algunos autores traducen el verbo inglés "click" por "clickar" o "cliquear".
  • Cliente: Un ordenador que accede a los recursos que otro ordenador (el servidor) deja a su disposición.
  • Clipboard: Nombre que recibe en inglés el "portapapeles".
  • Clipper: Compilador de lenguaje Xbase, desarrollado por Nantucket.
  • Clónico: La expresión "clónico" se suele usar casi como sinónimo de "compatible", aunque llevado a un nivel todavía más marcado: un ordenador que sea clónico de otro se parece tanto al original (al menos internamente) como permitan las leyes de copyright.
  • Cluster: Un disco duro (y cualquier otro medio de almacenamiento que tenga acceso directo, no secuencial) está dividido en "páginas" en las que podemos guardar información. Cada fichero puede ocupar una o más páginas, pero cada página sólo puede estar ocupada por un único fichero. El tamaño de esas páginas (que son los clusters) influirá en la cantidad de espacio desperdiciado en el disco: si hay páginas de tamaño grande, y tenemos almacenados muchos ficheros de pequeño tamaño, se desperdiciará mucho espacio (por ejemplo, con clusters de 32.768 bytes -32K-, un fichero de 200 bytes estaría desaprovechando los 32.568 bytes sobrantes del cluster, porque en ese mismo cluster no se guardará información de ningún otro fichero); con clusters pequeños, se desperdicia menos espacio al almacenar ficheros, pero el índice de contenido del disco ocupará más espacio, ya que hay mayor cantidad de clusters que controlar. En el sistema operativo MsDos (y Windows, hasta su versión Windows 95), el tamaño de los clusters es mayor cuanto mayor sea el disco duro, pudiendo llegar hasta los 32K de un disco duro de 2Gb (máximo tamaño que es posible manejar "a la vez" en estos sistemas). En Windows 98 (y en la actualización de Windows 95 llamada OSR2) se puede optar por un tamaño de cluster de 4K. En algunos sistemas Unix, el tamaño de cluster puede ser fijado al instalar el sistema (por ejemplo, eligiendo entre 1, 2 o 4K).
  • CMY: Descomposición de colores en función de tres componentes básicos: azul claro (Cyan), violeta (Magenta) y amarillo (Yellow). Es la descomposición habitual en las impresoras. Se trata de colores substractivos: cuanto mayor es la cantidad éstos, más se aproxima el color resultante al negro. Cada vez es más frecuente añadir más colores base, buscando negros más puros (CMYK, con negro -blacK) o mayor fidelidad en los colores intermedios (CcMmYK, con dos tonos adicionales de Cyan y Magenta).
  • CMYK: Ver CMY.
  • Coaxial: Cable de comunicación, usado en algunas redes de ordenadores, formado por un conductor interior, una primera capa de aislante, un segundo conductor en forma de malla u hoja metálica, y un nuevo aislante exterior.
  • Cobol: Lenguaje de programación enfocado a aplicaciones de gestión, hoy bastante en desuso (COmmon Businness-Oriented Language).
  • Código Máquina: es el lenguaje en el que el ordenador es capaz de reconocer órdenes. Los programas que se creen en otros lenguajes deberán traducirse previamente a Código Máquina (compilarse) o el ordenador no podrá entenderlos.
  • Cola: Estructura de datos utilizada en muchos lenguajes de programación, en la cual cada elemento tiene un predecesor (excepto el primero) y un sucesor (excepto el último). Además, existe una limitación en el manejo de esos datos: los elementos "entran" (se introducen) por un extremo y "salen" (se leen) por el extremo opuesto. A las estructuras con esta propiedad se les suele llamar FIFO (First In First Out: el primero en entrar es el primero en salir).
  • COM: Es la extensión que corresponde a un tipo de fichero ejecutable bajo MsDos. // COM: Nombre que reciben bajo DOS los puertos serie (para conectar módem o ratón, por ejemplo). Lo habitual es que un ordenador tenga dos puertos de este tipo, que se designarían COM1 y COM2 // COM: Modelo de programación orientada a objetos que define cómo interactúan los objetos con una única aplicación o entre distintas aplicaciones; el software cliente tiene acceso a un objeto mediante la "interfaz" (conjunto de métodos) del objeto. (Modelo de objetos componentes)
  • Coma (fija y flotante): La expresión "coma fija" (en inglés fixed point) se usa para referirse a números con una cantidad de cifras decimales constante. Estos números son más sencillos de manipular (pero menos útiles en la práctica) que los números en "coma flotante" (en inglés, Floating Point), con un número variable de cifras decimales.
  • Command.com: Ver Shell.
  • Commodore: Casa desarrolladora de hardware, hoy desaparecida, creadora de ordendores como el PET, Vic20, C16, C64, C128, Amiga y algunos compatibles PC (PC-10, PC-20, etc).
  • Communicator: Ver Netscape.
  • Compaq: Prestigioso fabricante de ordenadores.
  • Compatible: Se dice que un ordenador es compatible con otro cuando puede utilizar todo el software diseñado para aquel (a veces incluso todo o parte del hardware). Hoy en día es frecuente hablar de ordenadores "compatibles" refiriéndose a ordenadores "compatibles PC", con procesadores de la gama x86.
  • Compilador: Aplicación informática que se usa para crear programas en un cierto lenguaje de programación. Convierte los programas creados en un lenguaje de programación al lenguaje interno del ordenador (código máquina). En los compiladores, todo el programa original (fuente) se convierte a código máquina en bloque, y el programa resultante (programa ejecutable) se puede en otro ordenador usar sin necesidad de recurrir otra vez al compilador. En los intérpretes, el programa fuente se convierte a código máquina, línea por línea, justo en el momento en que se pone a funcionar; no se crea ningún ejecutable, y por eso es necesario distribuir el programa fuente pero también el intérprete que es capaz de entenderlo.
  • Compresor: Aplicación informática que se encarga de "comprimir" la información para que ocupe menos espacio y así facilitar su almacenamiento y su transporte. En la gran mayoría de los casos, esta compresión se realiza sin ningún tipo de pérdida: los datos se pueden "descomprimir" para que vuelvan a quedar exactamente como estaban; en algunos tipos de compresión de audio y de video sí se puede llegar a perder calidad, aunque normalmente no la suficiente como para que se aprecie a simple vista.
  • Computador: Sinónimo de Ordenador.
  • Concurrente: Un ordenador realiza varias tareas de forma concurrente cuando dispone de un solo procesador, y dedica un breve espacio de tiempo a cada una de las tareas sucesivamente.
  • Controlador: Se suele llamar controlador (driver, en inglés) al programa necesario para que un cierto programa o sistema operativo sea capaz de utilizar un dispositivo (por ejemplo, una impresora).
  • Controladora: Se suele llamar "tarjeta controladora" a la tarjeta de circuito impreso que hay que instalar a un ordenador para que sea capaz de trabajar con un cierto dispositivo (por ejemplo, la controladora de disco curo, que hoy en día suele venir integrada en la placa base de casi cualquier ordenador personal).
  • Cookies: Pequeños archivos de texto que un servidor Web almacena en el ordenador del usuario, para guardar información sobre éste, como un número de identificación, una contraseña, sus preferencias o cuántas veces ha visitado el sitio el usuario. Un sitio Web puede tener acceso a la información del cookie siempre que el usuario se conecta al servidor.
  • Coprocesador: Procesador auxiliar diseñado para descargar de trabajo al procesador principal. Cuando se usa sólo la palabra "coprocesador", es habitual referirse a coprocesadores matemáticos, encargados de realizar operaciones con números reales (con decimales, también llamados de "coma flotante"). La mayoría de los procesadores actuales ya llevan incorporado el coprocesador matemático (por ejemplo, toda la gama Pentium y la mayoría de los 486).
  • CORBA: Common ORB Architecture, un estándar basado en ORB.
  • Corel: Conocido fabricante de software, autor de paquetes como Corel Draw y que adquirió y desarrolla también otros como Ventura Publisher y WordPerfect. Recientemente ha lanzado al mercado su propia distribución de Linux.
  • Correr: Ver Ejecutar.
  • Cortafuegos: ver Firewall.
  • CPC: Gama de ordenadores desarrollados por Amstrad a mediados-finales de los años 80, basados en el procesador Z80. Tenían capacidad de realizar gráficos con 16 colores (de una paleta de 27 colores) a 160x200 puntos, 4 colores en 320x200 y 2 colores en 640x480. Podían reproducir sonido stereo (3 canales). Se distribuían con monitor en color o en fósforo verde. Las 3 versiones más difundidas fueron el CPC 464 (64k de memoria, unidad de cinta incorporada), CPC 664 (64k de memoria, unidad de disco de 3" y 178K por cara), CPC 6128 (128k de memoria, unidad de disco de 3" y 178K por cara).
  • CP/M: Sistema operativo "profesional" creado por Digital Research, que fue bastante usado en ordenadores de 8 bits.
  • Cps: caracteres por segundo: era la unidad en que se medía la velocidad de impresión en las impresoras matriciales.
  • CPU: Unidad Central de Proceso (Central Processing Unit). Ver "procesador".
  • Cracker: persona que intenta romper las protecciones de un cierto sistema informático, normalmente con fines maliciosos (distinto de un "hacker", que procura profundizar en un cierto sistema para aprender de él).
  • CRC: Código de redundancia cíclica, un código de comprobación que se suele añadir a los datos transmitidos en muchas comunicaciones, y que permiten detectar (hasta cierto punto) si se ha producido algún error en la transmisión.
  • Criptografía: diseño de procedimientos para cifrar los mensajes, de forma que si son interceptados, no se pueda saber su contenido.
  • CSMA/CD: Acceso múltiple por detección de portadora con detección de colisiones (Carrier Sense Multiple Access with Collision Detection). Sistema de acceso a una red local, por el cual los nodos se aseguran de que la red no está en uso antes de enviar un paquete. La detección de colisiones significa que la red puede determinar cuándo se producen colisiones y proporciona medidas correctoras.
  • Cyrix: Conocido fabricante de procesadores, compatibles con la serie X86 de Intel.

D

  • DAC: Conversor de digital a analógico.
  • Datagrama: En el mundo de las redes de ordenadores, se suele llamar "datagrama" a un paquete de información que se envía de forma "no orientada a conexión" y "no confiable".
  • Dbase: Gestor de bases de datos, realizado por Ashton Tate y posteriormente adquirido y mejorado por Borland.
  • DBF: Es la extensión que corresponde a un tipo de fichero de bases de datos, originalmente utilizado por Dbase, pero que es frecuente encontrar en todo tipo de aplicaciones.
  • DBMS: Sistema de gestión de bases de datos (DataBase Management System).
  • DCC: Abreviatura de la conexión directa por cable, que permite conectar dos ordenadores mediante cable serie o paralelo, sin necesidad de tarjeta de red (Direct Cable Connection)
  • DCE: Equipo de comunicación de datos, nombre que suele recibir en una comunicación el módem utilizado por un ordenador para conectarse con otro equipo (Data Communication Equipment).
  • DCOM: Modelo de objetos componentes distribuido. Extensiones del Modelo de objetos componentes (COM) que facilita la distribución transparente de objetos a través de redes y de Internet. DCOM forma parte de la especificación administrada por The Open Group para la distribución en plataformas heterogéneas.
  • DD: Doble densidad (Double Density). En el caso de diskettes de 3 1/2", la expresión Doble Densidad hace referencia a que tienen una capacidad de 720 Kb (formateado; 1 Mb nominal). Este tipo de diskettes ya están obsoletos. // DD: Disco duro (muy poco utilizado con este significado).
  • DDE: Forma de compartir información entre distintas aplicaciones en Windows: "Intercambio dinámico de datos" (Dynamic Data Exchange). Hoy en día ha sido desbancado por "OLE".
  • DDR: Tipo de memoria RAM, a la que se puede acceder tanto en el flanco de subida de la señal de reloj como en el flanco de bajada, con lo que practicamente se duplica la velocidad de acceso, comparado con los módulos de memoria Simm convencionales (Double Data Rate).
  • Debian: Casa creadora de una distribución de Linux.
  • Debugger: Depurador, en inglés.
  • Decimal: Sistema de numeración en base 10, el que usamos habitualmente.
  • Dell: Prestigioso fabricante de ordenadores, que no suele vender a través de tiendas que actúen de intermediarias, sino directamente por teléfono o Internet.
  • Delphi: Herramienta de programación visual para Windows, basada en el lenguaje Pascal, desarrollada por Borland.
  • Depurador: Aplicación informática empleada para descubrir posibles errores cuando se crea un programa.
  • DES: Conocido sistema de criptografía de clave secreta. Usa bloques de datos de 64 bits y clave de 56 bits.
  • Descargar: ver Bajar.
  • DeskJet: Gama de impresoras de inyección de tinta creada por Hewlett-Packard.
  • Desktop Publishing: Autoedición.
  • DFD: Diagrama de Flujo de Datos, una de las notaciones empleadas en la fase de diseño de un programa.
  • DHTML: HTML dinámico, que permite crear documentos HTML que capaces de cambiar su contenido dinámicamente e interactuar con el usuario.
  • DIMM: Tipo de módulos de memoria RAM (Dual In-Line Memory Module).
  • Directorio: apartado de un sistema de almacenamiento (diskette, disco duro, CdRom, etc), destinado a contener ficheros (o, a su vez, más directorios, que entonces se suelen llamar "subdirectorios").
  • DirectX: Interfaz de programación bajo Windows, que permite acceder directamente al hardware (por ejemplo, tarjetas gráficas y de sonido). Es empleado para programar juegos en este sistema.
  • Disco duro: Es un dispositivo de almacenamiento, que nació como evolución del diskette. Tiene una capacidad mucho mayor (hoy en día es habitual que pasen de los 40 Gb) y es mucho más rápido, pero no está diseñado para ser llevado de un sitio a otro, sino para permanecer dentro del ordenador (salvo algunas pocas excepciones, que sí son portables). También se le llama "disco fijo", y (hoy en desuso) "disco winchester".
  • Disco fijo: Ver disco duro.
  • Disco flexible: Ver diskette.
  • Disco Winchester: Ver disco duro.
  • Diskette (o floppy disk, o disquete): Es un dispositivo de almacenamiento de información. El diskette tiene poca capacidad (habitualmente 1.44 Mb), pero tiene la ventaja de poderse llevar de un sitio a otro (es extraible).
  • Disquete: Ver Diskette.
  • DLL: Biblioteca de enlace dinámico (Dynamic Link Library), que contiene funciones que pueden ser utilizadas desde los programas, y que son cargadas sólo en el momento en que se necesitan. Es un tipo de fichero muy frecuente en Windows.
  • DMA: Acceso directo a memoria, un proceso que permite a un dispositivo (externo o interno) transferir datos a la memoria del ordenador a alta velocidad, sin que estos datos pasen por el procesador (Direct Memory Access).
  • DNS: Sistema de nombres de dominio (Domain Name System).
  • DOC: Extensión de los ficheros de creados con Word y otros procesadores de texto (abreviatura de DOCument).
  • DOS: Sistema operativo de disco (Disk Operating System). Se trata de un sistema operativo monousuario y monotarea, basado en línea de comandos. Hay diversas versiones, con distintos nombres según la casa que lo desarrolle: MsDos (Microsoft), DrDos (Digital Research), PcDos (IBM), Novell Dos (Novell), etc.
  • Dot Pich: Es la separación entre los puntos básicos rojo, verde o azul que se iluminan en un monitor en color para representar un punto de la imagen.Valores frecuentes son 0.28 y 0.26mm de separación.
  • Download: ver Bajar.
  • DPI: Puntos por pulgada (Dots Per Inch), ver PPP.
  • DPMI: Una serie de funciones que permitían a programas de DOS trabajar en modo protegido, aprovechando toda la memoria existente (Dos Protected Mode Interface).
  • DRAM: Memoria RAM dinámica.
  • DrDos: Ver DOS.
  • Driver: ver controlador.
  • DSL: Línea Digital de Abonado, un tipo de línea digital alternativa a RDSI (Digital Subscriber Line). Existen variantes como ADSL y SDSL.
  • DSP: Procesador digital de señal (Digital Signal Processor).
  • DSTN: Ver Dual Scan (Dualscan SuperTwist Nematic).
  • Dual Scan: Tipo de pantalla LCD en color para ordenadores portátiles. Ofrece menos contraste y peor ángulo de visión que las TFT, a cambio de un precio inferior.
  • DTE: Equipo terminal de datos, nombre que suele recibir en una comunicación el ordenador que recibe o envía los datos (Data Terminal Equipment).
  • Duron: Procesador creado por AMD para el mercado de consumo. Existe en el mercado simultáneamente al Athlon (que tiene mayor potencia). Es análogo al Celeron de Intel.
  • DVD: Disco digital mejorado, con una capacidad muy superior al CD. (Digital Video Disk o Digital Versatile Disk). Al igual que en los CD, hay distintas variantes según si sólo puede leer, leer y escribir, etc.: DVD-ROM, DVD-RAM, etc. La capacidad de un DVD va desde los 4,7 Gb (una cara, una capa) hasta los 17 Gb (doble cara, doble capa). En el caso de los DVD grabables, esta capacidad puede disminuir hasta los 2,6 Gb por el mayor tamaño de los "surcos".
  • DX: Variante de los procesadores 80386 y 80486.
  • DX2: Variante de los procesadores 80486.
  • DX4: Variante de los procesadores 80486.

E

  • E-commerce: Comercio electrónico.
  • ED: Extra densidad (Extra Density). En el caso de diskettes de 3 1/2", la expresión Extra Densidad hace referencia a que tienen una capacidad de 2.88 Mb (formateado; poco frecuente).
  • EDO RAM: Un tipo de memoria RAM frecuente en los últimos 486 y primeros Pentium (Extended Data Out RAM), posterior a FastPage y anterior a SDRAM.
  • EEPROM: Memoria ROM borrable y programable eléctricamente (Electrically Erasable Programmable ROM).
  • EGA: Adaptador gráfico mejorado (Enhanced Graphics Adapter). Tarjeta gráfica de ordenadores PC y compatibles, evolución de la CGA, que permitía trabajar también a 16 colores con 320x200, 640x200 y 640x350 puntos.
  • EIDE: Ver IDE.
  • Eiffel: Lenguaje de programación orientado a objetos.
  • EISA: Tipo de arquitectura estándar de placas base, con ranuras de ampliación de 32 bits, basada en ISA y hoy en día abandonada (Enhanced ISA).
  • Ejecutable: Un programa que se puede "ejecutar" o usar "por sí solo", sin que haga falta tener una cierta aplicación informática desde la que manejarlo (para más detalles, ver Compilador).
  • Ejecutar: En informática, la palabra "ejecutar" (en inglés "run", que algunos autores traducen literalmente por "correr") equivale a poner un programa en funcionamiento.
  • E-mail: correo electrónico (electronic mail). Mensajes (normalmente privados) enviados a través de una red de ordenadores.
  • EMS: Ver memoria expandida.
  • Emulador: Sistema que imita el funcionamiento de otro. Por ejemplo, es cada vez más frecuente que los ordenadores actuales sean capaces de emular otros ordenadores más antiguos.
  • Encaminador: Ver Router.
  • ENIAC: Primer ordenador electromecánico, creado con válvulas de vacío, en 1946, en la universidad de Pennsylvania. Ocupaba una habituación de 9x30 metros, utilizaba 18.000 válvulas, 1.500 relés y sólo podía funcionar unos 2 minutos antes de que las válvulas empezaran a fundirse.
  • Ensamblador: (En inglés, Assembler, abreviado como ASM). Lenguaje de programación bajo nivel, muy cercano al código máquina. Su sintaxis depende por completo del tipo de ordenador que se esté usando.
  • EPROM: Memoria ROM borrable y programable (Erasable Programmable ROM). Se borran con luz ultravioleta (una exposición de unos 15-20 minutos).
  • Epson: Conocido fabricante de impresoras, ordenadores y otros tipos de hardware.
  • E/S: Entrada/salida (se suelen usar las siglas en inglés I/O).
  • Escritorio: Nombre que recibe la pantalla habitual de trabajo de varios sistemas informáticos, como Windows.
  • Ethernet. Estándar en redes locales con topología de bus, propuesto por el IEEE en su norma 802.3. El estándar inicial trabaja a 10 Mbps. Fue desarrollado inicialmente por Xerox y refinado posteriormente por Digital, Intel y Xerox.
  • Excel: Aplicación de hoja de cálculo realizada por Microsoft.
  • EXE: Es la extensión que corresponde a un tipo de fichero ejecutable bajo MsDos, OS/2 y/o Windows.
  • Expandida (memoria): Una de las convenciones que surgieron sobre cómo acceder a la memoria por encima de 1 Mb desde el sistema operativo DOS, algo para lo que inicialmente no estaba diseñado. Era incompatible con la otra propuesta de estándar, la memoria extendida, que fue la más utilizada. La especificación recibió el nombre de LIM/EMS (LIM es la abreviatura de las casas de software que la propusieron: Lotus-Intel-Microsoft).
  • Extendida (memoria): Una de las convenciones que surgieron sobre cómo acceder a la memoria por encima de 1 Mb desde el sistema operativo DOS, algo para lo que inicialmente no estaba diseñado. Era incompatible con la otra propuesta de estándar, la memoria expandida, que fue menos utilizada. La especificación recibió el nombre de XMS.
  • Extensión: En sistemas operativos como MsDos y Windows, es frecuente que el nombre de un fichero esté formado por dos partes, separadas por un punto. La primera parte (limitada en Dos a 8 letras de longitud) es el nombre propiamente dicho; la segunda parte (hasta 3 letras, en Dos) es la extensión, que suele indicar el tipo de información que guarda ese fichero.
  • Ext2: Sistema de archivos nativos de Linux y otras versiones de Unix.

F

  • FAQ: Recopilación de preguntas frecuentes sobre un cierto tema y sus respuestas (Frequently Asked Questions).
  • Fast Page RAM: Tipo de memoria RAM, anterior a la EDO, que era habitual en los primeros 486.
  • FAT: Tabla de situación de ficheros (File Allocation Table), algo así como un índice de qué datos hay en una unidad de disco, y dónde están situados dichos datos. Es el nombre que recibe el sistema de ficheros de MsDos.
  • FAT32: Sistema de ficheros empleado en Windows 98 (y en la segunda revisión de Windows 95). Es una evolución de FAT y VFAT.
  • Fax: Aparato diseñado para enviar y recibir imágenes en blanco y negro a través de la línea telefónica. El nombre es una abreviatura de FacSímil.
  • FDDI: Red en anillo, basada en fibra óptica, que permite velocidades de hasta 100 Mbps. (Fibre Distributed Data Interchange).
  • Fdisk: utilidad que permite administrar el espacio de un disco duro: básicamente crear o eliminar particiones. Está disponible en diversos sistemas operativos, como MsDos, OS/2, Windows NT, Linux, etc.
  • Fichero: (En inglés, File). Conjunto de información que se almacena para consultarse o utilizarse posteriormente. En un ordenador, normalmente cada fichero deberá tener un nombre con el que hacer referencia a él.
  • FIFO: Ver Cola.
  • Fija (coma): ver Coma.
  • File: Fichero, en inglés.
  • FIPS: Utilidad que permite cambiar las particiones existentes en un disco duro sin necesidad de vaciar primero todo su contenido. Se puede encontrar como accesorio de muchas distribuciones de Linux.
  • Firewall: (En español, "cortafuegos") Sistema de seguridad, encargado de proteger una red de área local de accesos no autorizados desde una WAN a la que esté conectada.
  • Firewire: Nombre comercial de IEEE 1394, un nuevo tipo de conexión digital, que permite velocidades a partir de 100 Mbps (millones de bits por segundo), y se pretende que llegue hasta los 400 MBps (millones de bytes por segundo).
  • Firmware: Es el "software que está dentro del hardware". Se refiere a los programas grabados en memorias ROM.
  • Flag: Indicador (algunos autores lo traducen literalmente por "bandera"). Se suele usar este nombre para indicadores de estado que sólo pueden tomar dos valores (verdadero o falso, encendido o apagado).
  • Flash: Tipo de tarjetas de memoria, presentes en muchos dispositivos electrónicos, como cámaras digitales.
  • Flotante (coma): ver Coma.
  • Floppy: Ver diskette.
  • Folder: (en español, "carpeta") Nombre que se da a los directorios en algunos sistemas, como Windows.
  • Formatear: Borrar por completo la información existente en un dispositivo de almacenamiento.
  • Formulario: En la publicación de páginas Web, un formulario es una página o parte de una página Web que el usuario completa y devuelve al servidor para su procesamiento.
  • Foros de discusión: ver News.
  • Forth: Lenguaje de programación.
  • Fortran: Lenguaje de programación, orientado a ingeniería (FORmula TRANslator).
  • FoxPro: Gestor de bases de datos, realizado por Microsoft.
  • FPU: Coprocesador matemático (Floating Point Unit).
  • FreeBSD: Sistema operativo UNIX de libre distribución.
  • Freeware: Aplicación informática que se puede copiar y distribuir libremente, y cuyo uso es gratis.
  • FrontPage: Programa de creación de páginas Web, desarrollado por Microsoft.
  • F-Prot: Conocido antivirus, desarrollado por Datafellows.
  • F-Secure: Conocido antivirus, desarrollado por Datafellows.
  • FTP: Protocolo estándar en Internet para transferencia de ficheros (File Transfer Protocol). También se suele usar este nombre para designar los programas de envío y recepción de ficheros que se apoyan en este protocolo.
  • Fuente: Programa escrito en un lenguaje de programación, antes de convertirse a ejecutable (ver Compilador) // Fuente: Tipo de letra (nomenclatura empleada por Windows).
  • Fujitsu: Conocido fabricante de ordenadores, impresoras, discos duros y otros tipos de hardware.

G

  • Gateway: En una red de ordenadores, es un dispositivo que se encarga de conectar dos redes de distinto tipo, y que es incluso capaz de transformar la información, adaptando el formato de paquetes de la primera red al que necesita la segunda. La mayoría de las veces se implementan por software. Trabajan a nivel de transporte (nivel 4 del estándar OSI) o superior.
  • Gb: Abreviatura de GigaByte.
  • Gbps: Miles de millones de bps.
  • GEM: Entorno gráfico desarrollado por Digital Research (Graphic Environment Manager). En el mundo de los compatibles PC fue desbancado por Windows, que es el estándar actual.
  • GIF: Es la Extensión que corresponde a un tipo de fichero gráfico de mapa de bits (Graphics Interchange Format).
  • GigaByte: Múltiplo del byte: un gigabyte son 1.024 MegaBytes, cerca de 1.000 millones de bytes.
  • GMT: Hora media en Greenwich (Greenwich Mean Time). No es un concepto exclusivo de informática, pero aparece en la configuración horaria de muchos programas y sistemas operativos. Por ejemplo, la hora en Madrid es GMT+1.
  • Gnome: Gestor de ventanas muy extendido en los sistemas operativos UNIX.
  • Gopher: Servicio de Internet, precursor de la WWW, que muestra la información en forma jerárquica, como en un directorio.
  • GPRS: Servicio para permitir el acceso a Internet desde ciertos móviles GSM, con velocidades entre 56 y 114 Kbps (General Packet Radio Service).
  • Grafo: Estructura de datos utilizada en algunos lenguajes de programación, en la cual cada elemento puede tener uno o varios predecesores y uno o varios sucesores.
  • GSM: Estándar en telefonía móvil digital (Global System for Mobile Communications).
  • GUI: Interfaz gráfica de usuario (Graphical User Interface). Entorno que muestra la información en pantalla en forma gráfica.
  • GwBasic: Intérprete de lenguaje Basic, creado por Microsoft, incluido en las primeras versiones de MsDos.

H

  • Hacker: Experto de la informática capaz de entrar en sistemas de acceso restringido, no necesariaente con malas intenciones.
  • Handheld PC: Ordenador que se puede sostener con una mano mientras se maneja. Es la categoría a la que suelen pertenecer los PDA.
  • Hardware: La parte "que se puede tocar" de un ordenador: caja (y todo su contenido), teclado, pantalla, etc.
  • Hayes: conocido fabricante de modems, el creador de los comandos AT.
  • HD: Disco duro (Hard Disk) // HD: Alta densidad (High Density). En el caso de diskettes de 3 1/2", la expresión Alta Densidad hace referencia a que tienen una capacidad de 1.44 Mb (formateado; 2 Mb nominales).
  • Hercules: Fabricante de tarjetas gráficas para ordenador. Frecuentemente se usa la palabra "Hercules" para hacer referencia a la primera tarjeta que creó este fabricante para los ordenadores IBM PC, que era capaz de mostrar 720x348 puntos en blanco y negro.
  • Hewlett-Packard: Prestigioso fabricante de hardware de casi todo tipo: ordenadores, impresoras láser y de inyección, plotters, etc.
  • Hexadecimal: sistema de numeración en base 16. Se emplean las cifras del 0 al 9 y las letras A a F (A=10, B=11, C=12, D=13, E=14, F=15), y cada posición de un número indica una potencia de 16. Por ejemplo, el número decimal 270 convertido a hexadecimal quedaría 10E (1x256 + 0x16 + 14x1).
  • Hipertexto: En un texto clásico, la estructura es totalmente lineal: se lee de principio a fin; en un hipertexto, se pretende poder romper esa estructura lineal, mediante "enlaces" (también llamados "Vínculos" o "Hipervínculos") que permiten saltar a otros temas relacionados, donde encontrar información ampliada. Las páginas Web de Internet (como ésta) son un ejemplo claro de Hipertexto.
  • Hipervínculo: ver Hipertexto.
  • HLP: Extensión de los ficheros de ayuda (HeLP).
  • Hoja de cálculo: Aplicación informática que permite manipular datos numéricos: hacer todo tipo de operaciones con ellos, mostrar gráficos, etc.
  • Host: Cualquier equipo conectado a una red, que proporciona servicios a equipos o usuarios remotos.
  • HP: Abreviatura de Hewlett-Packard.
  • HPFS: Sistema de ficheros nativo de OS/2 (High Performance File System)
  • HPGL: Lenguaje gráfico diseñado por Hewlett-Packard para sus plotters (Hewlett-Packard Graphics Language).
  • HSP: Nombre que reciben los módems que no tienen procesador digital de señal (DSP), sino que esta misión es realizada por el ordenador al que están conectados. Por ello, son ser más baratos, pero también suelen dar un rendimiento inferior y no funcionar con cualquier sistema operativo porque necesitan un controlador específico (Host Signal Processor).
  • HTM: Extensión bajo MsDos de los ficheros de tipo HTML.
  • HTML: HyperText Markup Language: el lenguaje de descripción de páginas habitual en Internet.
  • HTTP: El protocolo usado en las páginas del WWW (HyperText Transfer Protocol).

    • Hub: Concentrador. Es un dispositivo al que se pueden conectar mediante cables de red varios ordenadores. Así, desde cualquier ordenador se podrá acceder a cualquier otro, y el Hub se encargará de desconectar automáticamente los equipos que tengan problemas, sin que ello afecte al resto de la red. También se comienza a oir hablar de Hubs USB, que permiten conectar varios dispositivos USB a un mismo aparato, y resultan útiles cuando nuestro ordenador ya no tiene más conexiones USB disponibles.

I

  • IA: Siglas en español de Inteligencia Articial.
  • IANA: Internet Assigned Number Authority. Autoridad de Asignación de Números en Internet. Se trata de la entidad que gestiona la asignación de direcciones IP en Internet.
  • IBM: Importante fabricante de ordenadores y de software (International Business Machines).
  • IDE: Uno de los estándares en conexión de discos duros y dispositivos similares. Existe una variante mejorada, más rápida, llamada EIDE. // IDE: Entorno integrado de desarrollo: un entorno desde el que se pueden editar programas, compilarlos y depurarlos (Integrated Development Environment).
  • IEEE: Instituto de Ingenieros Eléctricos y Electrónicos, una institución americana responsable de la creación de una gran cantidad de estándares en electrónica e informática (Institute of Electrical and Electronics Engineers).
  • IEEE 1394: Ver Firewire.
  • IIOP: Internet InterORB Protocol, un protcolo de comunicación entre ORBs en Internet.
  • iMac: Ordenador desarrollado por Apple en 1998, que inicialmente estaba equipado con un procesador PowerPC G3 a 233 MHz, 32 Mb de memoria SDRAM, disco duro de 4 Gb, CdRom de 24x, monitor de 15 pulgadas, módem de 56 Kbps, etc.
  • Impresora: Dispositivo encargado de volcar a papel la información que maneja un ordenador. Hoy en día las más habituales son las de inyección, las láser y las matriciales, si bien existen otros muchos tipos, como las de margarita, las térmicas, las de sublimación, etc.
  • Informix: Conocido gestor de bases de datos.
  • Inkjet: ver inyección.
  • Inprise: Ver Borland.
  • Integrado (paquete). Ver suite.
  • Intel: Conocido fabricante de procesadores, creador de la gama X86.
  • Interface: Ver Interfaz.
  • Interfaz: Conexión de un ordenador con el exterior, o entre dos dispositivos.
  • Internet: Red de ordenadores a nivel mundial. Ofrece distintos servicios, como el envío y recepción de correo electrónico (e-mail), la posibilidad de ver información en las páginas Web, de participar en foros de discusión (News), de enviar y recibir ficheros mediante FTP, de charlar en tiempo real mediante IRC, etc.
  • InterNIC: Entidad administrativa de Internet que se encarga de gestionar los nombres de dominio en EEUU (los dominios .com, .net, .org, .edu, .gov y .mil; el nombre es abreviatura de Internet Network Information Center).
  • Intérprete: Ver compilador.
  • Intérprete de comandos: Ver Shell.
  • Interrupción: Cuando un procesador está esperando recibir información de un cierto dispositivo externo, tiene dos formas básicas de hacerlo: mirando continuamente si hay información disponible (modo de espera o modo "polling"), o dejando la posibilidad de que sea el dispositivo el que avise cuando la tenga preparada (modo interrupción). Suele haber varios (pocos) canales de interrupción, así como distintos niveles de prioridades.
  • Intranet: Red local privada que sigue la estructura (y los protocolos) de Internet.
  • Inyección: (o inkjet, o de "chorro de tinta") tipo de impresora cuyo funcionamiento se basa en disparar sobre el papel minúsculas gotas de tinta, que forman las imágenes o el texto.
  • I/O: Entrada/salida (Input/Output).
  • IP: Ver TCP/IP.
  • IP Address: Dirección de un ordenador dentro de una red con protocolo TCP/IP (4 números de 0 a 255, separados por puntos, ej: 194.179.5.205).
  • IPX/SPX: Conjunto de protocolos utilizados en redes Novell (Internet Packet eXchange - Intercambio de Paquetes entre Redes).
  • IR: Abreviatura de Infrarrojo (Infra Red).
  • IRC: Charla (escrita, no por viva voz) en tiempo real entre varias personas en Internet (Internet Relay Chat).
  • IRQ: Solicitud de interrupción (Interrupt ReQuest). Línea de interrupción que los dispositivos emplean para avisar al procesador de que necesitan su atención. Según el procesador, suele haber varias líneas (por ejemplo, 16), y cada dispositivo debería emplear una distinta, para evitar conflictos con otros dispositivos.
  • ISA: Tipo de arquitectura estándar de placas base, con ranuras de ampliación de 8 y 16 bits (Industry Standard Architectecture).
  • ISDN: Red Digital de Servicios Integrados o RDSI (Integrated Services Digital Network).
  • ISO: Organización Internacional para la creación de Standards.
  • ISO 9660: Formato estándar de grabación de CdRom.
  • ISP: Proveedor de servicios de Internet (Internet Services Provider). Una compañía que permite a sus usuarios tener acceso a Internet, normalmente a través de líneas telefónicas.
  • i386: Otro nombre dado al procesador 80386.
  • i486: Otro nombre dado al procesador 80486.

J

  • Java: Lenguaje de programación orientado a objetos, basado en C++, cada día más extendido, especialmente a través de Internet. Pretende ser un lenguaje totalmente portable entre distintos ordenadores, gracias a que no se compila a código máquina,sino a un lenguaje intermedio que luego es interpretado por la "máquina virtual Java", que sí es específica de cada plataforma. Esto le da una velocidad ligeramente inferior a la de los programas realizados en otros lenguajes compilados, como C++, a cambio de una mayor portabilidad (aparte de las mejoras que el lenguaje en sí incorpora sobre otros como C++).
  • Javascript: Lenguaje de Script para páginas Web, basado en la sintaxis de Java, evolucionado a partir del LiveScript de Netscape.
  • JAZ: Unidad de almacenamiento parecida a un diskette, pero con una capacidad de 1 Gb y de 2 Gb (según modelo) creada por Iomega.
  • JDBC: Conectividad de bases de datos Java. Las interfaces de acceso a datos basadas en ODBC para utilizar con el lenguaje Java.
  • JDK: Java Development Kit, un conjunto de aplicaciones que se utiliza para crear programas en lenguaje Java.
  • JFC: Java Foundation Classes, una serie de funciones ya creadas para que se puedan utilizar al crear programas en lenguaje Java.
  • Joystick: Palanca de control utilizada en muchos juegos.
  • JPEG, JPG: Es la Extensión que corresponde a un tipo de fichero gráfico de mapa de bits. Es un formato comprimido, que, al contrario que la mayoría, pierde definición al comprimir: se puede indicar la cantidad de compresión que se desea, pero cuanto más comprimamos, mayor pérdida de calidad tiene la imagen. Para fotografías digitalizadas con 640x480 puntos o más, un nivel de compresión entre 15 y 25 suele ser suficiente para reducir mucho el espacio ocupado por la imagen, pero a la vez que la pérdida de calidad no sea muy apreciable. (Joint Picture Expert Group, o Joint Photographic Expert Group, según los autores).
  • JScript: Implementación de JavaScript por parte de Microsoft, compatible con el JavaScript de Netscape Navigator 2.0.
  • Jumper: Interruptor que se usa para cerrar dos contactos eléctricos. Son frecuentes en placas base y tarjetas de expansión, como forma de indicar diferentes opciones de configuración.
  • JVM: Maquina Virtual Java (Java Virtual Machine).

K

  • K: Abreviatura de KiloByte.
  • Kb: Abreviatura de KiloByte.
  • Kbps: Miles de bps.
  • KDE: Gestor de ventanas muy extendido en los sistemas operativos UNIX (K Desktop Environment).
  • Kernel: núcleo de un sistema operativo.
  • Key: ver Clave.
  • Keyboard: Teclado, en inglés.
  • KiloByte: Múltiplo del byte: un kilobyte son 1.024 bytes.
  • Kylix: Herramienta de programación visual para Linux, basada en el lenguaje Pascal (análoga a Delphi), desarrollada por Borland.
  • K5: Procesador realizado por AMD, intermedio entre el 486 y el Pentium de Intel.
  • K6: Procesador realizado por AMD, similar al Pentium creado por Intel y compatible con las instrucciones de la familia 80x86. Existe una versión mejorada, llamada K6-2, que incorpora una serie de instrucciones nuevas, destinadas a mejorar el rendimiento en 3D. La última versión de esta gama ha sido el K6-3.
  • K7: Procesador realizado por AMD, posterior a la gama K6, y cuyo nombre definitivo en el mercado ha sido Athlon.

L

  • LAN: Red de área local (Local Area Network).
  • Laptop: Ordenador portatil en el que se levanta una tapa que alberga la pantalla, y en la parte inferior queda el teclado y la circuitería interna
  • Láser: tipo de impresora se suele basar en un mecanismo muy similar al de las fotocopiadoras. La base de su funcionamiento suele ser: un elemento fotoconductor (que conduce la electricidad cuando recibe la luz, normalmente llamado "tambor") se carga eléctricamente para que pueda atraer a la tinta en polvo (tóner). Mediante espejos y lentes (en las fotocopiadoras convencionales) o mediante un láser (o una barra de LEDs, en ciertas impresoras y/o copiadoras digitales) se descarga el fotoconductor en ciertas zonas, conformando lo que será la imagen a imprimir (una imagen "latente", no visible, formada por cargas eléctricas). En estas zonas cargadas del fotoconductor se deposita la tinta en polvo (tóner), desde donde después se transfiere al papel, en el que se fija mediante calor y/o presión.
  • LaserJet: Gama de impresoras láser creada por Hewlett-Packard.
  • LCD: Pantalla de cristal líquido (Liquid Crystal Display).
  • LED: Diodo emisor de luz (Light Emitting Diode).
  • Lenguaje de programación: ver Programa.
  • Lenguaje máquina: ver Código Máquina.
  • LIFO: Ver Pila.
  • LILO: Cargador de Linux (LInux LOader). Es un programa que se instala en el MBR del disco duro y que permite elegir entre distintos sistemas operativos que se encuentren instalados en distintas particiones del disco duro.
  • Link: Enlace (ver Hipertexto).
  • Linux: Versión de libre distribución (gratis) del sistema operativo Unix, desarrollada inicialmente por Linus Torvalds, y mejorada gracias a las contribuciones de programadores de todo el mundo.
  • LISP: Lenguaje de programación muy usado en Inteligencia Articial.
  • Lista: Estructura de datos utilizada en muchos lenguajes de programación, en la cual cada elemento tiene un predecesor (excepto el primero) y un sucesor (excepto el último).
  • Log: Registro, en inglés. Muchos programas y sistemas crean distintos ficheros de registro en los que van anotando los pasos que dan (lo que hace un cierto usuario, como transcurre una conexión, etc).
  • Login: Control de acceso a un cierto sistema, normalmente basado en el nombre de usuario y una contraseña (password).
  • Logo: Lenguaje de programación, que durante un tiempo se consideró el más adecuado para introducir en la informática a niños de corta edad.
  • Lotus: Conocida casa de software, creadora de programas como 1-2-3, Ami Pro, WordPro, Freelance, Notes, etc.
  • LPT: nombre que reciben bajo DOS los puertos paralelo (para conectar impresoras). Lo habitual es que un ordenador tenga un puerto de este tipo, que se designaría LPT1.
  • LZH: Extensión de ficheros comprimidos. El compresor correspondiente es LHA o LHARC.

M

  • Mac:Ver Macintosh.
  • MacOS: Sistema operativo de los ordenadores Apple Macintosh.
  • Macintosh: Conocida gama de ordenadores realizada por Apple, inicialmente basada en procesadores de la serie 68000 de Motorola, y hoy en día en procesadores PowerPC.
  • Mainboard: En inglés, placa base.
  • MAN: red de Area Metropolitana: red de ordenadores con tamaño intermedio entre las LAN y las WAN.
  • Margarita: tipo de impresora cuyo funcionamiento se basa en el mismo sistema que el de las máquinas de escribir: unos "pétalos" (de ahí el nombre) tienen dibujados las letras y los símbolos. Estos pétalos golpean una cinta entintada, lo que hace que aparezca el texto sobre el papel. Hoy en día están en desuso porque son lentas, ruidosas y no permiten dibujar ni emplear distintos tipos y tamaños de letra (salvo que se cambie toda la margarita).
  • Matricial: (o "de agujas") tipo de impresora cuyo funcionamiento se basa en unas agujas que golpean una cinta entintada, lo que hace que aparezcan sobre el papel unos puntos, que forman las imágenes o el texto.
  • Matrox: Conocido fabricante de tarjetas gráficas. Tiene gamas muy conocidas, como la Millenium, Mystique y la Productiva.
  • Mb: Abreviatura de MegaByte.
  • Mbps: Millones de bps.
  • MBR: Master Boot Record, sector de arranque del disco duro.
  • MCA: Abreviatura de Microchannel Architecture.
  • McAfee: Casa desarrolladora de antivirus (el conocido Scan o ViruScan).
  • MCGA: Adaptador gráfico multicolor (MultiColor Graphics Adapter). Tarjeta gráfica de ordenadores PC y compatibles, evolución de la EGA, que permitía trabajar también a 256 colores con 320x200 puntos, y en blanco y negro con 640x480 puntos.
  • MDA: Adaptador de pantalla monocromo (Monochrome Display Adapter): uno de los primeros adaptadores de pantalla disponibles para ordenadores PC. No era capaz de mostrar gráficos, sólo texto en blanco y negro.
  • MDB: Extensión de los ficheros creados con Access.
  • ME (o Millenium Edition): Nombre que recibe la versión de Windows posterior a Windows 98.
  • Mega: prefijo que quiere decir "un millón".
  • Megabyte: Múltiplo del byte: un megabyte son 1.024 KiloBytes, cerca de un millón de bytes.
  • Megapixel: Un millón de pixels. Es la unidad habitual en la que se mide la resolución de una cámara digital. Por ejemplo, una cámara capaz de captar imágenes de 640x480 puntos tendrá una resolución de aproximadamente 0,3 megapixels; si es capaz de captar 1280x960 puntos, su resolución sería de cerca de 1,3 megapixels.
  • Mendocino: Ver Pentium.
  • MFC: Microsoft Foundation Classes, una serie de funciones ya creadas para que se puedan utilizar al crear programas para Windows.
  • MFlops: Millones de operaciones en coma flotante (con números reales) por segundo, una medida de velocidad de ordenadores.
  • MFM: Sistema utilizado por muchos de los primeros discos duros disponibles en equipos compatibles PC y XT.
  • MHz: Megahertzios, es una medida de frecuencia (número de veces que ocurre algo en un segundo). En el caso de los ordenadores, un equipo a 200 MHz será capaz de dar 200 millones de pasos por segundo. En la velocidad real de trabajo no sólo influyen los MHz, sino también la arquitectura del procesador (y el resto de los componentes); por ejemplo, dentro de la serie X86, un Pentium a 60 MHz era cerca del doble de rápido que un 486 a 66 MHz.
  • Micra: Unidad de longitud. Una micra es la milésima parte de un milímetro.
  • Micro: en informática se suele usar como abreviatura de Microprocesador.
  • Microchannel (o MCA): Tipo de arquitectura estándar de placas base, basada en ISA, con ranuras de ampliación de 32 bits, desarrollada por IBM y hoy en día abandonada.
  • Microprocesador: ver Procesador.
  • Microsoft: Casa desarrolladora de software, creadora de sistemas operativos como MsDos y Windows, así como de aplicaciones informáticas de todo tipo.
  • MID: Extensión que corresponde a un tipo de ficheros de sonido (habitual en Windows).
  • MIDI: Conexión normalizada para instrumentos musicales (Musical Instruments Device Interface).
  • MIME Un estándar que permite enviar ficheros de cualquier tipo adjuntos a un mensaje de texto a través de Internet. (Multi-purpose Internet Mail Extensions).
  • Millenium (o Millenium Edition, o ME): Nombre que recibe la versión de Windows posterior a Windows 98.
  • Minix: Versión del sistema operativo UNIX desarrollada con fines educativos por el profesor Andrew Tannenbaum.
  • MIPS: Millones de instrucciones por segundo, una medida de velocidad de ordenadores // MIPS: familia de procesadores de tipo RISC, que equipan muchas estaciones de trabajo, como las de Silicon Graphics.
  • MMX: MultiMedia eXtensions, conjunto de nuevas instrucciones añadidas a la gama alta de los procesadores Pentium, con la intención de acelerar las operaciones más frecuentes en multimedia.
  • MOD: Extensión que corresponde a un tipo de ficheros de sonido.
  • Módem: Aparato que permite a un ordenador enviar y recibir información a traves de una línea analógica, normalmente la línea telefónica (MOdulador-DEModulador).
  • Modula-2: Lenguaje de programación estructurado y modular.
  • Monitor: ver pantalla.
  • Motorola: Conocido fabricante de procesadores, autor de la serie 68000 que equipaba los primeros ordenadores Macintosh de Apple, y coautor de los PowerPC que equipan los PowerMac.
  • Mouse: ratón, en inglés.
  • MPEG: Formato de video comprimido para ordenador (Motion Pictures Expert Group), con pérdida de calidad.
  • MP3: Formato de audio comprimido para ordenador, un subformato del MPEG (MPEG-1 Layer 3).
  • ms: milisegundos (milésimas de segundo), unidad usada para medir tiempos de acceso.
  • MsDos: Ver DOS.
  • MSX: Gama de ordenadores desarrollados por diversas casas (como Sony, Philips, Toshiba, Spectravideo, etc.) a mediados-finales de los años 80, siguiendo un estándar común. Estaban basados en el procesador Z80. Tenían capacidad de realizar gráficos con 16 colores, sonido estéreo, memoria entre 32 y 128K. Posteriormente surgió una versión mejorada, denominada MSX2.
  • Multimedia: Se tiende cada vez más a que los ordenadores no sean sólo capaces de manejar información en forma de texto, sino también imágenes de gran tamaño y colorido, o incluso sonidos y secuencias de video. Esta capacidad es lo que se conoce como Multimedia.
  • Multitarea: Es cuando un ordenador es capaz de realizar más de una tarea a la vez. Puede ser en paralelo (si tiene más de un procesador) o concurrente (si sólo tiene uno).

N

  • Navegador: Aplicación informática empleada para "navegar" por la red. Los más conocidos son el Internet Explorer de Microsoft y el Netscape Navigator (parte de Netscape Communicator).
  • Navegar: Nombre que se da al hecho de buscar información en Internet, empleando la WWW y sus enlaces para "saltar" de una página a otra.
  • NetBEUI: Protocolo de red sencillo, elaborado por Microsoft.
  • Netscape: Casa desarrolladora de software, creadora de programas como el conocido navegador de internet Navigator, que ahora se distribuye junto con otras utilidades (como Messenger, para el correo electrónico, Collabra para los grupos de noticias y Composer para la creación de páginas Web) bajo el nombre de Communicator.
  • News: Uno de los servicios que ofrece Internet. Se trata de un foro de discusion abierta, formado por distintos grupos de noticias temáticos, en los que cualquiera puede escribir mensajes públicos y leer los existentes.
  • NFS: Sistema de archivos de red (Network file System), un sistema de archivos que permite a un ordenador "montar" directorios y sistemas de archivo remotos, de otro(s) ordenador(es) de la red.
  • NMI: Interrupción no enmascarable. Es la entrada de una interrupción que tiene la máxima prioridad y que siempre es aceptada por el procesador.
  • NNTP: Protocolo de transferencia de noticias a través de la red. El protocolo usado para distribuir mensajes de noticias de red a servidores NNTP y a clientes NNTP (lectores de noticias) en Internet. NNTP proporciona distribución, consulta, recuperación y exposición de artículos de noticias mediante el uso de una transmisión confiable basada en secuencias de noticias en Internet. NNTP está diseñado de forma que los artículos de noticias se almacenan en un servidor en una base de datos centralizada, de manera que los usuarios pueden seleccionar elementos específicos que leer. También se proporciona indización, las referencias cruzadas y la caducidad de mensajes antiguos. Está definido en RFC 977.
  • Nodo: un ordenador que está conectado a una red.
  • Not: Operador a nivel de bits: negación, que cambia los ceros por unos y viceversa. // No, en inglés (se suele usar como parte de una condición en muchos lenguajes de programación).
  • Notebook: ordenador portátil (laptop) cuyo tamaño cerrado es similar a un A4.
  • Noticias: ver News.
  • ns: nanosegundos (milésimas de millonésimas de segundo), unidad usada para medir tiempos de acceso.
  • NT: Versión de Windows diseñada para entornos profesionales.
  • NTFS: Sistema de archivos nativo de Windows NT.
  • Number Nine: Conocido fabricante de tarjetas gráficas.

O

  • Oberon: Lenguaje de programación orientado a objetos, diseñado por el creador de Pascal y Modula-2.
  • Objective C: Lenguaje de programación orientado a objetos basado en el lenguaje C.
  • Océ: Conocido fabricante de plotters, copiadoras e impresoras.
  • OCR: Aplicación informática que trata de convertir a texto las imágenes adquiridas con un scanner (reconocimiento óptico de caracteres).
  • Octal: sistema de numeración en base 8. Se emplean las cifras del 0 al 7, y cada posición de un número indica una potencia de 8. Por ejemplo, el número decimal 101 convertido a octal quedaría 145 (1x64 + 4x8 + 5x1).
  • Octeto: conjunto de 8 bits (traducción al castellano de la palabra "byte").
  • ODBC: Conectividad abierta de bases de datos (Open Database Connectivity). Una interfaz de programación de aplicaciones (API), que permite a las aplicaciones tener acceso a bases de datos multiplataforma desde diversas especificaciones estándar de orígenes de datos.
  • Office: Suite realizada por Microsoft, que incluye aplicaciones como Word, Excel, Outlook (y opcionalmente otras como Access o Publisher).
  • Off-Line: Término que se suele utilizar para referirse a todo tipo de operaciones que se realizan sin estar conectado a una red (se traduciría por "fuera de línea", y lo opuesto en "On-Line").
  • Ofimática: Nombre que se suele dar a la informática aplicada a la oficina: proceso de textos, hojas de cálculo, etc.
  • OLE: Forma de compartir información entre distintas aplicaciones en Windows: "Enlace e incrustación de objetos" (Object Linking & Embedding).
  • Olivetti: Conocido fabricante de ordenadores, impresoras, máquinas de escribir, etc.
  • On-Line: Término que se suele utilizar para referirse a todo tipo de operaciones que se realicen mientras se está conectado a una red (se traduciría por "en línea", y lo opuesto en "Off-Line").
  • OOP: Programación Orientada a Objetos o POO (Object Oriented Programming).
  • OpenLinux: Versión del sistema operativo Linux, recopilada por Caldera.
  • Or: Operador a nivel de bits: suma lógica, que da como resultado 1 si unos de los bits es 1 y 0 si los dos bits son 0. // O, en inglés (se suele usar como parte de una condición en muchos lenguajes de programación).
  • Oracle: Conocido gestor de bases de datos.
  • ORB: Agente de Petición de Objeto (Object Request Broker). La idea básica es la siguiente: en una aplicación cliente/servidor, los servicios a los que accedemos pueden no estar disponibles siempre en un mismo servidor, de modo que estos servicios se solicitan a un agente, que es el que realmente conecta a la aplicación cliente con la aplicación servidor, sin que la aplicación cliente necesite saber dónde están ubicados físicamente estos servicios.
  • Organizer: Organizador personal realizado por Lotus.
  • OS: Abreviatura de Operating System (Sistema Operativo).
  • OSI: Modelo para la interconexión de sistemas abiertos (Open Systems Interconnection). Es un modelo teórico de conexión de sistemas, propuesto por ISO, estructurado en 7 capas (física, enlace, red, transporte, sesión, presentación y aplicación).
  • OS/2: Sistema operativo multitarea de IBM creado para ordenadores PC, con interface gráfica. Hoy en día está en desuso.
  • OS/400: Sistema operativo multitarea y multiusuario creado por IBM para sus sistemas AS/400.
  • Outlook: Organizador personal realizado por Microsoft.

P

  • Paginación: Sistema de gestión de memoria, en el que tanto la memoria física como la memoria virtual se dividen en bloques de igual tamaño (llamadas páginas), que son las que se vuelcan a disco a medida que se va llenando la memoria física, o se recuperan de disco cuando vuelven a ser necesarias.
  • Palm: conocido fabricante de PDAs.
  • Palmtop: ordenador portátil (laptop) cuyo tamaño cerrado es similar al de la palma de la mano.
  • Panda: Casa española desarrolladora de software, creadora de Panda Antivirus (entre otras aplicaciones).
  • Pantalla: La pantalla (o monitor) es el dispositivo encargado de mostrar la información mientras trabajamos con el ordenador. Hoy en día es habitual que las pantallas sean de color, aunque todavía se pueden encontrar pantallas monocromas: de fósforo verde, ámbar o blanco. Su tamaño se suele indicar en pulgadas; por ejemplo una pantalla de 15" tiene una diagonal de 15 · 2,54 = 38 cm.
  • Paquete: Fragmento de información enviada a través de una red. Suelen tener un tamaño máximo fijo, y estar formados por información binaria que representa datos y un encabezado con un número de identificación., direcciones de origen y de destino, y datos de control de errores.
  • Paradox: Gestor de bases de datos, realizado por Borland.
  • Paralelo: En una comunicación paralelo, los bits de información que forman un byte se envían todos a la vez, por hilos distintos. Esto permite mayor velocidad que en una conexión serie, aunque a veces es a cambio de que la conexión sea unidireccional, o bidireccional pero más rápida en un sentido que en el otro. // Paralelo: se habla de procesamiento paralelo cuando un ordenador es capaz de realizar más de una tarea a la vez gracias a que posee varios procesadores entre los que repartir el trabajo.
  • Paridad: método sencillo de comprobación de errores en una comunicación, que consiste en añadir un bit al final de cada bloque de datos, de forma que el número total de unos sea par (paridad par) o impar (paridad impar). Al recibir la información en el otro extremo, se cuenta el número total de unos y se comprueba si coincide con lo que indica el bit de paridad.
  • Partición: Consiste en dividir un disco duro en varias partes, cada una de las cuales se comportará como si fuera un disco duro independiente de los demás. Es la opción más razonable (a veces imprescindible) para instalar varios sistemas operativos en un mismo ordenador.
  • Pasarela: Ver Gateway.
  • Pascal: Lenguaje de programación estructurado, de propósito general.
  • Password: Clave de acceso o contraseña necesaria para acceder a un determinado sistema.
  • Patch: "Parche" que se aplica a un programa, normalmente con la intención de corregir algún "bug".
  • PC: Ordenador personal (Personal Computer). Esta abreviatura proviene del IBM Personal Computer, creado por la casa IBM a principios de los 80. El PC original trabajaba con un procesador 8086 a 4,77 Mhz y 64 Kb de memoria. Desde entonces es frecuente hablar de los "PC" refiriéndose a los ordenadores compatibles con el IBM PC original (capaces de usar cualquier programa que fuera diseñado para aquel), basados en procesadores de la serie X86 de Intel.
  • PCB: Placa de circuito impreso (Printed Circuit Board).
  • PCI: Tipo de arquitectura estándar de placas base, con ranuras de ampliación de 32 o 64 bits, usada en los equipos con procesador Pentium y superiores (y algunos 486). Es la abreviatura de Peripheral Component Interconnection.
  • PCMCIA: Estándar en ranuras de ampliación para ordenadores portátiles (Personal Computer Memory Card International Association).
  • PCX: Es la Extensión que corresponde a un tipo de fichero gráfico de mapa de bits.
  • PDA. Ordenador portátil de muy reducido tamaño, diseñado como terminal de aquisición de datos o como organizador personal (Personal Digital Assistant, asistente digital personal).
  • PDF: Es la Extensión que corresponde a un tipo de fichero (un libro electrónico) creado con Adobe Acrobat.
  • Pentium: Procesador de 32 bits realizado por Intel, evolución del 80486 (y compatible con él y con toda la familia x86), con velocidades a partir de 60 MHz (hasta 233 Mhz en su versión "normal", y por encima de 400 Mhz en versiones mejoradas como los Pentium II). Variantes: el Pentium MMX reconoce una serie de instrucciones nuevas, pensadas para acelerar las operaciones más frecuentes en multimedia, y tenía velocidades de 133 a 233 MHz; el Pentium Pro es una versión orientada al mercado más profesional, y existen placas base que permiten montar varios de estos procesadores trabajando a la vez (en paralelo); el Pentium II es una mejora del MMX, que se "pincha" en una ranura especial (Slot 1) y tiene velocidades de 233 a 450 MHz; el Celeron es una variante más barata del Pentium II, que no incorpora caché de primer nivel; el Mendocino es una variante mejorada del Celeron, que incorpora 128k de caché de primer nivel.
  • PerfectOffice: Suite realizada por Novell, que incluye aplicaciones como WordPerfect y Quattro Pro.
  • Periférico: Dispositivo auxiliar que se puede conectar a un ordenador (por ejemplo: una impresora).
  • Perl: Lenguaje interpretado, muy utilizado en la creación de CGIs.
  • PGP: Pretty Good Privacity. Paquete de encriptacion basado en clave publica, creado por Phil Zimmerman.
  • Pila: Estructura de datos utilizada en muchos lenguajes de programación, en la cual cada elemento tiene un predecesor (excepto el primero) y un sucesor (excepto el último). Además, existe una limitación en el manejo de esos datos: los elementos "entran" (se introducen) por un extremo y "salen" (se leen) por ese mismo extremo. A las estructuras con esta propiedad se les suele llamar LIFO (Last In First Out: el último en entrar es el primero en salir).
  • PING: Un comando usado para comprobar las conexiones a uno o más hosts remotos. La utilidad ping emplea paquetes ICMP de petición de eco y respuesta de eco para determinar si un sistema IP concreto de una red es funcional. La utilidad ping es útil para diagnosticar fallos IP de la red o del enrutador (Packet INternet Groper).
  • Pixel: Es el elemento de menor tamaño que forma una imagen ("un punto"). Abreviatura de Picture Element.
  • Placa base: Placa de circuito impreso en la que "se pinchan" la mayoría de los demás componentes de un ordenador.
  • PLC: Autómata programable.
  • Plotter: un tipo de impresora diseñado especialmente para trazar imágenes lineales. Los primeros usaban plumillas, pero hoy en día van siendo cada vez más frecuentes los de inyección, que tienen mayor facilidad para realizar dibujos no lineales y en múltiples colores.
  • Plug and Play: Es una expresión que se podría traducir por "enchufar y listo". Hace referencia a sistemas operativos que sean capaces de detectar automáticamente los dispositivos que se les instalen (normalmente, los propios dispositivos también deberán cumplir ciertas condiciones).
  • PNG: Es la Extensión que corresponde a un tipo de fichero gráfico de mapa de bits (Portable Network Graphics).
  • PNP: Abreviatura de Plug and Play.
  • Polling: Ver Interrupción.
  • POO: Programación Orientada a Objetos, en español (es más frecuente ver las siglas en inglés: OOP).
  • POP: Un protocolo utilizado para recibir correo electrónico en Internet (Post Office Protocol - Protocolo de oficina de correos).
  • Portable: se dice que un programa informático (o una aplicación, o un sistema operativo) es portable cuando se puede hacer funcionar fácilmente en otro sistema distinto a aquel en el que fue diseñado.
  • Portal: Página en Internet diseñada para servir como punto de partida en nuestra navegación. Suelen incoporar distintos servicios, como un directorio de páginas por distintos temas, un buscador de información, noticias, chats, etc.
  • Portapapeles: En los sistemas Windows, el portapapeles es donde van a parar los datos que se "copian" o "cortan" para después "pegar" en otro sitio.
  • Posix: Portable Operating System Interface for Computer Environments, conjunto de estándares que definen una serie de aspectos de un sistema operativo, que se diseñó para garantizar portabilidad entre distintos sistemas UNIX.
  • POST: Power On Self Test. AutoTest de Encendido. Serie de comprobaciones que hace un ordenador al ser encendido.
  • Postscript: Un lenguaje gráfico de descripción de páginas, muy utilizado en impresoras de gama media y alta.
  • PowerMac: Ordenadores Macintosh de Apple de última generación, basados en procesadores PowerPC.
  • Powerpoint: Aplicacion para crear presentaciones, realizada por Microsoft.
  • PPM: Páginas por minuto: es la unidad en que se mide la velocidad de impresión de las impresoras láser y en las de inyección de tinta.
  • PPP: Puntos por pulgada: una forma muy habitual de medir la resolución (nivel de detalle) con que puede trabajar una impresora o un scanner (en inglés, DPI) // PPP: Un protocolo estándar en Internet para conexiones a través del puerto serie (Point-to-Point Protocol). Es más reciente y robusto que SLIP.
  • Procesador: El "cerebro" del ordenador. Su velocidad de trabajo se mide en Megahertzios (MHz) y su capacidad de proceso por el número de bits que es capaz de manejar a la vez (por ejemplo: 32 bits, o 64 bits).
  • Procesador de texto. Un tipo de aplicación informática que permite escribir textos de todo tipo, desde cartas hasta libros. Hoy en día es frecuente que permitan usar distintos tipos de letra, incluir imágenes y tablas de datos, escribir en columnas, añadir ecuaciones matemáticas, etc.
  • Programa: Un conjunto de órdenes para un ordenador. Un programa puede estar formado por apenas unas pocas órdenes (por ejemplo, uno que sume dos números) o por varios miles de órdenes (como un programa de gestión completo para una empresa). Cuando se trata de un programa ya terminado que se compra, se suele hablar de una Aplicación Informática. Los programas se deben escribir en un cierto lenguaje de programación. Los lenguajes de programación que se acercan más al lenguaje humano que al del ordenador reciben el nombre de "lenguajes de alto nivel" (como Pascal); los que se acercan más al ordenador son los de "bajo nivel" (como el ensamblador). Lo más habitual es crear los programas en un lenguaje de alto nivel (llamado "fuente") y después convertirlos al lenguaje propio del ordenador ("compilarlos" para obtener un "ejecutable").
  • PROM: Memoria ROM programable (Programmable ROM).
  • Protegido (modo): Los procesadores de la serie x86 de Intel tienen, a partir del modelo 80386, varios modos de trabajo, siendo los más habituales el modo real y el modo protegido. En el modo real, el procesador trabaja como si fuera un 8086, con todas sus limtiaciones: no se puede acceder directamente a más de 640K de memoria, y esta memoria está dividida en bloques (segmentos) de 64K de tamaño. En el modo protegido, se puede acceder a toda la memoria que realmente posee el ordenador, y además esta memoria está disponible de forma lineal, sin estar dividida en segmentos. (Nota: también había un modo protegido en los procesadores 80286, poco utilizado, y no compatible con el de los 80386 y superiores).
  • Protocolo: Normas a seguir en una cierta comunicación: formato de los datos que debe enviar el emisor, cómo debe ser cada una de las respuestas del receptor, etc.
  • Proxy: Software que permite a varios ordenadores acceder a Internet a través de una única conexión física. Según lo avanzado que sea, puede permitir acceder a páginas Web, FTP, correo electrónico, etc. Es frecuente que también incluyan otros servicios, como cortafuegos (FireWalls).
  • Puente: En una red de ordenadores, es un dispositivo que se encarga de conectar dos redes o dos segmentos de red, y que es capaz de discriminar cuales de los "paquetes" recibidos en un extremo deben pasar al otro lado.
  • Pulgada: Unidad del sistema inglés de longitud, equivalente a 2,54 cm.

Q

  • QBasic: Intérprete de lenguaje Basic, creado por Microsoft, incluido en las últimas versiones de MsDos.
  • QBE: Query By Example, lenguaje de consulta a bases de datos, en el que dichas consultas se realizan en una tabla en la que se escriben "datos de ejemplo".
  • Quattro: Aplicación de hoja de cálculo realizada por Borland.
  • Quick Basic: Compilador de lenguaje Basic para MsDos, creado por Microsoft.
  • Quick C: Compilador de lenguaje C para MsDos, creado por Microsoft.
  • Q&A: Preguntas y respuestas (Questions & Answers).

R

  • RAID: Diversas estrategias que permiten una mayor tolerancia a fallos en un sistema. Existen seis niveles RAID. Por ejemplo, el estándar RAID 1 consiste en el empleo de discos espejo, de modo que si el disco duro principal falla, todo su contenido se encuentra reflejado también en el disco duro secundario, que pasa a actuar como principal (Redundant Array of Inexpensive Disks: Matriz redundante de discos de bajo coste).
  • RAM: Memoria de acceso directo (Random Access Memory). Normalmente se usa este nombre para referirse a memorias en las que se puede leer y también escribir (RWM). En los últimos PC es habitual que se use Fast Page Ram (386 y anteriores), EDO Ram (486 y Pentium) y SDRAM (últimos Pentium, Pentium MMX y superiores).
  • RAR: Extensión de ficheros comprimidos. El compresor correspondiente es el RAR.
  • RAS: Servidor de acceso remoto, nombre que recibe en Windows NT la posibilidad de conectar a un ordenador desde otro distante, a través de la línea telefónica.
  • Raster: Tipo de imagen (ver BitMap).
  • Rasterizar: Convertir una imagen de vectorial a bitmap.
  • Ratón: Dispositivo utilizado para comunicarse con el ordenador. Permite señalar zonas de la pantalla, como modo de indicar al ordenador lo que deseamos hacer.
  • RDSI: Red Digital de Servicios Integrados: red telefónica digital, con velocidades habitualmente de 64 Kbps o 128 Kbps.
  • Real (modo): modo de trabajo de los procesadores 80x86 (ver protegido).
  • Red de ordenadores: Conjunto de ordenadores conectados entre sí con el fin de compartir recursos (por ejemplo, impresoras) e información. La conexión puede ser directa (por cable) o no (por vía telefónica, por ejemplo). En inglés, la palabra “red” se traduce por Network, y según el espacio que abarquen o la distancia a la que se encuentren los ordenadores que integran la red, se suele distinguir entre LAN (red de área local, Local Area Network) y WAN (red de área amplia, Wide Area Network). No hay unos límites claros entre un tamaño de red y otro, y de hecho hay autores que también distinguen un tamaño intermedio (MAN).
  • Red Hat: Una de las casas especializadas en realizar distribuciones de Linux.
  • Regrabador: Grabador de CDs que es capaz de guardar información también en discos regrabables (CD-RW). Hoy en día, practicamente cualquier grabador de CDs será también regrabador.
  • Registro: En el mundo de las bases de datos, cada una de las fichas que componen una tabla.
  • Release: (en español, "revisión" o "versión"). Es habitual que una aplicación software sufra modificaciones, mejoras o correcciones. El número de versión suele indicar el avance de los cambios. Suelen ser números correlativos, y frecuentemente son dos cifras separadas por un punto. Por ejemplo, el paso de la versión 2 a la 3 de una aplicación suele conllevar cambios significativos, mientras que el paso de la 3.0 a la 3.1 indica cambios de menor importancia; el siguiente grupo de mejoras fuertes llevaría a la versión 4.0. Hay quien afina más, utilizando tres cifras en vez de dos: 1.1.56. Algunos fabricantes usan el número de año de lanzamiento (p.ej.: Office 97) en vez de números consecutivos. Nota: hay algún autor que traduce textualmente por “liberar” el hecho de que aparezca en el mercado una nueva versión de un cierto software; posiblemente “lanzar” (por ejemplo) sería una traducción algo más correcta.
  • Repetidor: En una red de ordenadores, es un dispositivo que se encarga de regenerar la señal para que se puedan alcanzar mayores distancias en la comunicación. Trabajan a nivel físico (nivel 1 del estándar OSI). Por ejemplo, en el caso de Ethernet, la longitud que puede tener un segmento de red en condiciones normales es de 500 metros, pero se pueden incluir hasta 4 repetidores que permiten que un tramo llegue hasta los 2.500 m. En algunos casos, los repetidores son capaces de transformar la señal de un tipo de cable a otro distinto.
  • Reset: ver arranque.
  • Revisión: Ver release.
  • RFC: Request for Comments (Petición de comentarios). La serie de documentos, iniciada en 1969, que describe el conjunto de protocolos de Internet y los experimentos relacionados. Sólo unos pocos de los RFC describen estándares de Internet, pero todos los estándares de Internet están escritos como RFC.
  • RGB: Descomposición de colores en función de tres componentes básicos: rojo, verde y azul (Red, Green, Blue). Es la descomposición habitual en las pantallas de ordenador. Se trata de colores aditivos: cuanto mayor es la cantidad de rojo, verde y azul, más se aproxima el color resultante al blanco.
  • Ripper: Utilidad que se emplea para extraer información de cierto soporte. Ejemplos habituales son las utilidades que extren la música de un Cd, la película de un DVD o imágenes y otros recursos que estén incluidos dentro de un fichero ejecutable o de datos.
  • RISC: Un tipo de procesadores que reconoce un conjunto pequeño de órdenes, pero que es capaz de responder a esas órdenes a una gran velocidad (opuesto a CISC).
  • RJ-45: Nombre de los conectores usados en los cables UTP.
  • RLL: Sistema utilizado por muchos de los primeros discos duros disponibles en equipos compatibles PC y XT.
  • RMI: Invocación de métodos remotos (Remote Method Invocation), consiste en que un objeto acceda a un método (una de las funcionalidades) de otro objeto remoto (que esté situado en otro punto de una red).
  • ROM: Memoria sólo de lectura (Read Only Memory).
  • Root: Administrador de un sistema Unix. // Root: Raíz, en inglés; se suele utilizar para designar el directorio (o carpeta) principal de una cierta unidad de disco.
  • Router: En una red de ordenadores, es un dispositivo que se encarga de conectar dos redes o dos segmentos de red, y que es capaz de discriminar por cual de sus salidas debe enviar cada uno de los paquetes de información que recibe. Trabajan a nivel de red (nivel 3 del estándar OSI).
  • RSA: Conocido sistema de criptografía de clave pública. Su nombre viene de los apellidos de sus inventores: Rivest, Shamir y Adleman.
  • RS232: Es una conexión serie normalizada, muy frecuente en ordenadores personales. Hay dos conectores normalizados, de 9 pins (DB9) y de 25 pins (DB25).
  • RTB: Red Telefónica Básica: la red telefónica convencional, analógica.
  • RTC: Red Telefónica Conmutada: otro nombre para la red telefónica de voz.
  • Run: Ver Ejecutar.
  • RWM: Memoria en la que se puede leer escribir (Read/Write Memory).
  • RX: En el mundo de las comunicaciones, abreviatura de Recepción o Recibiendo.

S

  • SAI: Sistema de alimentación ininterrumpida (en inglés, UPS).
  • SB: Se suele usar como abreviatura de SoundBlaster.
  • Scanner: Dispositivo capaz de leer imágenes estáticas y enviarlas al ordenador. Desde el ordenador habrá la posibilidad de imprimir estas imágenes, modificarlas, etc.
  • SCO: Santa Cruz Operation, casa desarrolladora de uno de los UNIX comerciales más extendidos.
  • SCSI: Small Computer System Interface, una conexión estándar para diversos dispositivos como discos duros o scanners. Los discos duros SCSI suelen ser más rápidos (y más caros) que los IDE.
  • SDK: Kit de desarrollo de software, un conjunto de aplicaciones para desarrollar programas en un determinado lenguaje o para un determinado entorno (Software Development Kit).
  • SDRAM: Tipo de memoria RAM, utilizada en equipos PC de gama alta (Pentium II y superiores, además de algún Pentium "normal").
  • SE: Windows 98 SE es una versión ligeramente mejorada de Windows 98 (Second Edition).
  • Segmentación: Sistema de gestión de memoria, en el que tanto la memoria física como la memoria virtual se dividen en bloques (en general de distinto tamaño, llamados segmentos), que son las que se vuelcan a disco a medida que se va llenando la memoria física, o se recuperan de disco cuando vuelven a ser necesarias.
  • Segmento: Nombre que reciben habitualmente los bloques en los que se divide la memoria (ver segmentación). En los primeros IBM PC, la memoria estaba dividida en "segmentos" de 64K de tamaño (lo que se conoce como memoria en "modo real"; en los 386 y superiores se puede acceder a toda la memoria de forma lineal, es lo que se conoce como "modo protegido").
  • Serie: En una conexión serie, los bits de información se mandan uno tras otro. Esto hace que la comunicación sea más lenta que en la transmisión paralelo.
  • Service Pack. Nombre que da Microsoft a los "parches" que corrigen errores en Windows NT o le añaden características nuevas.
  • Servidor: En una red de ordenadores, un servidor es un equipo que pone ciertos recursos a disposición de otros ordenadores (los clientes). Estos recursos pueden ser datos, aplicaciones, impresoras, etc.
  • SET: Protocolo diseñado para permitir Transacciones Electrónicas Seguras, de modo que se puedan realizar compras a través de Internet sin perder la privacidad (Secure Electronic Transactions).
  • SGBD: Sistema de gestión de bases de datos (en inglés DataBase Management System - DBMS).
  • Shareware: Aplicación informática que se puede copiar y distribuir libremente. Se trata de una versión de evaluación, que suele estar limitada en algún aspecto. Si la aplicación parece satisfactoria al usuario que la prueba, debe entonces pagar al autor, y a cambio recibe una versión de la aplicación sin ninguna limitación.
  • Shell (o Intérprete de comandos): Nombre que recibe la parte de un sistema operativo que recibe nuestras órdenes. En MsDos suele ser el fichero COMMAND.COM, mientras que para los sistemas Unix hay varios disponibles, como el Bourne Shell (bsh), Korn Shell (ksh) o C Shell (csh).
  • SIMM: Tipo de módulo de memoria RAM (Single In-line Memory Module). Usado en equipos 386 y 486.
  • Sinclair: Casa fabricante de ordenadores, hoy desaparecida, que tuvo una época de auge con los primeros ordenadores domésticos, especialmente con su modelo ZX Spectrum. Fue también autora de otros modelos anteriores, como el ZX 80 o el ZX81 y posteriores (pero con menos éxito) como el QL.
  • SIPP: Tipo de módulo de memoria RAM (anticuado, usado en algún 286).
  • Sistema operativo: Es una capa intermedia entre el ordenador y el usuario. Se podría considerar como un programa (normalmente de gran tamaño) que toma el control del ordenador y que nos proporciona las utilidades básicas. Para usos más avanzados, necesitaremos instalar aplicaciones informáticas como bases de datos, hojas de cálculo, programas a medida, etc. Ejemplos de sistemas operativos podrían ser: MsDos, Windows 95, Windows 98, Unix, MacOS, OS/2...
  • SLIP: Un protocolo estándar en Internet para conexiones a través del puerto serie (Serial Line Internet Protocol).
  • SmallTalk: Lenguaje de programación orientado a objetos.
  • SmartSuite: Suite realizada por Lotus, que incluye aplicaciones como WordPro, 1-2-3 y Organizer.
  • SMP: Multiproceso simétrico, la única forma real de multitarea, consistente en que un sistema esté equipado con varios procesadores, que trabajan en paralelo.
  • SMTP: Protocolo simple de transferencia de correo (Simple Mail Transfer Protocol). Un protocolo utilizado para enviar correo electrónico en Internet, definido en STD 10, RFC 821.
  • Socket: Un objeto de software utilizado por un cliente para conectarse a un servidor; los componentes básicos incluyen el número de puerto y la dirección de red del host local.
  • Software: La parte "que no se puede tocar" de un ordenador: los programas y los datos.
  • SoHo: Se suele hablar de entornos SoHo para referirse a entornos domésticos o de pequeña empresa, en los que se puedan necesitar equipos de una potencia relativamente baja (Small Office - Home Office).
  • Solaris: Versión del sistema operativo Unix, desarollada por Sun.
  • Sound Blaster: Una de las marcas de tarjetas de sonido más conocidas. Corresponde a toda una gama creada por la casa Creative Labs.
  • Spectrum: Gama de ordenadores desarrollados por Sinclair a mediados de los años 80. Estaban basados en el procesador Z80, con 16 o 48k de memoria (las primeras versiones), capacidad de realizar gráficos con 8 colores a 256x186 puntos y de emitir sonidos, y se podían conectar a cualquier televisión. Posteriormente se desarrollaron versiones mejoradas, con teclados más cómodos (Spectrum + -Plus-), unidad de cinta incorporada (Spectrum +2) o unidad de disco de 3" y 178K, memoria ampliada a 128 K y otras mejoras internas (Spectrum +3).
  • Spooler: Ver Buffer.
  • Spreadsheet: Hoja de cálculo, en inglés.
  • SQL: Un lenguaje estándar de consulta a gran cantidad de bases de datos (Structured Query Language).
  • SRAM: Memoria RAM estática.
  • SSL: Capa de conexión segura (Secure Sockets Layer).
  • Stack: Pila, en inglés.
  • StarOffice: Suite realizada por StarDivision, hoy propiedad de Sun, que incluye aplicaciones de proceso de textos, hoja de cálculo, base de datos, dibujo, presentaciones, y que está disponible para diversos sistemas operativos.
  • STP: Cable de par trenzado apantallado (Shielded Twisted Pair).
  • Streamer: lector/grabador de cintas magnéticas especiales para ordenador. Permiten una capacidad elevada a un bajo precio, pero son más lentos que otros dispositivos como los diskettes, CD-Rom, o disco duro.
  • String: Nombre que reciben las cadenas de texto en muchos lenguajes de programación.
  • Stylus: Gama de impresoras de inyección de tinta creada por Epson.
  • Subir: Ver Bajar.
  • Subnotebook: ordenador portátil (laptop) de dimensiones inferiores a un A4.
  • Subdirectorio: Ver Directorio.
  • Suite: Conjunto de programas diseñados para trabajar juntos. En español se suele llamar "Paquete Integrado". Es frecuente que incluyan un procesador de texto, una hoja de cálculo, un organizador personal, y pueden tener otros módulos, como gestores de bases de datos, programas de gráficos o presentaciones, etc.
  • Sun: casa de desarrolladora de hardware y software, que ha tenido un gran auge últimamente debido a la creación del lenguaje Java.
  • SuperVGA: Ver SVGA.
  • Surf: Navegar, en inglés.
  • Suse: Una de las casas especializadas en realizar distribuciones de Linux.
  • SVGA: SuperVGA. Tarjetas gráficas de ordenadores PC y compatibles, evolución de la VGA. Suelen permitir trabajar a 800x600, 1024x768 y 1280x1024 puntos, con 256 colores (8 bits), 65536 colores (16 bits), 16 millones de colores (24 bits) o 4.000 millones de colores (32 bits, "color auténtico" o "true color"), dependiendo de la cantidad de memoria instalada.
  • SX: Variante de los procesadores 80386 y 80486.
  • S3M: Extensión que corresponde a un tipo de ficheros de sonido.

T

  • T1: Un estándar telefónico de EE.UU. para un recurso de transmisiones de nivel de señal digital 1 (DS1) con 1,544 Mbps en Norteamérica y 2,048 Mbps en Europa. Esta tasa de bits se obtiene con el ancho de banda equivalente aproximadamente a veinticuatro líneas de 56 Kbps. Un circuito T1 es capaz de atender un mínimo de 48 modems a 28.800 bps o 96 modems a 14.400 bps. Los circuitos T1 se utilizan también para las conexiones telefónicas por voz. Una única línea T1 admite 24 conexiones telefónicas con 24 números de teléfono. Cuando se utiliza para la transmisión de voz, una conexión T1 se debe dividir en 24 circuitos separados.
  • T3: Un estándar telefónico de EE.UU. para un recurso de transmisiones de nivel de señal digital 3 (DS3). Equivale en ancho de banda a 28 conexiones T1. La velocidad en bits es de 44736 Mbps. T3 se denomina a veces un circuito 45-meg.
  • Tabla: En el mundo de las bases de datos, un conjunto de registros (fichas) que tienen una cierta homogeneidad (por ejemplo, los datos de nuestros proveedores podrían estar almacenados en una misma tabla).
  • Tambor: Uno de los componentes de muchas impresoras láser y copiadoras (para más detalles, ver Láser).
  • Tb: Abreviatura de TeraByte.
  • TCP: Protocolo de redes, orientado a conexión y confiable, que forma parte del conjunto de protocolos de TCP/IP.
  • TCP/IP: Conjunto de protocolo de comunicaciones, el estándar en los sistemas Unix y en Internet (Transmission Control Protocol/Internet Protocol).
  • Tarjeta de sonido: Dispositivo que da al ordenador la capacidad de sintetizar y emitir sonidos, así como de capturarlos. Es frecuente que un ordenador que no tenga tarjeta de sonido disponga al menos de un pequeño altavoz, que se puede controlar directamente desde el procesador para emitir sonidos de baja calidad.
  • Tarjeta gráfica: Dispositivo que da al ordenador la capacidad mostrar imágenes en pantalla. Las prestaciones de una tarjeta gráfica suelen depender de la cantidad de puntos y de colores que es capaz de mostrar, de su rapidez, y de la cantidad de memoria de que dispone (a mayor cantidad de memoria, será capaz de mostrar imágenes con más puntos y/o más colores). La tarjeta gráfica debe ir en consonancia con la pantalla o monitor. Por ejemplo, una pantalla que sólo permita mostrar 800x600 puntos o menos, no podrá sacar todo el partido a una tarjeta que alcance los 1024x768 puntos. Las más frecuentes en los PC han sido, de más antigua a más moderna: Hercules, CGA, EGA, MCGA, VGA, SVGA.
  • Telnet: Uno de los servicios que ofrece Internet: es la posibilidad de acceder remotamente a otro ordenador de la Red, y trabajar desde nuestra pantalla como si estuviesemos realmente tecleando delante de ese ordenador.
  • Terabyte: Múltiplo del byte: un terabyte son 1.024 GigaBytes, cerca de un billón (un millón de millones) de bytes.
  • TFT: Tipo de pantalla LCD en color para ordenadores portátiles. Ofrece más contraste y mejor ángulo de visión que las Dual Scan, pero es más cara (Thin Film Transistor).
  • TIF: Es la Extensión que corresponde a un tipo de fichero gráfico de mapa de bits: el formato TIFF.
  • TIFF: Uno de los tipos de fichero gráfico de mapa de bits más extendidos, incluso entre ordenadores de muy distintos tipos (Tagged Image File Format).
  • Token Ring: Red en anillo con paso de testigo (token), utilizada en los sistemas AS/400 de IBM. La tasa de transferencia es de 4 o 16 Mbits/s. Sigue un estándar normalizado por IEEE como 802.5.
  • Tóner: La tinta en polvo que suelen usar las impresoras láser y copiadoras (para más detalles, ver Láser).
  • Topología: Disposición física de los nodos de una red. Por ejemplo, es posible que se encuentren formando un bus, una estrella, una anillo, etc.
  • Toshiba: Conocido fabricante de hardware, especialmente conocido por sus ordenadores portátiles.
  • Turbo C: Compilador de lenguaje C, desarrollado por Borland. Más adelante incluyó también el lenguaje C++ y pasó a llamarse Turbo C++.
  • Turbo C++: Compilador de lenguaje C++, desarrollado por Borland. Las últimas versiones recibieron el nombre de "Borland C++".
  • Turbo Pascal: Compilador de lenguaje Pascal, desarrollado por Borland. La última versión recibió el nombre de "Borland Pascal".
  • Turing, Alan: Matemático inglés (1912-1954), que dió pie a la teoría del ordenador programable y que propuso un test para determinar la inteligencia de una máquina.
  • Tux: nombre que recibe el pingüino que se emplea como mascota de Linux.
  • TX: En el mundo de las comunicaciones, abreviatura de Transmisión o Transmitiendo.

U

  • UAL: Abreviatura de "Unidad Aritmético-Lógica", una de las partes de la Unidad Central de Proceso de un ordenador.
  • UC2: Extensión de ficheros comprimidos. El compresor correspondiente es el Ultra Compressor (UC).
  • UDMA: Abreviatura de Ultra DMA.
  • UDP: Protocolo de redes, no orientado a conexión y no confiable (usado para transmisiones de voz y video), que forma parte del conjunto de protocolos de TCP/IP.
  • Ultra DMA: Especificación de discos duros IDE, que permiten un medio de acceso más rápido a través de DMA, para conseguir tasas máximas de transferencia de 33 Mb/s (discos duros que sigan el estándar UDMA33) o de 66 Mb/s (en UDMA66).
  • UMTS: Servicio diseñado para permitir transferencias entre dispositivos móbiles a velocidades de hasta 2 Mbps (Universal Mobile Telecommunications System).
  • Unix: Sistema operativo multitarea y multiusuario. Existen distintas versiones realizadas por distintas casas, como AIX, XENIX, SCO, Linux, BSD, etc.
  • Upload: Ver Bajar.
  • UPS: Sistema de alimentación ininterrumpida (Uninterruptible Power Supply, en español abreviado como SAI).
  • URL: Dirección de una cierta página de información dentro de Internet (Universal Resources Locator).
  • USB: Bus serie universal (universal serial bus), un nuevo tipo de conexión serie que se está imponiendo rápidamente por ciertas características como: se pueden conectar varios dispositivos a un mismo puerto (hasta 127), se pueden conectar con el ordenador encendido, y el ordenador detecta el dispositivo del que se trata.
  • Usenet: Uno de los servicios que ofrece Internet, grupos de noticias, también conocidos como News.
  • UTP: Cable de par trenzado sin apantallar (Unshielded Twisted Pair).
  • UUE: Extensión de los ficheros creados con la utilidad UUdecode.
  • UUencode: Utilidad que permite enviar enviar ficheros de cualquier tipo como parte de un mensaje de texto a través de Internet, gracias a que convierte el fichero en una serie de caracteres de texto legible, a costa de que ocupe algo más de espacio. El receptor deberá tener la utilidad correspondiente (UUdecode) para reconstruir el fichero original. El uso de UUencode está decayendo actualmente, y se está usando cada vez más MIME en su lugar.

V

  • VBA: Visual Basic para Aplicaciones. Versión del lenguaje Visual Basic que puede ser utilizada desde muchas aplicaciones de Microsfot, como Word o Access.
  • VBScript: Visual Basic Scripting Edition. Subconjunto del lenguaje Visual Basic, diseñada para ser utilzada en navegadores Web y otras aplicaciones que usan controles ActiveX y servidores de Automatización OLE.
  • Vectorial: Un tipo de imágenes para ordenador, en las que se almacena información sobre las líneas y figuras geométricas que las componen. Esto permite que no pierdan definición si se amplían, al contrario de lo que ocurre con las imágenes "Bitmap".
  • Vectorizar: convertir una imagen Bitmap a vectorial.
  • Versión: Ver release.
  • VESA: Video Electronics Standards Association. Una asociación encargada de realizar estándares relacionados con las tarjetas gráficas para ordenadores. Realizó una arquitectura de palcas base de 32 bits (Vesa Local Bus o VLB) y definió una serie de modos de video estándar para diversas tarjetas gráficas SVGA.
  • VFAT: Sistema de ficheros empleado en Windows 95 y 98. Es una mejora del sistema llamado FAT.
  • VGA: Matriz gráfica de video (Video Graphics Array). Tarjeta gráfica de ordenadores PC y compatibles, evolución de la MCGA, que permitía trabajar también a 16 colores con 640x480 puntos.
  • Virtual: Esta palabra se suele usar para referirse a algo que no existe realmente, sino sólo dentro del ordenador. Las dos acepciones más habituales son "Realidad virtual", referida a un espacio en 3 dimensiones creado dentro del ordenador, por el que el usuario puede desplazarse (normalmente con la ayuda de dispositivos auxiliares, como gafas estereoscópicas, guantes o joysticks), y "Memoria virtual", que consiste en que un ordenador aparente tener más memoria de la que físicamente tiene, gracias a que parte del disco duro se utiliza como zona de almacenamiento intermedio, en la que se va volcando información cuando la memoria real se satura (de forma transparente, sin que el usuario tenga que hacer nada).
  • Virus: Un programa con intenciones malignas, que es capaz de propagarse de un fichero a otro del ordenador (o incluso de un ordenador a otro, si llega un fichero infectado a otro ordenador, bien sea a través de un diskette, un Cd, una red local o Internet).
  • Visual Basic: Herramienta para crear programas bajo Windows, basada en el lenguaje Basic, desarrollada por Microsoft.
  • Visual C++: Herramienta para crear programas bajo Windows, basada en el lenguaje C++, desarrollada por Microsoft.
  • VLB: VESA Local Bus, tipo de arquitectura estándar de placas base, con ranuras de ampliación de 32 bits, basada en ISA, diseñadas especialmente para tarjetas gráficas, hoy en día abandonada.
  • Von Neumann, John: Matemático húngaro (1903-1957), que influyó mucho en el diseño de la arquitectura de los ordenadores modernos.
  • VRML: Un lenguaje para la creación de aplicaciones HTML tridimensionales (Lenguaje de modelado de realidad virtual).

W

  • WAIS: Un método para buscar y recuperar información de bases de datos disponibles en Internet, hoy en día en desuso.
  • WAN: Red de area amplia (Wide Area Network).
  • WAP: Wireless Application Protocol, un protocolo de comunicaciones diseñado para que se pueda acceder desde teléfonos móviles a algunos de los servicios que proporciona Internet.
  • WAV: Es la Extensión que corresponde a un fichero de sonido (el estándar en Windows): WAVe.
  • Web: Ver WWW.
  • Webcam: Cámara Web, una cámara digital de reducido tamaño que se puede utilizar para realizar videoconferencia mediante el ordenador y, en ocasiones, para capturas fotos fijas y/o imagen en movimiento separada del ordenador.
  • Whistler: Nombre en clave dado a Windows XP durante su proceso de desarrollo.
  • Winchester: nombre que antiguamente de daba a los discos duros.
  • Windows: Nombre genérico de toda una familia de software diseñado por Microsoft. Las primeras versiones (hasta la 3.11) eran un entorno gráfico basado en ventanas, para el sistema operativo Dos. A partir de Windows 95 (Windows 95 y Windows 98) ya se trata de un sistema operativo en sí mismo, con capacidades multitarea. También existen versiones "especiales" de Windows, como Windows NT, diseñado para entornos profesionales, o Windows CE, para ordenadores portátiles de muy reducidas dimensiones.
  • WLAN: Red local inalámbrica (Wireless LAN).
  • WMF: Es la Extensión que corresponde a un tipo de fichero gráfico vectorial (el estándar en Windows): Windows MetaFile.
  • Word: Procesador de textos realizado por Microsoft.
  • WordPerfect: Procesador de textos realizado inicialmente por WordPerfect Corporation y actualmente por Corel.
  • WordPro: Procesador de textos realizado por Lotus, evolución de otro anterior llamado AmiPro.
  • WordStar: Procesador de textos, que marcó un estándar bajo MsDos pero que actualmente no se continúa mejorando (hasta donde yo conozco).
  • World Wide Web: Ver WWW.
  • WP: Se suele usar como abreviatura de Word Processing (procesamiento de textos) o de WordPerfect.
  • WWW: World Wide Web: posiblemente, el servicio más conocido de Internet: una serie de páginas de información, con texto, imágenes (a veces, incluso otras posibilidades, como sonido o secuencias de video), y enlazadas a su vez con otras páginas que tengan información relacionada con ellas.

X

  • X: Ver XWindows.
  • Xbase: Nombre genérico que se suele dar al lenguaje de programación para bases de datos que usaba dBase y que aplicaron (y ampliaron) otras herramientas como Clipper o FoxPro.
  • Xenix: Versión del sistema operativo UNIX desarrollada por Microsoft a finales de los 70 y principios de los 80, diseñada específicamente para los ordenadores compatibles PC.
  • Xerox: Conocido fabricante de impresoras, copiadoras y plotters.
  • XFree86: Implementación gratuita del sistema de ventanas XWindows, que es empleada por el sistema Linux.
  • XLS: Extensión de los ficheros de creados con Excel.
  • XML: Lenguaje de descripción de páginas de Internet (eXtensible Markup Language), diseñado con la intención de reemplazar al estándar actual HTML.
  • XMS: Ver memoria extendida.
  • Xor: Operador a nivel de bits: suma lógica exclusiva, que da como resultado 1 si los dos bits son distintos y 0 si los dos bits son iguales.
  • XP: Versión de Windows posterior a 2000 y a ME, que fusiona las líneas profesional y doméstica en un solo producto.
  • XT: El IBM XT era una variante del ordenador IBM PC original, que incluía disco duro (el PC original no).
  • X-Windows: Entorno gráfico basado en ventanas, para el sistema operativo Unix. Su nombre a veces se abrevia simplemente como X. Puede tener distintas apariencias, según empleemos el escritorio KDE, Gnome, OpenWindows, AfterStep, Enlightment, etc. (Hay versiones de Unix que sólo incluyen un tipo de escritorio, mientras que otras permiten escoger entre varios).
  • X86: Nombre genérico que se suele dar a la familia de procesadores Intel basados en el 8086: el 8086 original y su variante 8088, 80286, 386, 486 y la actual gama Pentium (Pentium, Pentium Pro, Pentium MMX, Pentium II, Celeron, Pentium III). También se suele usar este mismo nombre para los procesadores similares a alguno de ellos, aunque fueran realizados por otras casas, como los V20 y V30 de Nec, los 586 y 686 de Cyrix, o los K5, K6 y K7 (Athlon) de AMD.

Y

Z

  • ZIP: Extensión de ficheros comprimidos. El compresor correspondiente suele llamarse ZIP o PKZIP. // ZIP: Unidad de almacenamiento similar a un diskette, pero con una capacidad de 100 Mb (o de 250 Mb, según versiones), creada por Iomega.
  • Z80: Procesador de 8 bits, creado por la casa Zilog, que fue muy utilizado en la primera generación de ordenadores personales, en equipos como los Sinclair ZX Spectrum, MSX, Amstrad CPC, etc.

0-9

  • 1-2-3: Aplicación de hoja de cálculo realizada por Lotus.
  • 2000: Nombre que recibe la versión profesional de Windows posterior a Windows NT.
  • 286: Nombre genérico que se suele dar a los ordenadores basados en el procesador 80286, o incluso al mismo procesador.
  • 3D Studio: Conocido programa de creación de imágenes en 3 dimensiones, realizado por Autodesk.
  • 386: Nombre genérico que se suele dar a los ordenadores basados en el procesador 80386, o incluso al mismo procesador.
  • 486: Nombre genérico que se suele dar a los ordenadores basados en el procesador 80486, o incluso al mismo procesador.
  • 80x86: Ver X86.
  • 8086: Procesador CISC de 16 bits realizado por Intel, base de toda la gama x86. Su bus de direcciones era de 20 bits (capaz de direccionar 1 Mb de memoria). Se fabricó con velocidades de 4,77 a 10 Mhz.
  • 8087: Coprocesador matemático diseñado para facilitar al procesador 8086 los cálculos con números reales (con decimales).
  • 8088: Procesador realizado por Intel,. Se trataba de una variante del 8086 (compatible con él) que trabajaba con un bus de datos de 8 bits (en vez de 16), con la intención de abaratar costes a cambio de una menor velocidad efectiva.
  • 80286: Procesador de 16 bits realizado por Intel, evolución del 8086 (y compatible con él). Su bus de direcciones era de 24 bits (capaz de direccionar 16 Mb de memoria). Se fabricó con velocidades entre 6 y 16 Mhz (aunque existieron versiones que llegaron hasta los 25 MHz).
  • 80287: Coprocesador matemático diseñado para ayudar al procesador 80286.
  • 80386: Procesador de 32 bits realizado por Intel, evolución del 80286 (y compatible con él y con el 8086), con un bus de direcciones de 32 bits (capaz de direccionar 4 Gb de memoria). Se fabricó con velocidades entre 16 y 33 Mhz (aunque AMD realizó también una versión a 40 MHz). Se realizó una variante más barata, con bus de datos de 16 bits, llamada 80386 SX. A raíz de la creación de esta versión "pequeña", se cambió el nombre de la versión "normal" a 80386 DX.
  • 80387: Coprocesador matemático diseñado para ayudar al 80386.
  • 80486: Procesador de 32 bits realizado por Intel, evolución del 80386 (y compatible con él y con toda la familia x86). Se fabricó con velocidades entre 16 y 100 Mhz. Incluía coprocesador en el propio micro y otras mejoras frente al 80386. Se realizó una versión "barata", llamada 486 SX que no incluía dicho coprocesador. También se hicieron versiones mejoradas sobre la normal (llamada DX): los 486 DX2 eran versiones con velocidad "duplicada" (por ejemplo, en un 486 DX2/66, la placa base trabajaba a 33 MHz, mientras que en el interior del micro la velocidad era el doble: 66 MHz); los 486 DX4 tenían la velocidad triplicada (el 486 DX4/100 trabajaba a 99 MHz en una placa a 33 MHz).

(Texto creado por Nacho Cabanes)


Curso de C. Aun no!

Lo siento, pero esta opción todavía no está disponible. Espero que esté terminada en un breve plazo de tiempo.


Curso de C. Cambios entre versiones (ampliadas).

Los cambios en la versión "ampliada" del curso han sido (hasta la 2.25, los posteriores y las próximas mejoras previstas aparecen en la "lista normal"):
  • 2.26h, y posteriores: estarán disponibles para usuarios registrados unos 2 meses antes que para el resto de usuarios - puede ver los cambios en la "lista normal").
  • 2.25h, de fecha "oficial" 06-01-02 (accesible a los usuarios registrados desde 28/10/01).
    • Cambiado el formato, de "Ayuda de Windows" (HLP) a "Ayuda de Windows 98" (CHM), para que me resulte más fácil de corregir y ampliar.
    • Unidas algunas páginas que antes estaban separadas, para que el curso se pueda imprimir y visualizar con más facilidad.
    • Añadida información sobre dónde conseguir y cómo instalar Dev-C++ 4.0.
    • Añadida información sobre dónde conseguir y cómo instalar Turbo C 2.01.
    • Añadida información sobre dónde conseguir y cómo instalar el compilador de C y C++ DJGPP 2.03.
    • Añadida información sobre dónde conseguir y cómo instalar el compilador de C y C++ llamado Digital Mars 8.22 (que parece una evolución de Symantec C++, gratuita).
    • Añadida información sobre dónde conseguir y cómo instalar el compilador Watcom C++ 11.0c.
    • Inaugurado un apartado de "otros temas complementarios", con nociones sobre unidades de medida usadas en informática, sobre el sistema binario y el diccionario básico de informática.
    • Ligeramente reescrito el tema 1 ("Generalidades del C"), buscando mayor legibilidad.
    • Corregida una pequeña errata en el cuarto tema del apartado sobre C++ Builder y otra en la "ampliación" sobre cómo usar los servicios del DOS desde Turbo C, y otra en el ejemplo 35.
    • Cambiada la apariencia de la lista de cambios (ésta), para que se vea mejor con cualquier tipo de pantalla.
  • 2.22w, de 19-03-01
    • Ampliado el tema sobre la salida en pantalla desde C++, indicando como tener más control sobre el formato de salida en pantalla.
    • Añadido un apartado sobre el manejo de ficheros desde C++.
    • Ampliado el tema sobre “gráficos”, con un ejemplo de cómo representar funciones matemáticas en pantalla.
    • Añadida información sobre el compilador de C y C++ que se incluye con Linux (y con otras versiones de Unix).
    • Añadida alguna imagen extra para mayor claridad (en el ejemplo del “sistema de menús” y en el primer ejemplo sobre gráficos.
    • Añadidos botones >> y << que permiten moverse con mayor facilidad entre los distintos temas (de momento sólo utilizables en los temas básicos, no en las ampliaciones).
  • 2.21w, de 21-08-00
    • Ampliado el tema sobre “programas a partir de varios fuentes” con información sobre Make y sobre los proyectos.
    • Ampliado el tema sobre “cadenas de texto para hablar sobre “sprintf” y “sscanf”.
    • Añadida información sobre cómo conseguir el compilador Turbo C++ Second Edition.
    • Añadido un tema sobre el nuevo estándar de C++ que sí permite cadenas (strings) como parte del lenguaje.
    • Ampliadas las definiciones de informática general que se pueden consultar: incluida la versión 0.60 de mi diccionario básico (536 definiciones), aunque por ahora sólo algunas están accesibles desde el Índice.
  • 2.20w, de 11-07-00
    • Cambiada mi dirección de correo electrónico y mi página Web: e-mail “ncabanes@pobox.com”, Web “http://www.pobox.com/users/ncabanes”
    • Añadida información sobre cómo conseguir e instalar la versión “Free CommandLine Tools” de Borland C++ 5.5.
    • Ampliado un poco el tema sobre gráficos con Turbo C++ y añadido un pequeño apartado sobre cómo crear gráficos con C++ Builder
  • 2.19w, de 01-07-00
    • Revisión completa para eliminar posibles erratas (por ejemplo, faltaba alguna llave de cierre en alguno de los ejemplos, que podía hacer que no funcionase correctamente) y para ampliar o reescribir algunos temas (por ejemplo, el tema 1).
    • Sincronizada la versión gratis con la ampliada, para que las erratas que se corrijan en la “grande” repercutan también en la “pequeña” (aunque la “grande” seguirá teniendo un mayor contenido, claro).
  • 2.18w, de 12-02-00
    • Ampliada la posibilidad de búsqueda desde cualquier pantalla (pulsando el botón “Indice”). Ahora incluye también las ampliaciones, los fuentes de ejemplo y la introducción a C++.
    • Añadidas más definiciones tomadas de mi diccionario básico de informática, y más enlaces entre temas.
    • Añadido otro tema que cuenta las funciones matemáticas disponibles en C.
    • Ampliada la introducción a C++: incluidas las bases de la sobrecarga de operadores y de las funciones amigas.
    • Corregida una errata en el apartado que hablaba de constructores y destructores: habían desaparecido los símbolos ~.
    • Los ejemplos del curso se incluyen ya exportados, dentro del fichero ZIP, para mayor comodidad.
  • 2.17w, de 27-08-99
    • Se puede buscar temas y palabras clave desde cualquier pantalla (pulsando el botón “Indice”). La referencia incluida en esta versión abarca desde el tema 1 hasta el 12.
    • Añadida alguna definición extra, tomadas de mi diccionario básico de informática: compilador e intérprete, aplicación informática, pixel, programa, ejecutable, código máquina, ensamblador, ANSI, sistema operativo. Están disponibles en la ayuda, y hay enlace en los primeros temas del curso.
  • 2.16w, de 05-07-99
    • Se puede buscar temas y palabras clave desde cualquier pantalla (pulsando el botón “Indice”). La referencia incluida en esta versión abarca desde el tema 1 hasta el 12.
    • Corregida alguna errata de poca importancia: algún tipo de letra, algún paréntesis sin cerrar, etc.
    • Añadida alguna imagen más: la pantalla de trabajo de Turbo C++ 1.01, del ejemplo de la agenda y del ejemplo de los sprites.
    • Añadido un apartado para quien quiera cotillear sobre el autor de este curso.
  • 2.15w, de 18-06-99
    • Incluida una introducción a la programación visual bajo Windows empleando Borland C++ Builder (unas 35 páginas impresas).
  • 2.11, de 14-03-99
    • Creada una versión con lector para Windows, que contiene el mismo texto la de MsDos.
    • Corrige algunas erratas de poca importancia.
  • 2.10, de 02-05-98
    • Incluye una introducción a la Programación Orientada a Objetos y a C++.
    • Desde la lista de cambios (ésta) se puede acceder directamente a los temas que se mencionan.
  • 2.09, de 14-03-98
    • La versión 2.08 no exportaba los ejemplos correctamente, sino que aparecía un símbolo "extraño" al principio de cada línea; corregido.
    • Añadido otro fuente de ejemplo: un sistema de menús sencillo para MsDos.
  • 2.08, de 01-03-98
    • Muchos ejemplos comprobados también con el compilador de libre distribución DJGPP.
    • Realizados ligeros cambios en algunos fuentes para que funcionen correctamente también con este compilador (Lista1, Lista2, Arbol, Ej40, Ej41, Ej45)
    • Corregido un error en EJ45.C: una letra que sobraba en la línea siguiente a main().
    • Añadido un ejemplo de cómo hacer gráficos accediendo a memoria con Djgpp.
    • Completado algo más el tema de "programas a partir de varios fuentes".
  • 2.07, de 18-01-98
    • Añadido un tema sobre los compiladores que he empleado para probar los ejemplos: dónde conseguirlos y como instalarlos.
    • Corregido un despiste que hacía que no funcionase el ejemplo de cómo acceder a la impresora desde C.
    • Otras ligeras mejoras internas al lector.
  • 2.06, de 08-11-97
    • Añadido un ejemplo más completo y más desarrollado: una agenda de direcciones, dentro del apartado de ampliaciones.
    • Corregidas algunas erratas: concer -> conocer, modidicadores->modificadores, etc.
    • Comprobados 40 de los ejemplos básicos con un compilador shareware de pequeño tamaño: PCC 2.1c.
    • El ejemplo 14b no se exportaba bien; corregido.
  • 2.05, de 02-10-97
    • En el tema sobre cómo acceder a la pantalla gráfica sin emplear los BGI, añadido un nuevo ejemplo, que dibuja figuras que se mueven por la pantalla sin borrar el fondo (sprites).
    • Añadido un tema sobre cómo acceder al lenguaje ensamblador desde C.
    • Incluido un ejemplo de cómo elegir números al azar, y aplicado al juego de adivinar un número.
  • 2.04, de 01-09-97
    • En el tema sobre cómo acceder a la pantalla gráfica sin emplear los BGI me había dejado un párrafo sin terminar de escribir (mil disculpas); corregido.
    • Añadido un tema sobre las listas simples y los árboles binarios (estructuras dinámicas).
  • 2.03, de 14-08-97
    • Añadido un tema sobre cómo acceder a la pantalla gráfica sin emplear los BGI.
    • Añadido un tema sobre los códigos ASCII.
    • Otras mejoras internas en el texto del curso, para hacerlo más fácil de mantener.
  • 2.02, de 20-07-97
    • Recolocados los cambios del más reciente al más antiguo.
    • Añadida una lista de las funciones de gráficos con BGI incluidas en Turbo C++ y un ejemplo de programa rudimentario de dibujo.
    • Añadido un tema sobre cómo controlar el ratón desde MsDos.
  • 2.01, de 27-06-97
    • Añadido un tema que cuenta como acceder a la línea de comandos.
    • Incluidos tres fuentes completos de ejemplo: adivinar un número, mostrar un fichero de texto, comparar dos ficheros.
  • 2.00, 07-05-97
    • Primera versión ampliada en formato hipertexto. Se puede manejar con ratón. Se pueden exportar las lecciones, los ejemplos y las tablas.
  • 1.00, 20-12-96
    • Versión inicial.


Curso de C. Pascal -> C.

Este apartado es sólo una referencia rápida para quien haya programado en Pascal y ahora esté empezando con el C. No pretende cubrir todos los casos posibles, sino simplemente aportar una serie de ejemplos con las situaciones más habituales (creo), a modo de recordatorio:

Declaraciones de variables.
Escritura en pantalla.
Lectura de datos.
Comprobación de condiciones.
Estructuras de control.
Procedimientos y funciones.
Punteros.

Para cada una de estas categorías, iré comparando cómo se haría en Pascal y cómo en C.

Declaraciones de variables.

       PASCAL
C
var
a: byte;
char a;
b,j: integer;
int b,j;
c: longint;
long c;
d: real;
float d;
e: boolean;
(Se debe simular con char o int)
f: string[80];
char f[81];
g: string;
char g[256];
h: array[1..20,1..10] of integer;
int f[20][10];
i: record
struct {
nombre: string[20];
char nombre[20];
edad: byte;
char edad;
end;
} i;

  Escritura en pantalla.

   writeln('Hola');
printf("Hola\n");
write(a);
printf("%d",a);
writeln('Nombre: ', f);
printf("Nombre: %s\n", f);
write('Elemento (5,6):', h[5,6]);
printf("Elemento (5,6): %d", h[4][5]);

  Lectura de datos.

   readln(f);
gets(f);
read(b);
scanf("%d", &b);

  Comprobación de condiciones.

   if a>2 then write('Sí');
if (a>2)  printf("Sí");
if not ((a<b) and (b<c)) then [...]  if
! ((a<b) && (b<c))  [...]
if (a<>0) or (b=2) then [...]
if a || (b==2)  [...]

   case a of
switch (a) {
1: write('Uno');
case 1: printf("Uno"); break;
2, 3: write('Dos o tres');
case 2:
4: begin
case 3: printf("Dos o tres"); break;
writeln('Cuatro');
case 4: printf("Cuatro");
solucion := 8;
solucion = 8;
end;
break;
else
default:
write('No es 1, 2, 3 ni 4');
printf("No es 1, 2, 3 no 4");
end;
}

  Estructuras de control.

   for a := 1 to 5 do write(a);        for (a=1; a<=5; a++)  printf("%d",a);

   while a<5 do
while (a<5)
begin
{
a := a+1;
a++;
write(a)
printf("%d",a);
end;
}

   repeat
do {
a := a+1;
a++;
write(a)
printf("%d",a);
until a>=5;
} while (a<5);

  Procedimientos y funciones.

   procedure hola;
void hola();
function fatorial(x:real):real;
float factorial(float x);
procedure modifica(var b:integer);  void modifica(int*
b);

  Punteros.

   var p: ^integer;
int *p;
p^ := 2;
*p = 2;

Hay que recordar que en C las cadenas de texto son punteros, y en Pascal no, luego en C no se pueden concatenar cadenas haciendo a = b + c; ni darles valores con a = "Hola";

Por otra parte, los arrays en C se pueden recorrer como array o como puntero: s[5] o *(p+5).