Archivo de la categoría: Desarrollo

Temas relacionados con programación y herramientas de desarrollo.

El resultado de la CUPCAM

Y al final todo llega y nosotros no hicimos buen papel :( Fuimos un total de 19 grupos a participar y nosotros quedamos en el puesto 11 :'(. De todos modos volveremos el año próximo y venceremos.

De momento decir que me quedé pillado con un problema y analizando la solución era igual que la mia, solo que yo no me di cuenta de una cosa y hacía que el tiempo de ejecución de m algoritmo tendiese a infinito :(

La solución del problema es la siguiente. Muchos reconocerán en ella el algoritmo de la mochila o de la estantería. El caso es que yo nunca había oído hablar de él.

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
#include stdlib.h
#include stdio.h
 
#define MAX 1001
 
int T[MAX] = { -1 };
 
int max(int a, int b)
{
   return a>=b ? a : b;
}
 
/**
* Pasamos cuanto espacio tenemos y el ultimo libro de la lista a comprobar.
* Devuelve el maximo espacio que puede ocupar.
*/
int A(int espacio, int libros)
{
   int a, b;
 
   /* Si el espacio libre es 0 o no hay libros que colocar, el espacio
   * que ocupamos es 0 */
   if( !espacio || !libros) return 0;
 
   /* El libro ocupa mas que el espacio que tengo libre,
   * por lo que sigo sin tenerlo en cuenta */
   if( T[libros] > espacio ) return A(espacio, libros-1);
 
   /* Si nuestro libro cabe, devolvemos el maximo entre poner
   * el libro actual e intentarlo con el resto o directamente
   * intentarlo con el resto sin tener en cuenta el libro
   * actual */
   return max( A(espacio, libros-1), T[libros]+A(espacio-T[libros], libros-1) );
}
 
int main(void)
{
   int i;
   int espacio;
   int num_libros;
 
   while(1) {
      scanf("%d %d", &espacio, &num_libros);
      if( espacio == 0 && num_libros == 0 ) break;
 
      for(i = 1; i <= num_libros; i++ ) {
         scanf("%d", &(T[i]));
      }
      printf("%d\n", espacio-A(espacio, num_libros));
   }
   return 0;
}

La solución que yo hice era más como la que sigue:

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
#include stdlib.h
#include stdio.h
 
#define MAX 1001
 
int T[MAX] = { -1 };
 
int max(int a, int b)
{
   return a>=b ? a : b;
}
 
/**
* Pasamos la lista de libros, el espacio que queda libre y el total de libros en la lista.
*/
int A(int *tamanos, int espacio, int libros)
{
   int c;
   int resultado = espacio;
   int temporal;
 
   if( !libros ) return espacio;
 
   /* Calculamos el espacio libre teniendo en cuenta que usamos el libro actual */
   for(c=1; c < libros; c++) {
      temporal = A(tamanos[c], espacio-tamanos[0], libros-1);
      if(temporal<resultado ) resultado = temporal;    
   }
   /* Calculamos el espacio libre sin tener en cuenta que usamos el libro actual */
   for(c=1; c < libros; c++) {
      temporal = A(tamanos[c], espacio, libros-1);
      if(temporal < resultado ) resultado = temporal;
   }
   return resultado;
}
 
int main(void)
{
   int i;
   int espacio;
   int num_libros;
 
   while(1) {
      scanf("%d %d", &espacio, &num_libros);
      if( espacio == 0 && num_libros == 0 ) break;
 
      for(i = 1; i &lt;= num_libros; i++ ) {
         scanf("%d", &a(T[i]));
      }
      printf("%d\n", A(espacio, num_libros));
   }
   return 0;
}

Ahora no recuerdo muy bien si era esactamente así, pero sí se que funcionaba. Como podéis comprobar, nuestro gran fallo (bueno, mi gran fallo) fue que pasaba muchas veces por las mismas convinaciones :@ El bucle ese fue mortal y no nos dismo cuenta ninguno que se podía quitar :'(

La próxima vez lo haremos mejor.

Punteros inteligentes en C++ (smart pointers) 3

En el artículo anterior nos quedamos con que hacía falta añadir nuevas funcionalidades que nos permitan mejorar las características del invento. Así que vamos a empezar poco a poco.

Para empezar, vamos a suponer que queremos poder asignar un puntero con el operador =. Para ellos hacemos lo siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<typename _t> class PtrSimple2 {
[..]
   PtrSimple2<_t>&
   // </_t>
   operator=( const PtrSimple2<_t>& ptr ) {
      // Hay que decrementar el puntero viejo, y en caso necesario
      // liberar la memoria
      (*_count) = (*_count) - 1;
      if( *_count == 0 ) {
         delete _count;
         delete _ptr;
      }
      _count = ptr._count;
      _ptr = ptr._ptr;
      (*_count) = (*_count) + 1;
 
      return *this;
   }
[..]
};
//</_t></typename>

Con esto ya tendríamos la asignación, pero sigue existiendo un problema de base: ¿qué pasa con la herencia? Por ejemplo, tenemos una clase A y otra B que hereda de A. Si yo creo que puntero de tipo A y otro de tipo B, me gustaría poder pasar sin problemas de uno a otro y que lo reconozca (¿sería lo lógico, no?). Pues tal y como están las cosas ahora, no lo haría (haced la prueba si no me creéis). Para solventar esto, añadimos los siguientes fragmentos de código:

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
template<typename _t> class PtrSimple2 {
//</typename>
[..]
public:
   // Para poder leer el puntero sin problemas
   _t* get_ptr() {
      return _ptr;
   }
 
   // Nuevo constructor
   // Estoy simplificando algunas cosas, es posible que no se pueda acceder
   // directamente a los miembros del otro puntero
   template<typename _u> PtrSimple2( const PtrSimple2<_u>& ptr ) {
      _count = ptr._count;
      _ptr = dynamic_cast<_t *>( ptr._ptr );
      (*count) = (*count) + 1;
   } // </_t></_u></typename>
 
   // Con el operador = hay que hacer lo mismo
   template<typename _u> PtrSimple2<_t>& operator=( const PtrSimple2<_u>& ptr ) {
      // Hay que decrementar el puntero viejo, y en caso necesario
      // liberar la memoria
      (*_count) = (*_count) - 1;
      if( *_count == 0 ) {
         delete _count;
         delete _ptr;
      }
      _count = ptr._count;
      _ptr = dynamic_cast<_t *>( ptr._ptr );
      (*_count) = (*_count) + 1;
 
      return *this;
   }
[..]
};
//</_t></_u></_t></typename>

¿Con esto ya estaría todo resuelto, no? Podemos controlar la herencia, los punteros, etc. Eso sí, siempre y para todo hay que utilizar este tipo de dato. Entonces, ¿puede haber casos en los que esto no funcione? La respuesta es sí. Imaginemos que tenemos que pasar a un método el la dirección a la que apuntamos (lo que hacemos con get_ptr) y luego podemos, desde otro lado, tomar dicha dirección en otro puntero.

Esta situación tan incomoda se pude solucionar con una política aún más agresiva de control de punteros. Para esto, debemos separar la parte de los contadores de punteros de la del propio control de los punteros. Vamos a ver otra clase para hacer punteros inteligentes que sigue una política un poco más agresiva:

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// ESTO VA EN LA CABECERA
 
void pointer_inc_ref( void *ptr );
bool pointer_dec_ref( void *ptr );
 
/**
 * Creates a smart pointer type that can free the memory used
 * automatically when it isn't necesary.
 * This pointer type can be very slow.
 */
template<typename _t> class pointer_to {
	Type *ptr;
 
public:
	pointer_to( _t *pointer ) {
		ptr = pointer;
		pointer_inc_ref(ptr);
	}
 
	pointer_to( const pointer_to<_t>& pointer ) {
		ptr = pointer.ptr;
		pointer_inc_ref(ptr);
	}
 
	template<typename _u> pointer_to( const pointer_to<_u>& pointer ) {
		ptr = dynamic_cast<_t *>( pointer.get() );
		pointer_inc_ref( ptr );
	}
 
	~pointer_to() {
		if( pointer_dec_ref( ptr ) )
			delete ptr;
	}
 
	_t* get() const {
		return ptr;
	}
 
	_t* operator->() const {
		return ptr;
	}
 
	_t& operator*() const {
		return *ptr;
	}
 
	// </_t>
	pointer_to<_t>& // </_t>
	operator=( const pointer_to<_t>& pointer ) {
		// </_t>
		if( pointer_dec_ref(ptr) )
			delete ptr;
		ptr = pointer.ptr;
		pointer_inc_ref(ptr);
	}
 
	pointer_to<_t>& operator=( _t*pointer ) {
		if( pointer_dec_ref(ptr) )
			delete ptr;
		ptr = pointer;
		pointer_inc_ref(ptr);
		return *this;
	}
 
	template<typename _u> pointer_to<_t>& operator=( const pointer_to<_u>& pointer ) {
		if( pointer_dec_ref(ptr) )
			delete ptr;
		ptr = dynamic_cast<_t>( pointer.ptr );
		pointer_inc_ref(ptr);
		return *this;
	}
}; // class pointer_to
// </_t></_u></_t></typename></_t></_u></typename></_t></typename>
 
 
// ESTO VA EN EL CODIGO
 
typedef map<void *, int> pointer_map_t;
typedef pointer_map_t::iterator pointer_map_iterator;
//</void>
static map<void *, int> pointer_map;
 
void pointer_inc_ref( void *ptr )
{
	if( ptr != 0 ) {
		pointer_map_iterator it;
		it = pointer_map.find( ptr );
		if( it == pointer_map.end() ) {
			pointer_map.insert(pointer_map_t::value_type(ptr, 1));
		}
		else {
			pointer_map[ptr] ++;
		}
	}
}
 
bool pointer_dec_ref( void *ptr )
{
	if( ptr != 0 ) {
		pointer_map[ptr] --;
		if( pointer_map[ptr] == 0 ) {
			pointer_map.erase(ptr);
			return true;
		}
	}
	return false;
}
//</void>

En este caso, estamos haciendo una política muy agresiva, pero ya se controlan casi todos los casos (aún pueden existir problemas, para los que la política a utilizar sería que el puntero inteligente no libere la memoria [por lo que sería una perdida utilizarlo]). Además, existen problemas de dobles enlaces que podrían provocar que la memoria nunca se libere.

Seguro que hay mejores modos de hacerlo, pero este es, el que a partir de algunas cosillas que he leído por ahí he conseguido implementar. Si hay alguna pregunta (y habéis sido capaces de leer hasta aquí) no os cortéis en hacerla.

Punteros inteligentes en C++ (smart pointers) 2

En el artículo anterior veíamos como crear una clase simple de puntero inteligente, pero no se mostró ningún ejemplo de su uso. Por este motivo, a continuación pasamos a poner un ejemplo y a ver las deficiencias de dicha clase:

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
#include <iostream>
using std::cout;
using std::endl;
 
// Aqui iría nuestra clase de puntero simple
// template...
 
// Clase de ejemplo para mostrar el uso de los punteros inteligentes
class UnaClase {
   UnaClase() {
      cout < < "Soy UnaClase y me estan creando" << endl;
   }
   ~UnaClase() {
      cout << "Soy UnaClase y me están destruyendo" << endl;
   }
   void hacerAlgo() {
      cout << "Soy UnaClase y estoy haciendo algo útil" << endl;
   }
};
 
int main( void ) {
   PtrSimple<UnaClase> ptrUnaClase = new UnaClase();
 
   // Llamamos al método hacerAlgo de UnaClase a través del puntero inteligente.
   ptrUnaClase->hacerAlgo();
 
   // En este punto, C++ destruye la instancia a PtrSimple, eliminando automáticamente
   // la memoria ocupada por este.
}
 
// Para evitar fallos en wordpress:
// 
// </iostream>

Si hacemos la prueba, veremos como el destructor de UnaClase se llama automáticamente al término del programa. Con esto hemos conseguido nuestro primer objetivo, no tener que liberar nosotros mismos la memoria, pero ahora surgen otros problemas: ¿cómo utilizar el puntero en más partes de la aplicación que no sean el propio método?

Antes de continuar vamos a ver una cosita de C++ que nos ayudará mucho: el paso de parametros por referencia. Esto nos permite pasar a un método una referencia a un objeto y si lo modificamos estaremos modificando el objeto original. ¿Cómo se hace esto? pues muy fácil:

1
2
3
4
// Utilizamos nuestra clase UnaClase
void metodo( UnaClase& unaClase ) {
   unaClase.hacerAlgo();
}

Como vemos, al poner los argumentos del método, justo después del tipo se ha añadido un símbolo &. Éste es el que indica que es por referencia. Si no se pone nada sería por valor y si se pusiera un * sería una dirección. De este modo, ya podríamos pasar sin peligro a un método un puntero inteligente:

1
2
3
4
5
6
// Utilizamos nuestra clase UnaClase
void metodo( PtrSimple<unaclase>& unaClase ) {
   unaClase->hacerAlgo();
}
// Para evitar problemas
// </unaclase>

Ahora imaginemos el siguiente caso:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <vector>
using std::vector;
// Nuestras definiciones
 
// El codigo
typedef PtrSimple<unaclase> PtrUnaClase;
// Ojo que el worpress cambia algunas cosas (como mayúsculas a minúsculas :@)
vector<ptrunaclase> vectorPunteros;
 
// Utilizamos nuestra clase UnaClase
void metodo( PtrUnaClase& unaClase ) {
   vectorPunteros.add(unaClase);
}
 
// Para evitar problemas
// </ptrunaclase></unaclase></vector>

Estamos haciendo uso de STL para el vector. Este código no funcionará, ya que la clase vector hace uso del constructor de copia de PtrSimple, que no lo hemos definido, por lo que utilizaría el contructor de copia por defecto. Esto derivaría en que en un momento dado el puntero podría liberarse mientras en el vector seguiría existiendo un puntero al mismo, lo que supondrá un fallo de segmentación. Para solventar esto, hay que hacer uso de contadores de uso de punteros, que nos permitirán saber cuantas copias hay del mismo para liberarlo en caso necesario:

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
template<typename _t> class PtrSimple2 {
private:
   _t *_ptr;
   int *_count; // Copias del puntero
 
public:
   // Creamos el puntero a partir de una zona de memoria ya existente
   PtrSimple2( _t* ptr = NULL ) {
      _ptr = ptr;
      if( _ptr == NULL )
         _count = NULL;
      else {
         _count = new int;
         *_count = 0;
      }
   }
 
   // Constructor de copia: tomamos un puntero ya existente e incrementamos
   // el contador de usos.
   PtrSimple2( const PtrSimple2<_t>& ptr ) {
      _ptr = ptr._ptr;
      _count = ptr._count;
      if( _count != NULL )
         (*count) = (*count) + 1;
   }
 
   ~PtrSimple2() {
      if( _count != NULL ) {
         (*count) = (*count) - 1;
         // Si nadie apunta a esta zona de memoria,
         // se libera la memoria
         if( (*count) == 0 )
            delete _ptr;
      }
   }
 
   _t* operator->() const {
      return _ptr;
   }
 
   _t& operator*() const {
      return *_ptr;
   }
};
 
// Para evitar fallos en wordpress
// </_t></typename>

Con la clase anterior, ya no debería haber problemas con el tema de STL ni de que hayan copias por ahí… ¿o no? Siguen existiendo casos conflictivos, por ejemplo, tener que pasar sólo la dirección (no el puntero) y luego desde otra parte del código tomar dicho puntero a una clase. Con este caso, perderiamos los contadores de uso que teníamos, por lo que perdemos el control.

A parte de lo anterior, también es constatable que nos faltan funcionalidades: y si quiero comparar dos puntero, y si quiero asignarle una dirección nueva al puntero cuando este ya tenía otra, etc. Esta y otras dudas, para el siguiente artículo.

Punteros inteligentes en C++ (smart pointers)

Para mucha gente, la lacra de programar en C/C++ frente a otros lenguajes es el tener que liberar memoria y controlar donde se libera ésta. Es cierto que el control que dan estos lenguajes al programador es mucho y permite hacer cosas magnificas, pero hay que reconocerlo: somos vagos. En este sentido, se puede hacer en C++ una cosa denominada punteros inteligentes que pueden liberar memoria cuando esta ya no sea necesaria.

Los punteros inteligentes son un tipo de clase que haciendo uso de plantillas son capaces de comportarse como punteros y, segun su implementación, liberar la memoria cuando haga falta. Por contra, esto supone un decremento del rendimiento de la aplicación que habrá que considerar antes de utilizarlos.

Hay una gran cantidad de implementaciones en internet, pero aquí sólo me voy a centrar en la idea básica de todas ellas. Para empezar podemos considerar tres tipos de políticas de manejo de punteros:

  • Agresivas: que consisten en mantener un contador de copias por cada puntero en el sistema (es el mecanismo más lento).
  • Simple: sólo se considera un puntero, no puede asignarse a otros, por lo que no se necesita ningún tipo de contador (es el mecanismo más rápido).
  • Intermedio: cualquier política de uso que se encuentre entre simple y agresiva (su rendimiento será dependiente del tipo de política).

Como ejemplo de puntero que sigue una política simple podemos considerar el siguiente:

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
/**
* PtrSimple permite el manejo de la memoria de forma sencilla dentro de un cuerpo de sentencias.
* Los punteros manejados por el no deben ser liberados por el programador ni deberían utilizarse
* de otro modo que no sea a través de esta clase.
*/
template<typename _T> class PtrSimple {
private:
   _T *_ptr;
 
public:
   PtrSimple( _T* ptr = NULL ) {
      _ptr = ptr;
   }
   ~PtrSimple() {
      if( _ptr != NULL )
         delete _ptr;
   }
 
   T* operator->() {
      return _ptr;
   }
 
   T& operator*() {
      return *_ptr;
   }
};
// Esto es para que no me falle en wordpress :P : </typename>

Sin duda este es el tipo de puntero más sencillo que se puede hacer, ya que sólo controla un único puntero, no pudiendo ser asignado a otra clase, lo que limita mucho sus posibilidades a su uso en un único método. A partir de este se puede empezar a trabajar para añadirle funcionalidades (cosa que dejaré para otra entrada).

De momento lo dejo aquí para no alargar más la cosa. A ver si en un par de días saco otro artículo para seguir con el tema. Ahora a esperar que ferdy me crucifique :P

Subversion con Eclipse

Pues hace poco me ha tocado utilizar para unas prácticas Subversion junto con Eclipse. De partida, Eclipse no lleva soporte para Subversion, por lo que toca instalar un plug-in a parte llamado Subclipse.

El invento funciona muy bien (bueno java es lento y se nota en toda la interfaz). La única pega que tengo son los problemas que da porque cada uno de los que componemos el grupo de prácticas tenemos una versión de java diferente lo que hace que siempre se actualice el fichero de configuración del proyecto :@

Wiimote y Bluetooth

Como comenta Queltosh, estamos trabajando en programar desde linux el mando de la Nintendo Wii. De momento tengo varias cositas hechas, pero de poca importancia, principalmente la detección y conexión de los mandos que halla. La siguiente porción de código es la que se encarga de la detección:

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
static int search_wiimotes()
{
    inquiry_info *ii = NULL;
    int max_rsp, num_rsp;
    int dev_id, sock, len, flags;
    int i;
    char addr[19] = { 0 };
    char name[248] = { 0 };
 
    dev_id = hci_get_route(NULL);
    sock = hci_open_dev( dev_id );
    if (dev_id < 0 || sock < 0) {
        perror("opening socket");
        exit(1);
    }
 
    len  = 4;
    max_rsp = 255;
    flags = IREQ_CACHE_FLUSH;
    ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
 
    num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
    if( num_rsp < 0 ) perror("hci_inquiry");
 
    for (i = 0; i < num_rsp; i++) {
        ba2str(&(ii+i)->bdaddr, addr);
        memset(name, 0, sizeof(name));
        if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name), name, 0) < 0)
	        strcpy(name, "[unknown]");
        if( !strcmp( name, "Nintendo RVL-CNT-01" ) ) {
        	strcpy( _wii_handlers[_wii_counter].addr, addr );
        	_wii_counter ++;
        }
    }
 
    free( ii );
    close( sock );
 
    return 0;
}

Con el código anterior se detectan hasta un máximo de 255 dispositivos Bluetooth y luego seleccionamos los que sean Wiimotes.

Una vez hemos detectado todos los Wiimotes, procedemos a conectarnos a ellos. Pues en este punto surje un problema: algunas implementaciones de la biblioteca Bluez (o de la pila de Bluetooth, no estoy seguro), tienen un fallo y no dejan realizar la conexión, diciendo que la clave no es correcta. Parece ser que el fallo ya está solucionado en versiones posteriores, pero en la que trae Ubutu Edgy (la que uso para las pruebas) no :'(. Así que a esperar toca.

Para los que quieran el código que hace la conexión con un Wiimote:

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
/**
 * Crea una conexión con un wiimote.
 * @param addr Dirección del wiimote
 */
static int wii_internal_connect( char addr[19] )
{
	struct sockaddr_l2 loc_addr;
	int fd_in, fd_out;
 
	fprintf( stderr, "Conectando a: %s\n", addr );
 
	fd_in = socket( AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP );
	if( fd_in < 0 ) return -1;
 
	str2ba( addr, &loc_addr.l2_bdaddr );
	loc_addr.l2_family = AF_BLUETOOTH;
	loc_addr.l2_psm = htobs( 0x13 );
 
	if( connect( fd_in, (struct sockaddr *)&loc_addr, sizeof(loc_addr) ) < 0 ) {
		close( fd_in );
		return -1;
	}
 
	fd_out = socket( AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP );
	if( fd_out < 0 ) {
		close( fd_in );
		return -1;
	}
 
	str2ba( addr, &loc_addr.l2_bdaddr );
	loc_addr.l2_family = AF_BLUETOOTH;
	loc_addr.l2_psm = htobs( 0x11 );
 
	if( connect( fd_out, (struct sockaddr *)&loc_addr, sizeof(loc_addr) ) < 0 ) {
		close( fd_out );
		close( fd_in );
		return -1;
	}
 
 
	_wii_handlers[_wii_counter].in  = fd_in;
	_wii_handlers[_wii_counter].out = fd_out;
	_wii_counter ++;
 
	return 0;
}

Sobre las conversiones «sucias» de C en 64 bits

Esta entrada viene por un comentario realizado por ferdy en un comentario anterior.

Para comprobar si lo que dije funcionaba, he hecho el siguiente programa:

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
#include <stdio .h>
#include <stdlib .h>
 
#define SIZEOF(x) printf( "sizeof( %s ) = %d Bytes (%d bits)\n", #x, sizeof(x), sizeof(x)*8 )
int main( void )
{
        void *ptr;
        int valor;
 
        /* Compruebo que se pueda pasar de entero sin signo a puntero.
        * A grandes rasgos consiste en hacer creer al compilador que el
        * entero es una dirección de memoria. */
 
        valor = 5;
        printf( "Valor inicial antes de la transformacion: %d\n", valor );
        ptr = (void *)valor;
        valor = (int)ptr;
        printf( "Valor tras la transformacion: %d\n", valor );
 
        /* Compruebo que se pueda pasar de entero con signo a puntero.
        * A grandes rasgos consiste en hacer creer al compilador que el
        * entero es una dirección de memoria. */
 
        valor = -5;
        printf( "Valor inicial antes de la transformacion: %d\n", valor );
        ptr = (void *)valor;
        valor = (int)ptr;
        printf( "Valor tras la transformacion: %d\n", valor );
 
        /* Compruebo los tamaños de los distintos tipo de datos. El tipo 'void *'
        * es un puntero. Todos los puntero tienen el mismo tamaño en memoria,
        * por lo que no pruebo todas las combinaciones. */
 
        SIZEOF(void *);
        SIZEOF(char );
        SIZEOF(short int);
        SIZEOF(int );
        SIZEOF(long int);
        SIZEOF(float);
        SIZEOF(double);
        SIZEOF(long double);
        return 0;
}
</stdlib></stdio>

No aconsejo a nadie utilizar este tipo de conversiones de tipos, ya que pueden dar problemas. Además, hacen que el código sea menos legible.

El resultado del código anterior sobre un procesador AMD Turion ML-28 con Debian Sid pure 64 es el siguiente:


Valor inicial antes de la transformacion: 5
Valor tras la transformacion: 5
Valor inicial antes de la transformacion: -5
Valor tras la transformacion: -5
sizeof( void * ) = 8 Bytes (64 bits)
sizeof( char ) = 1 Bytes (8 bits)
sizeof( short int ) = 2 Bytes (16 bits)
sizeof( int ) = 4 Bytes (32 bits)
sizeof( long int ) = 8 Bytes (64 bits)
sizeof( float ) = 4 Bytes (32 bits)
sizeof( double ) = 8 Bytes (64 bits)
sizeof( long double ) = 16 Bytes (128 bits)

De este modo podemos ver que se podría, en teoría, convertir cualquier tipo de dato en puntero y almacenarlo en este (a excepción del tipo ‘long double’).

Como recomendación, es preferible realizar asignación dinámica de memoria para almacenar el entero (que es, supongo, la propuesta de ferdy) y luego transformar el puntero a entero a puntero genérico.

Con esto creo que ya está todo, pero si creéis que falta algo, decidlo.

Punteros a función en C

Bueno, unas de las cosas que me quedan en el tintero es poner ejemplos de código. Con esta entrada espero poner fin a esto hablando de los punteros a función en C.

Muchos de los que hayan programado en C sabrán lo infernal que puede resultar el uso de punteros. Hoy, para rizar el rizo, voy a hablaros de los punteros a función.

Como sucede con los punteros normales (a variables), un puntero a función no es más que un apuntador que nos dice donde se encuentra una función. La pregunta ahora es… ¿Y para qué nos puede servir esto? Pues la verdad es que para muchas cosas, por ejemplo, cuando se dispone de un algoritmo de ordenación en C y queremos que este nos sirva para cualquier cosa, lo interesante es tener un mecanismo que nos permita comparar dos elementos. Podemos hacer el algortimo de modo que sólo soporte un tipo de datos o podemos hacer que soporte cualquier tipo. Esto lo hacemos con punteros a funciones. Vamos a ver un 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
/**
* Ordena los elementos de una lista de menor a mayor.
* La lista es genérica y almacena sus elementos como punteros a cualquier cosa (void *).
* Se utiliza una función de comparación a la que se le pasan dos elementos de la lista y nos
* informa si el primero es mayor que el segundo.
*/
void ordenar( lista_t lista, int (*cmp)(void *elm1, void *elm2) ) {
   void *elem1, *elem2, *tmp;
   int numero_elementos = lista_numero_elementos( lista );
   int i, c;
 
   for( i = 0; i < numero_elementos; i ++ )
   {
      for( c = 0; c < numero_elementos-i-1; c ++ )
      {
         elem1 = lista_obtener_elemento( lista, c );
         elem2 = lista_obtener_elemento( lista, c+1 );
         /* Si elem1 > elem2 Entonces los intercambiamos.
         * Para hacer esta comparación, utilizamos la función del usuario. */
         if( cmp( elem1, elem2 ) )
         {
            lista_asignar_elemento( lista, c, elem2 );
            lista_asignar_elemento( lista, c+1, elem1 );
         } /* if( cmp( elem1, elem2 ) ) */
      } /* for( c = 0; c < numero_elementos-i-1; c ++ ) */
   } /* for( i = 0; i < numero_elementos; i ++ ) */
}
 
/**
* Compara elm1 y elm2, teniendo en cuenta que estos deben ser enteros.
* Si elm1 es mayor que elm2, devuelve un valor distinto que 0. En cualquier
* otro caso, devolverá 0.
*/
int CompararEnteros( void *elm1, void *elm2 )
{
   int a, b;
   a = (int)elm1;
   b = (int)elm2;
   return a > b;
}
 
/* Código del usuario */
 
/* Para ordenar la lista de enteros: */
ordenar( lista, CompararEnteros );
 
 
/* Código del usuario */

Bueno, espero que no sea necesario que explique qué hacen las funciones de la lista. Como vemos, con este mecanismo podemos usar enteros, flotantes, estructuras o imágenes. En verdad ahora podríamos ordenar una lista de cualquier cosa. Esto se consigue precisamente por el uso de punteros a función. Los que hayan hecho porgramación orientada a objetos se darán cuenta que esto se podría haber hecho con herencia.

En la línea 7, tenemos int (*cmp)(void *elm1, void *elm2). Esto es precisamente la declaración de una variable cmp que será un puntero a una función que devolverá un entero y que acepta dos parámetros de tipo void *. Para utilizar el puntero, sólo hay que hacer como si fuera una función normal y corriente:

cmp( param1, param2 );

En general, la definición de todo puntero a función es de la forma:

tipo_de_retorno (*nombre_de_la_variable)( parametros );

Para asignar a un puntero a función la dirección de un puntero, no hay más que hacer una signación normal:

puntero_a_función = función;

La función sería sin los argumentos.

Esto es sólo un ejemplo de lo que se puede hacer. Los punteros a funciones también pueden emplearse para crear menús dinámicos en los que queremos que, cuando se les pulse, se llame a una función (esta se pasaría como puntero a función). En muchas prácticas de las asignaturas de sistemas operativos, al utilizar señales tipo UNIX, se utilizan punteros a funciones. Se utilizan cuando decimos: «pues cuando llegue la señal SIGXXXX, quiero que llames a esta función». Cuando se hace programación multihilo (o con threads), también se usan los punteros a función.

Bueno, esto ha sido una pequeña introducción. Si a alguien le interesa saber más, puede decirlo en los comentarios. Si se me ocurre poner algo más, ya lo haré. Espero que os haya servido de ayuda.

Mi primera Live CD

Bueno, ayer me puse con Jesús y conseguimos que gendist funcionase. El problema era de la Debian, que le faltaban dos paquetes: el del squashfs y el del unionfs. Se ve que gendist toma los módulos del sistema instalado o algo por el estilo.

Despues de pegarnos un rato, descubrimos eso y que soy como que idiota. Esto último más que nada porque no hacía más que utilizar una imagen iso antigua para probar si funcionaba. No volverá a pasar.

De momento, he quitado el XPde, no se porqué, daba fallo de segmentación. Así que nada, GNome y ya veremos cómo lo configuro.

Cuando tenga tiempo, prepararé un pequeño tutorial del estilo: ‘Haga su propia live en 15 min.’, de momento, a seguir jugando.