A veces es muy útil saber el contenido de una variable en mitad de ejecución del programa, pero cuando queremos compilar la versión definitiva, eliminar todas las trazas de depuración de una vez, aunque cuando estemos desarrollando, es interesante volverlas a tener.
Podemos hacerlo de forma sencilla con algunas macros de preprocesador de C; podemos poner un pequeño fragmento de código al empezar cada programa, y lo mejor de todo es que la versión definitiva seguirá ocupando lo mismo, ya que si desactivamos la depuración es como si en el código final no se escribieran las trazas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include
#define depuracion
#ifdef depuracion
#define depura_int(variable) printf("%s: Linea: %d (Variable \"%s\" vale: %d)\n", __FILE__, __LINE__, #variable, variable);
#define depura_string(variable) printf("%s: Linea: %d (Variable \"%s\" vale: \"%s\")\n", __FILE__, __LINE__, #variable, variable);
#else
#define depura_int(variable)
#define depura_string(variable)
#endif
int main()
{
int numero=99;
char strtest[20]="CADENA DE PRUEBA";
depura_int(numero)
depura_string(strtest)
return 0;
} |
Observad lo que hacen las macros depura_int y depura_string, nos darán el número de línea y el archivo donde están, así como el nombre de la variable y el valor que tiene en ese momento.
Cuando no queramos depurar comentamos la línea que contiene #define depuracion y listo.
A veces, es un poco difícil que alguien que sólo conoce conio.h se pase a Linux, más que nada, porque se puede utilizar ncurses, pero hay que cambiar un poco de mentalidad para poder trabajar con la nueva biblioteca.
Por eso, hace unos meses creé unas cuantas funciones que se podían utilizar para ir reemplazándolas poco a poco. Funcionan de forma muy parecida a conio.h (en los colores). Se basan en códigos ANSI, y las podemos utilizar para cualquier programa rápido en que necesitemos utilizar colores en terminal. ncurses es muy potente y si vamos a complicarnos un poco más la vida, mejor utilizar esta segunda.
Cuando estuve implementando estas funciones, tuve un problema, a la hora de leer información del terminal sin mostrarla por pantalla, me basé en: wsize.c de Stephen J. Friedl. También podéis encontrar información sobre los códigos ANSI aquí.
He incluído la opción subrayado (UNDERLINE) y un par de funciones que nos calculan las dimensiones de la pantalla (ya que los terminales pueden ser redimensionados y ser enormes).
Descargar stermp - Simple Terminal Play (stermp.c, stermp.h)
Os pongo aquí el código del ejemplo (conio.c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <stdio.h>
#include "stermp.h"
int main ()
{
int pp , fn ;
clrscr ();
for (pp =BLACK ; pp <=WHITE ; pp ++)
{
gotoxy (5, 20+pp );
for (fn =BLACK ; fn <DARKGRAY ; fn ++)
{
textcolor (pp );
textbackground (fn );
printf("COLOR\t");
}
}
restore_color ();
textcolor (RED +UNDERLINE );
gotoxy (51, 5);
printf("Posición X: %d\n", wherex ());
printf("Posición Y: %d\n", wherey ());
printf("Pantalla %dx%d", screenwidth (), screenheight ());
} |
Al principio, cuando empezaba a programar, yo me quejaba mucho de esa tonta manía de C que no me dejaba devolver arrays como resultado de una función.
Aunque al final, devolver arrays es una tontería, gasta memoria, gasta tiempo y termina siendo más fácil solucionarlo todo con un puntero.
Pero bueno, si realmente queremos devolver un array, siempre podemos meterlo dentro de un registro. Probando con una cadena de caracteres:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h>
#include <string.h>
typedef struct
{
char cadena [1000];
} t_array ;
t_array devuelvo ()
{
t_array otroarr ;
printf("\nDirección de cadena: %X\nCadena: %s\n", &otroarr , otroarr. cadena);
strcpy (otroarr. cadena, "ESTE STRING APARECE VARIAS VECES EN EL VOLCADO DE MEMORIA");
printf("\nDirección de cadena: %X\nCadena: %s\n", &otroarr , otroarr. cadena);
return otroarr ;
}
int main ()
{
t_array array ;
array =devuelvo (&mem );
printf("\nDirección de cadena: %X\nCadena: %s\n", &array , array. cadena);
} |
Si tenéis un equipo Linux que actúe como servidor y equipo de música (por poner algo), y no tenga un monitor conectado en todo momento; os puede ser útil este script:
1 2 3 4 5 6 7 8
| #!/bin/bash
hora=`date +%H`
min=`date +%M`
decir="Son las "$hora" y "$min
echo $decir | espeak -ves |
Si asociamos este script a una combinación de teclas, por los altavoces nos dirá la hora que es. Es muy útil en ocasiones
A artir de aquí se puede extender tanto como se quiera: por ejemplo para que diga la hora en un lenguaje más coloquial: Son las tres menos cuarto, por ejemplo. Es cuestión de imaginación.
Requiere del sintetizador espeak aunque no debe ser difícil utilizar festival u otros.
Espero que os sea de gran ayuda
Matlab® es un lenguaje muy utilizado en ingeniería, ya que permite hacer de forma más o menos sencilla cálculos matemáticos muy complejos, y a veces necesitamos procesar gran cantidad de datos y llega a ser desesperante ver cómo tarda sin tener tan siquiera una ligera idea de cuánto queda.
El principio del código que voy a postear se basa en dos funciones: tic y toc. La primera de ellas marca el inicio de la cuenta de tiempo, y la segunda el final, indicándonos los segundos transcurridos entre tic y toc. Podemos por ejemplo hacer esto:
1 2 3
| tic
% Tarea que tarda mucho
toc |
Y nos devolverá los segundos en total que ha tardado.
Pero vamos a ir algo más allá. Imaginemos que tenemos una tarea iterativa de N iteraciones, queremos que cada N/20 iteraciones nos diga más o menos lo que queda (en cada iteración sería mucho, porque estaría todo el rato diciendo el tiempo, y además gastando tiempo en ello; por lo que tendríamos que esperar mucho más de lo necesario).
Éste es el código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| N= 2000;
aviso= 100;
tic % Inicia el contador de tiempo
for n= 0:N
% Calculamos el progreso total y los tiempos estimado y restante
if (mod(n, aviso )== 0)
progreso=n/N; % Progreso (Entre 0 y 1)
transcurrido= toc; % Tiempo transcurrido
estimado=transcurrido/progreso; % Estimamos el tiempo total
queda = estimado-transcurrido; % Tiempo restante
% Lo mostramos todo en pantalla
disp(sprintf('Progreso %2.2f%%. Tiempo Transcurrido: %ds. Tiempo estimado: %ds. Tiempo restante %ds.', progreso* 100, round(transcurrido ), round(estimado ), round(queda )));
end
% Esto se supone que es una tarea que tarda mucho tiempo
pause(0.01);
end
disp('Completado!'); |
Cuando trabajamos con Matlab® a la hora de poner los datos de una variable en pantalla, basta con poner la variable (sin ; al final), pero si queremos escribir un texto cualquiera, debemos utilizar disp():
1
| disp('Estoy escribiendo un mensaje en matlab'); |
Pero si queremos intercalar información o escribir nuestras variables de una forma más elegante, debemos utilizar sprintf(), se comporta como el sprintf() de C, y acepta sus \n, \t, %d, %f, %s… incluso podemos formatear la salida como veremos en el ejemplo. Para más información acerca de lo que podemos introducir en la función, es mejor acudir a help.
Esta función combinada con disp(), como vemos arriba, trabaja muy parecido a printf() de C.
Pero,
¿qué hace sprintf() cuando en vez de un número le pasamos una matriz? Insertará todos los elementos seguidos, como si de un número muy largo se tratase.
¿cómo pongo un signo %? disp(sprintf(’%%’));
Es común en los programas de consola hacer preguntas al usuario y éste responda, pero sólo una de las opciones válidas, si no, volver a preguntarle hasta que nos diga un valor correcto.
Aquí os enseño un ejemplo sencillo de todo esto (utilizaré la función mygetch):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
int mygetch (int echo )
{
struct termios oldt , newt ;
int ch ;
/* Obtenemos atributos del terminal */
tcgetattr ( STDIN_FILENO , &oldt );
newt = oldt ;
/* Eliminamos el modo canónico: caracteres especiales */
newt. c_lflag &= ~ICANON ;
/* Eliminamos el echo a voluntad */
if (!echo )
newt. c_lflag &= ~ECHO ;
/* Definimos los nuevos atributos al terminal */
tcsetattr ( STDIN_FILENO , TCSANOW , &newt );
ch = getchar ();
/* Ponemos los atributos como estaban al principio */
tcsetattr ( STDIN_FILENO , TCSANOW , &oldt );
return ch ;
}
int get_option (char *texto , char *validas )
{
int tecla ;
char *ok ;
do
{
/* Mostramos texto en pantalla */
printf("%s: ", texto );
/* Pedimos una tecla y la pasamos a mayúsculas, para no */
/* hacer demasiadas comprobaciones luego */
tecla =toupper (mygetch (1));
/* strpbrk nos dirá si tecla es una de las válidas */
ok =strpbrk ((char*)&tecla , validas );
/* strpbrk devuelve NULL si no ha encontrado tecla en validas */
if (ok ==NULL)
printf("\n¡Opción incorrecta! Por favor seleccione una de estas: %s\n", validas );
} while (ok ==NULL);
return tecla ;
}
int main ()
{
int tecla ;
do
{
tecla =get_option ("Desea continuar? [(S)i, (N)o, (M)e lo pienso]", "SNM");
switch (tecla )
{
case 'S': printf("\nHas elegido continuar...\n");
break;
case 'N': printf("\nSalir\n");
break;
case 'M': printf("\nTe doy 5 segundos para pensártelo\n");
sleep (5);
break;
}
} while (tecla !='N');
return 0;
} |
Como vemos, sólo tenemos que llamar a la función get_option, pasarle las opciones válidas, y la función sólo nos va a devolver una de las opciones que le digamos.
Hace mucho tiempo, cuando empezaba con la programación, tenía la librería conio.h de Borland (en la que aún se siguen basando en muchos sitios), que nos permitía entre otras cosas borrar la pantalla, posicionarnos dentro de la pantalla, escribir con colores, y pedir una tecla al usuario.
Bien, vayámonos al último caso, pedir una tecla al usuario, se hacía con la función getch, y con sólo pulsar la tecla, salía de la función, es decir, no hacía falta pulsar enter.
Pero cuando nos vamos a linux, nos damos cuenta de que tenemos que utilizar getchar() y esa función estará pidiendo letras del teclado hasta que pulsemos enter.
Podemos optar por varias soluciones:
- Utilizar ncurses (que le da mil vueltas a conio.h)
- Si sólo queremos algo puntual, la que os propongo a continuación
La solución la encontré hace un tiempo y la tenía por aquí archivada, y la primera publicación del código que he encontrado ha sido esta por kermi3, la función original la publicó VvV.
La solución que propongo tiene un pequeño cambio en el que podemos elegir si queremos que se muestre dicha tecla pulsada o no (echo), y está comentada:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <stdio.h>
#include <termios.h>
#include <unistd.h>
int mygetch (int echo )
{
struct termios oldt , newt ;
int ch ;
/* Obtenemos atributos del terminal */
tcgetattr ( STDIN_FILENO , &oldt );
newt = oldt ;
/* Eliminamos el modo canónico: caracteres especiales */
newt. c_lflag &= ~ICANON ;
/* Eliminamos el echo a voluntad */
if (!echo )
newt. c_lflag &= ~ECHO ;
/* Definimos los nuevos atributos al terminal */
tcsetattr ( STDIN_FILENO , TCSANOW , &newt );
ch = getchar ();
/* Ponemos los atributos como estaban al principio */
tcsetattr ( STDIN_FILENO , TCSANOW , &oldt );
return ch ;
}
int main ()
{
int tecla ;
printf("Pulsa una tecla (sin echo): ");
tecla =mygetch (0);
printf("\nHas pulsado la tecla %c (%c)\n", tecla , tecla );
printf("Pulsa una tecla (con echo): ");
tecla =mygetch (1);
printf("\nHas pulsado la tecla %c (%c)", tecla , tecla );
} |
La incluiré en la próxima revisión de strutils.
A veces, mientras se está desarrollando un pequeño programa en C en el que hay entradas del usuario por teclado, hay veces que parece que se pulsan teclas solas, esto es debido a una entrada de teclado anterior que no ha llegado a volcarse entera en nuestras variables.
En principio tenemos fflush(), es una función para el volcado de buffer de escritura, escritura de ficheros o escritura en la pantalla; pero bueno, funciona cuando hacemos fflush(stdin) y compilamos en Windows, pero no en Linux… tenemos un par de soluciones (aunque seguro que se nos ocurren muchas más):
- __fpurge() - Aunque no es una función estandar, y en alguna ocasión me ha dado algún problema (no ha hecho su trabajo como debía).
- La otra solución, parece un poco más rudimentaria, pero no me ha dejado tirado:
1 2
| int ch;
while ((ch = getchar()) != '\n' && ch != EOF); |
Hace tiempo tuve la necesidad de hacer una función de cambios de base, pero que no estuviera limitada, es decir, no tuviera definido qué base vamos a introducir y qué base debe devolver. Es decir, convertiremos un número en base b1 a base b2; la limitación, el número de caracteres con los que contemos, en el ejemplo hay hasta base 36, y es fácil extenderlo hasta base256… un número en hexadecimal no tiene desde el 0 al 9 y de la A a la F, pues un base36, del 0 al 9 y de la A a la Z. Con un poco de imaginación podremos sacar muchas ideas de este fragmento. Incluye programa de ejemplo:
Hace tiempo tuve la necesidad de hacer una función de cambios de base, pero que no estuviera limitada, es decir, no tuviera definido qué base vamos a introducir y qué base debe devolver. Es decir, convertiremos un número en base b1 a base b2; la limitación, el número de caracteres con los que contemos, en el ejemplo hay hasta base 36, y es fácil extenderlo hasta base64, base256, por ejemplo… con un poco de imaginación podremos sacar muchas ideas de este fragmento. Incluye programa de ejemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include <stdio.h>
#include <string.h>
#include "strutils.h"
void cambiobase (char *n1 , int b1 , char *n2 , int b2 )
{
int lnum = strlen (n1 ); /* Longitud de nuestro número */
/* Caracteres que forman todos los posibles números hasta base 36 */
char numeros [37]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int divide ;
int i ;
int posn =0;
int long2 ;
do
{
divide = 0;
long2 = 0; /* Será un puntero de n1, */
/* ya que iremos modificando su valor, long2 */
/* nos indicará por dónde vamos */
for (i =0; i <lnum ; i ++)
{
/* divide vale lo que valía antes multiplicado por la base + el número que acabamos de extraer */
/* que coincidirá con la posición del carácter dentro del array de numeros[] */
divide =divide *b1 +strchr (numeros , n1 [i ])-(char*)&numeros ;
if (divide >=b2 )
{
/* Si el número resultante es mayor o igual a la base */
n1 [long2 ] = numeros [(int) divide / b2 ]; /* Modificaremos el dígito long2 de n1, */
/* y este será el número divide/b2 en base b1 */
divide = divide % b2 ; /* divide será el resto de la división */
long2 ++;
}
else if (long2 >0)
{
n1 [long2 ] = '0'; /* divide<b2, pero en iteraciones anteriores ha sido >= */
long2 ++;
}
}
lnum =long2 ;
/* hemos hallado un dígito de n2. Su valor será divide */
n2 [posn ] = numeros [divide ];
posn ++; /* posn es el puntero del nuevo número */
} while (long2 !=0);
n2 [posn ] = '\0';
n2 =strrev (n2 , posn );
}
int main ()
{
char num1 [20], num2 [20];
int base1 , base2 ;
printf("Numero a convertir:");
scanf ("%s", num1 );
printf("¿En qué base está ese número? ");
scanf ("%d", &base1 );
printf("¿A qué base quieres convertirlo? ");
scanf ("%d", &base2 );
cambiobase (num1 , base1 , num2 , base2 );
printf("\n%s\n", num2 );
return 0;
} |
La idea principal la saqué hace tiempo de un código escrito en otro lenguaje.
Tenemos que tener en cuenta que esta función utiliza a strutils.h (ver anterior post), aunque también podéis copiar directamente la función strrev y a compilar!!
Todo esto es debido a que el algoritmo para cambiar la base genera el número (como una cadena, pero al fin y al cabo un número) al revés, es decir el dígito de menor peso primero.
Últimos comentarios