lunes, 15 de agosto de 2011

Generar números aleatorios en C

Usando la función rand() para obtener números aleatorios, la primera vez que lo ejecutas esta bien, pero lo ejecutas nuevamente te mostrara la misma serie de números generados anteriormente. Para solucionar esto usaremos la función srand() para cambiar la variable (semilla) en la que se basa rand() para generar los números aleatorios.
Para obtener números aleatorios eficazmente necesitamos una semilla que varia de un instante a otro, por eso usaremos el comando en ensamblador rdtsc, nos retornará el numero de ciclos del procesador desde el inicio.Esta solución funciona únicamente con procesadores x86.


El siguiente código genera 10 números aleatorios:

#include "stdio.h"
#include "stdlib.h"
#include "stdint.h"

__inline__ uint64_t rdtsc();

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

    int i;    
    srand(rdtsc());
    for(i=0; i<10; i++)
    {
         printf("%d\n", (int)(16.0 * (rand() / (RAND_MAX + 1.0))));
    }

  return 0;
}
__inline__ uint64_t rdtsc()
{ 
    uint32_t lo, hi;
    __asm__ __volatile__ ("xorl %%eax,%%eax \n cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx");
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
    
   return (uint64_t)hi << 32 | lo;
}
Referencia: http://en.wikipedia.org/wiki/Time_Stamp_Counter#C.2B.2B

domingo, 14 de agosto de 2011

Instalando Allegro en Debian 6

Allegro es una biblioteca libre y de código abierto para la programación de videojuegos desarrollada en lenguaje C. La biblioteca cuenta con funciones para gráficos, manipulación de imágenes, texto, sonidos, dispositivos de entrada (teclado, ratón y mandos de juego) y temporizadores, así como rutinas para aritmética de punto fijo y acceso al sistema de archivos.


Para instalar esta libreria en Debian 6 (también aplica para derivados) sigue los siguientes pasos:


1.)Abre el gestor de paquetes Synaptic:


2.) Selecciona e instala los paquetes, escribe “liballegro” sin comillas y selecciona todos los paquetes y luego dale al botón "Aplicar" para que comience  la instalación.




3.)Probando, para cerciorarnos si todo funciona bien compilaremos el siguiente código:

#include "allegro.h"

int main(){
    //iniciamos Allegro
    allegro_init();

    //cargamos el teclado
    install_keyboard();

    //iniciamos modo grafico en ventana con resolución de 640x480
    set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0);

    //imprimimos un mensaje en pantalla
    textout_ex( screen, font, "PINGUINAZOS! presiona una tecla para salir", 5, 5, makecol( 255, 0, 0), -1);
    //esperamos a que se presione una tecla.
    readkey();
    
    return 0;
}
//indica que el programa de Allegro termino, es necesario ponerlo despues del main.
END_OF_MAIN();


Para compilarlo debemos agregar la sentencia `allegro-config --static` como parámetro al compilar, seria algo como esto:


# g++ AllegroDemo.cpp -o allegro `allegro-config --static`
# ./allegro


y este seria el resultado:



Si no tienes Debian u otra distro basada en debian, por ejemplo Fedora o SUSE, puedes usar el gestor de paquetes YUM (en fedora) o YasT (en SUSE) y buscar los paquetes mostrados anteriormente. Si este no es tu caso y tienes alguna otra distro, en el siguiente link encontrarás con un PDF con información acerca de la instalación de Allegro descargando el .tar.gz y compilandolo manualmente.

Estructura de Datos - Agenda con listas ligadas C/C++

En este ejemplo se muestra el manejo de memoria dinámica en C/C++ con listas simples. Dentro del código podemos observar los procesos básicos de la estructura de datos: creación, eliminación, impresión y búsqueda.

En el compilador g++ en GNU/linux nos dice que el uso de la función gets() para leer cadenas de texto es peligroso, por lo tanto las cadenas las tomamos con scanf("%*c%[^\n]", nodo->cadena) que seria equivalente.(leer directamente una cadena con %s con scanf solo nos guarda la cadena hasta que encuentre un espacio, por eso usamos "%*c%[^\n]" , para indicar que guarde hasta que el usuario teclee un Enter.


#include "stdio.h"
#include "stdlib.h"
#include "string.h"

struct agenda
{
      int tel;
      char nombre[30];
      struct agenda *siguiente;
}*inicio = NULL, *nodo = NULL;

void menu();
void agregar_contacto();
void eliminar_contacto();
int listar();
void mostrar_lista();

int main(int argc, char *argv[]){
      
      int opcion = 0;
      
      do
      {    
          menu();  
          fflush(stdin);                     
          scanf("%d", &opcion);         
          
          switch(opcion)
          {
             case 1:
                   agregar_contacto();
                   break;
             case 2:
                   eliminar_contacto();
                   break;
             case 3:
                   mostrar_lista();
                   break;   
             case 4:
                   exit(0); 
                   break;         
             default:
                   printf("\n\a  Error! Opcion invalida...\n\n"); 
                   break;                 
          }  
      
      }while(opcion != 4);
       
      return EXIT_SUCCESS;
}

void menu()
{      
      printf("Seleccione una opcion: \n");
      printf("\t1.Agregar contacto\n");
      printf("\t2.Eliminar contacto\n");
      printf("\t3.Mostrar lista de contactos\n");
      printf("\t4.Salir\n");
      printf("OPCION: ");      
}

void agregar_contacto()
{     
      fflush(stdin);
      struct agenda *aux = NULL;
      nodo = (struct agenda*)malloc(sizeof(struct agenda));
          
      printf("\tNombre: ");      
      scanf("%*c%[^\n]",nodo-> nombre);
      printf("\tTelefono: ");
      if(scanf("%d", &nodo->tel) != 1)
      {
            printf("\n\tError! numero de telefono  \n\n");
            free(nodo);            
            return;   
      }    
      
      if(inicio == NULL)
      {
            inicio=nodo;
            nodo->siguiente = NULL;
      }
      else
      {
            aux = inicio;
            while(aux->siguiente != NULL)
            {
                  aux = aux->siguiente;
            }
            aux->siguiente = nodo;
            nodo->siguiente = NULL;
      }      
      
      return;
}

void eliminar_contacto()
{     
      if(inicio == NULL)
      {
            printf("\nUsted no ha agregado ningun contacto\n\n"); 
            return;          
      }
      
      fflush(stdin);      
      
      struct agenda *aux;      
      int maximo = 0, objetivo = 0, contador = 0;
      bool flag = false;
       
      printf("Ingrese el numero de contacto que desea eliminar: \n");
      maximo = listar();
      
      printf("\n\tELIMINAR: ");
      if(scanf("%d", &objetivo) != 1 || objetivo > maximo || objetivo < 1)
      {
            printf("\n\tError! ha ingresado una opcion invalida \n\n");
            return;
      } 
           
      nodo = inicio;      
      while(!flag)
      {
            contador++;
            if(contador == objetivo ) flag = true;
            else nodo=nodo->siguiente;            
      }      
      
      /* proceso de eliminacion */
       
      if(nodo == inicio)  //si es el primero
      {
            inicio = nodo->siguiente;
            free(nodo);
      }
      else
      {
            aux = inicio;
            while(aux->siguiente != nodo)
            {
                  aux = aux->siguiente;
            }
            aux->siguiente = nodo->siguiente;
            free(nodo);
      }
    
    
}

int listar()
{
      struct agenda *aux = NULL;
      int contador = 0;
            
      for(aux=inicio;aux!=NULL;aux=aux->siguiente)
      {           
            contador++;      
            printf("\t %d.%s \n", contador, aux->nombre);                    
      }
      
      return contador;   
}
void mostrar_lista()
{
      if(inicio == NULL)
      {
            printf("\nUsted no ha agregado ningun contacto\n\n"); 
            return;          
      }
      
      fflush(stdout);
      struct agenda *aux = NULL;
            
      for(aux=inicio;aux!=NULL;aux=aux->siguiente)
      {                 
            printf("-> Nombre: %s\n",aux->nombre);           
            printf("   Telefono: %d\n\n", aux->tel);
            
      }      
}

Como invertir un numero en C

Como el titulo lo indica, como invertimos un numero en C, sin usar arrays:


#include  "stdio.h"
#include  "stdlib.h"

int main(int argc, char *argv[]) 
{ 
      long numero; 
      int digito; 

      printf( " Ingrese numero: " ); 
      scanf( "%ld", &numero ); 

      printf( " Su numeral invertido es: " ); 

      while( numero > 0 ) 
      { 
            digito = numero % 10; 
            numero /= 10; 
            printf( "%d", digito ); 
      } 

      return 0; 
} 
La salida es:

sábado, 13 de agosto de 2011

Interfaz gráfica usando GTK

¿Y como hago la interfaz gráfica?, es tal vez es unas de las primeras preguntas que nos hacemos al migrar a linux, desde windows creábamos ventanas de diferentes maneras, ya sea con wxDevC++, Borland C++ Builder, usando APIs de Windows, etc... En GNU/Linux encontramos también diferentes formas de crear una aplicación con interfaz gráfica de usuario, en esta ocasión mostrare como hacerlo con la biblioteca GTK ("GIMP Tool Kit"), con esta biblioteca se desarrollan en su mayoría aplicaciones para los entornos gráficos GNOME, XFCE y ROX aunque también se puede usar en el escritorio de Windows, MacOS y otros.

Lo primero que debemos hacer es instalar la librería GTK, tecleamos en la terminal como superusario:

En Debian y derivados:
# apt-get install libgtk2.0-dev


En Fedora:
# yum -y install libgtk2.0-dev


Instalado esto, podemos empezar nuestra primera aplicación GTK, les muestro este sencillo ejemplo:
#include "gtk/gtk.h"

void hello( GtkWidget *widget, gpointer data);
int contador_click = 0;

int main(int argc, char *argv[])
{ 
      //Creamos los widget a utilizar, una ventana principal y un boton
      GtkWidget* ventana;
      GtkWidget* boton;
   
      gtk_init(&argc, &argv);//llamada a todas las aplicaciones GTK
   
      //Creamos una nueva ventana y un boton con una etiqueta
      ventana=gtk_window_new(GTK_WINDOW_TOPLEVEL);
      boton = gtk_button_new_with_label ("Imprimir");
   
      //Asignamos el alto de la ventana
      gtk_container_set_border_width (GTK_CONTAINER (ventana), 50);

      /* definimos los eventos. Para la ventana, hacemos que cierre 
       * correctamente al presional el boton de salida "x". Para el boton 
       * hacemos una llamada a la funcion "hola" cuando se le de click*/     
     
      g_signal_connect (boton, "clicked",G_CALLBACK (hello), NULL);
      g_signal_connect(ventana, "delete-event", G_CALLBACK(gtk_main_quit), NULL);
   
      //agregamos el boton a la ventana
      gtk_container_add (GTK_CONTAINER (ventana), boton);
   
      //mostramos los widget
      gtk_widget_show (boton);
      gtk_widget_show(ventana);
   
      gtk_main(); //Controla la salida y espera por un evento a ocurrir
   
      return 0;
}

/* Esta funcion imprime un mensaje en la terminal cada vez que el usuario
 * hace click en el boton */
void hello( GtkWidget *widget, gpointer data)
{
     contador_click++;
     g_print ("Hola Mundo Cruel %d \n", contador_click);
}


Y este es el resultado:



Al compilar manualmente en la terminal deben agregar la librería gtk de la siguiente manera:
g++ interfazGTK.cpp -o pinguinazos `pkg-config --libs gtk+-2.0` `pkg-config --cflags gtk+-2.0`

O puede usar una IDE y adjuntar esos parámetros al proyecto.( ej: Geany, Anjunta).

viernes, 12 de agosto de 2011

Usando Ncurses - Funcion getch y move

Este es mi primera entrada en el blog, no sabia que publicar así que he decidido empezar precisamente por el inicio de mis experiencias al migrar a GNU/Linux. Cuando estaba en segundo semestre en la universidad, en las clases de lenguajes de programación compilábamos código en C bajo dev c++, y unas de las primeros ejercicios que realizó nuestra clase fue un programa que mostraba el código ASCII de cada tecla. Cuando intentaba compilar el código en casa, (bajo Fedora 12 en ese tiempo) me mostraba un error debido a que la función getch() no es ANSI C, ya que pertenece a la biblioteca conio.h creada por Borland que no existe en GNU/linux.
Navegando en internet me encontré con ncurses y resolví mi inquietud de la siguiente manera:

#include "ncurses.h"

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

      initscr();     
      int t;   
   
      while((t=getch())!=27)
      {
            move(5,0);     
            printw("Codigo ASCII: %d  Caracter: %c \n", t, t);
            move(0,0);
            printw("Presiona ESC para terminar... "); 
       }     
     
      endwin();
      return 0;
}
Al usar ncurses cambiamos un poco la forma de leer datos por el teclado e imprimir por pantalla, en lugar de usar printf y scanf, se usará printw y scanfw que funcionan exactamente de la misma manera.
También hay que tener en cuenta que al usar ncurses, debemos usar la función initscr() para iniciar la pantalla y endwin() para terminar; el getch() funciona exactamente igual que el de conio.h. Por otro lado, también observamos la función move(), con ella podemos posicionar el cursor donde queramos enviándole las coordenadas, seria el equivalente a gotoxy() (también de conio.h).
Y por ultimo no se les olvide al compilar agregar el argumento de la libreria ncurses, quedando de esta manera:
g++ ejemplo.cpp -o demo -lncurses