Curso de C
Por Nacho Cabanes, versión 0.90 pre4

10. Bibliotecas de uso frecuente

10.1. Llamadas al sistema: system

Si hay algo que no sepamos o podamos hacer, pero que alguna utilidad del sistema operativo sí es capaz de hacer por nosotros, podemos hacer que ella trabaje por nosotros. La forma de llamar a otras órdenes del sistema operativo (incluso programas externos de casi cualquier tipo) es utilizar la orden “system”. Por ejemplo, podríamos mostrar la lista de ficheros de la carpeta actual con la orden “ls”, así:

/*---------------------------*/
/*  Ejemplo en C nº 85:      */
/*  C085.C                   */
/*                           */
/*  Llamadas a servicios del */
/*  sistema                  */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <stdio.h>
#include <stdlib.h>
 
int main(){
 
    printf("El contenido de la carpeta actual es:\n");
    system ("ls");
 
    return 0;
}
 

10.2. Temporización

Si lo que queremos es hacer una pausa en un programa, en ocasiones tendremos funciones que nos permitan hacer una pausa de ciertas milésimas de segundo, como la función “delay” de Turbo C.

/*---------------------------*/
/*  Ejemplo en C nº 86:      */
/*  C086.C                   */
/*                           */
/*  Pausa con Turbo C        */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <stdio.h>
#include <dos.h>
 
int main(){
 
    printf("Vamos a esperar 2 segundos... ");
    delay(2000);
    printf("El tiempo ha pasado.\n");
 
    return 0;
}
 

Si queremos comprobar la fecha y hora del sistema, lo podemos hacer con las funciones disponibles en “time.h”, que sí son parte del estandar ANSI C, por lo que deberían estar disponibles para casi cualquier compilador:

/*---------------------------*/
/*  Ejemplo en C nº 87:      */
/*  C087.C                   */
/*                           */
/*  Leer fecha y hora        */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <stdio.h>
#include <time.h>
 
int main() {
   time_t segundos;
   struct tm *fecha;
 
   segundos = time(NULL);
 
   printf("Instante actual: %u s\n", segundos);
 
   fecha = gmtime(&segundos);
   printf("Como texto es: %s\n", asctime(fecha));
 
   printf("En concreto, la hora Greenwich es: %2d:%02d:%02d\n", 
     fecha->tm_hour, fecha->tm_min, fecha->tm_sec);
 
   return 0;
}
 

Dentro de “time.h”, tenemos definido un tipo llamado “time_t” que representará a una cierta fecha (incluyendo hora). La mayoría de los sistemas lo representan internamente como un entero largo (el número de segundos desde cierta fecha), aunque es algo que a nosotros no nos debería hacer falta saber si usamos directamente ese tipo “time_t”.

Tenemos también un tipo de registro (struct) predefinido, llamdo “struct tm”, que guarda la información desglosada del día, el mes, el año, la hora, etc. Los principales campos que contiene son éstos:

int tm_hour; /* hora (0 - 23) */
int tm_mday; /* Dia del mes (1 - 31) */
int tm_min; /* Minutos (0 - 59) */
int tm_mon; /* Mes (0 - 11 : 0 = Enero) */
int tm_sec; /* Segundos (0 - 59) */
int tm_wday; /* Dia de la semana (0 - 6 : 0 = Domingo) */
int tm_yday; /* Dia del año (0 - 365) */
int tm_year; /* Año menos 1900 */

y el modo de usarlas se ve en el fuente anterior:

printf("En concreto, la hora es: %2d:%02d:%02d\n",
fecha->tm_hour, fecha->tm_min, fecha->tm_sec);

Como hemos visto en este ejemplo, tenemos varias funciones para manipular la fecha y la hora:

* “time” devuelve el número de segundos que han pasado desde el 1 de enero de 1970. Su uso habitual es hora = time(NULL);
* “gmtime” convierte ese número de segundos que nos indica “time” a una variable de tipo “struct tm *” para que podamos conocer detalles como la hora, el minuto o el mes. En la conversión, devuelve la hora universal (UTC o GMT, hora en Greenwich), que puede no coincidir con la hora local.
* “localtime” es similar, pero devuelve la hora local, en vez de la hora universal (el sistema debe saber correctamente en qué zona horaria nos encontramos).
* “asctime” convierte un dato horario de tipo “struct tm *” a una cadena de texto que representa fecha, hora y día de la semana, siguiendo el formato Sat May 20 15:21:51 2000 (día de la semana en inglés abreviado a 3 letras, mes en inglés abreviado a 3 letras, número de día, horas, minutos, segundos, año).

Pero aún hay más:

* “difftime” calcula la diferencia entre dos fechas.
* “mktime” crea un dato de tipo “struct tm *” a partir de otro incompleto. Es útil por ejemplo para saber el día de la semana si conocemos el día, mes y año.
* …

Si queremos imitar el funcionamiento de la orden “delay” de Turbo C, lo podemos hacer leyendo continuamente la fecha y la hora, o bien usar la función “clock()”, que da una estimación (lo más aproximada que el sistema permita) del tiempo que hace que nuestro programa comenzó a ponerse en marcha:

/*---------------------------*/
/*  Ejemplo en C nº 88:      */
/*  C088.C                   */
/*                           */
/*  Pausa usando “clock”     */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <stdio.h>
#include <time.h>
 
void espera (int segundos) {
  clock_t instanteFinal;
  instanteFinal = clock () + segundos * CLOCKS_PER_SEC ;
  while (clock() < instanteFinal) {}
}
 
int main () {
  int n;
  printf ("Comienza la cuenta atras...\n");
  for (n=10; n>0; n--)  {
    printf ("%d\n",n);
    espera (1);
  }
  printf ("Terminado!\n");
  return 0;
}
 

Nota: en Turbo C no existe la constante CLOCKS_PER_SEC, sino una llamada CLK_TCK con el mismo significado (“ticks” del reloj en cada segundo, para poder convertir a segundos el valor que nos indica “clock()”).

 

10.3. Pantalla y teclado con Turbo C

La familia Turbo C / Turbo C++ / Borland C++ incluye una serie de compiladores creados por Borland para Dos y para Windows. Con ellos se podía utilizar ciertas órdenes para escribir en cualquier posición de la pantalla, para usar colores, para comprobar qué tecla se había pulsado, etc. Eso sí, estas órdenes no son C estándar, así que lo más habitual es que no se encuentren disponibles para otros o para otros sistemas operativos.

Aun así, como primer acercamiento al control de estos dispositivos desde Linux, puede ser interesante conocer lo que ofrecía la familia de Turbo C y posteriores, porque sientan muchas de las bases que después utilizaremos, pero a la vez se trata de funciones muy sencillas.

Comencemos por las más habituales en cuanto a manejo de pantalla:

Por lo que respecta al teclado, tenemos

Todas ellas se encuentran definidas en el fichero de cabecera “conio.h”, que deberemos incluir en nuestro programa.

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.

Pero hay que tener una precaución: en MsDos sólo se pueden usar como colores de fondo los 7 primeros: desde BLACK hasta LIGHTGRAY. Se podía evitar en los ordenadores más modernos, a cambio de perder la posibilidad de que el texto parpadee, pero es un detalle en el que no entraremos. 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 color equivalente en el rango de 0 a 7:

textbackground(BLUE);

Para usarlas, tenemos que incluir “conio.h”. Vamos a ver un ejemplo que emplee la mayoría de ellas:

/*---------------------------*/
/*  Ejemplo en C nº 89:      */
/*  C089.C                   */
/*                           */
/*  Pantalla y teclado       */
/*  con Turbo C              */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <conio.h>      /* Para funciones de pantalla */
 
main()
{
  int i,j;                /* Para los bucles "for" */      
  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 que se pulse una tecla, sin mostrarla */
}
 

El resultado sería éste:

Ejemplo de conio.h

Tenemos más funciones definidas en “conio.h”, que nos permiten saber en qué posición de la pantalla estamos, definir “ventanas” para trabajar sólo con una zona de la pantalla, etc. Pero como el trabajo en modo texto se considera cada vez más anticuado, y especialmente dentro del entorno Windows, no profundizaremos más.

10.4. Acceso a pantalla en Linux: curses.h

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, cambiar colores o apariencias del texto, dibujar recuadros, etc.

Eso sí, el manejo es más complicado que en el caso de Turbo C: hay que tener en cuenta que en Linux (y en los Linux en general) podemos encontrarnos con que nuestro programa se use desde un terminal antiguo, que no permita colores, sino sólo negrita y subrayado, o que no actualice la pantalla continuamente, sino sólo en ciertos momentos. Es algo cada vez menos frecuente, pero si queremos que nuestro programa funciones siempre, deberemos llevar ciertas precauciones.

Por eso, el primer paso será activar el acceso a pantalla con “initscr” y terminar con “endwin”. Además, cuando queramos asegurarnos de que la información aparezca en pantalla, deberíamos usar “refresh”.

Como primeras órdenes, vamos a usar:

Con todo esto, un primer ejemplo sería:

/*---------------------------*/
/*  Ejemplo en C nº 90:      */
/*  C090.C                   */
/*                           */
/*  Pantalla con Curses (1)  */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <curses.h>
 
main() {
  initscr();
  clear();
  move(10,10);
  printw("hola");
  refresh();
  getch();
  endwin();
}
 

Un detalle importante: a la hora de compilar hay que añadir la opción de que enlace la librería “ncurses”, bien desde las opciones de nuestro entorno de desarrollo (si usamos Ajuta, KDevelop o algún otro), o bien desde la línea de comandos:

cc ejemplo.c –lncurses –o ejemplo

(hay que recordar que se distinguen las mayúsculas de las minúsculas).

Podemos mejorarlo nuestro ejemplo un poco:

* En la inicialización, es frecuente que nos interese comprobar el teclado cada vez que se pulse una tecla, sin esperar a que se complete con Intro, y de asegurarse de eso se encarga la orden “cbreak”.
* También es frecuente que queramos que cuando se pulse una tecla, ésta no aparezca en pantalla. Eso lo hacemos con “noecho”.

Ya en el cuerpo del programa, también podemos seguir haciendo mejoras:

* Podemos desplazarnos a una cierta posición y escribir un texto, todo ello con la misma orden, si usamos “mvaddstr”.
* Podemos cambiar la apariencia del texto si le aplicamos ciertos atributos, usando la orden “attron”. Los atributos más habituales en un terminal Unix serían la negrita (A_BOLD) y el subrayado (A_UNDERLINE). Estos atributos se desactivarían con “attroff”.

Con todo esto, ahora tendríamos algo como

/*---------------------------*/
/*  Ejemplo en C nº 91:      */
/*  C091.C                   */
/*                           */
/*  Pantalla con Curses (2)  */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <curses.h>
 
main()
{
  initscr();              /* Inicialización */
  cbreak();               /* Sin esperar a Intro en el teclado */
  noecho();               /* Para que getch no escriba en pantalla */
  clear();                /* Borramos la pantalla */
  attron(A_BOLD);         /* Activamos la negrita (bold) */
  mvaddstr(2,10,"hola");  /* Escribimos en cierto sitio */
  attroff(A_BOLD);        /* Desactivamos la negrita */
  refresh();              /* Actualizamos la info en pantalla */
  getch();                /* Esperamos a que se pulse una tecla */
  endwin();               /* Se acabó */
}
 

Finalmente, si queremos escribir en pantalla usando colores, tendremos que decir que queremos comenzar el modo de color con start_color(); durante la inicialización. Eso sí, antes deberíamos comprobar con has_colors() si nuestro terminal puede manejar colores (tenemos definida una constante FALSE para poder comprobarlo).

Entonces podríamos definir nuestros pares de color de fondo y de primer plano, con “init_pair”, así: init_pair(1, COLOR_CYAN, COLOR_BLACK); (nuestro par de colores 1 será texto azul claro sobre fondo negro).

Para usar estos colores, sería muy parecido a lo que ya conocemos: attron(COLOR_PAIR(1));

Y si queremos comprobar las teclas extendidas, haríamos keypad(stdscr, TRUE); durante la inicialización y en cuerpo del programa ya podríamos usar órdenes como

tecla = getch();
if (tecla == KEY_LEFT) ...

Vamos a ver un ejemplo que junte todo esto:

/*---------------------------*/
/*  Ejemplo en C nº 92:      */
/*  C092.C                   */
/*                           */
/*  Pantalla con Curses (3)  */
/*                           */
/*  Curso de C,              */
/*    Nacho Cabanes          */
/*---------------------------*/
 
#include <curses.h>
 
main()
{
  int tecla;
 
  initscr();
  if(has_colors() == FALSE)
  { 
    endwin();
    printf("Su terminal no permite usar colores!\n");
    exit(1);
  }
  start_color();
  cbreak();
  noecho();
  keypad(stdscr, TRUE);
 
  clear();
  init_pair(1, COLOR_CYAN, COLOR_BLACK);
  attron(COLOR_PAIR(1));
  mvaddstr(2,10,"hola");
  attroff(COLOR_PAIR(1));
  refresh();
  tecla = getch();
  if (tecla == KEY_LEFT)
     printw("Has pulsado izquierda");
  getch();
  endwin();
}
 

Al igual que ocurría con Turbo C, esto no es todo lo que hay. También podemos crear ventanas, definir colores “a medida” a partir de su cantidad de rojo, verde y azul, podemos leer lo que hay en la pantalla, podemos manejar el ratón, e incluso tenemos rutinas para manejo de paneles y de formularios, pero todo ello queda fuera del cometido de este apartado, que es puramente introductorio.

Ejercicios propuestos: