Recovecos oscuros de C, C99 y C++

Advertencia: Post para coders. Puede producir dolor de cabeza. También puede contener información horriblemente equivocada, en cuyo caso se ruegan correcciones en los comentarios.
Hoy me he pasado un buen rato preguntandome por qué no funcionaba esto (recemos para que el WordPress no me destroce demasiado el codigo :P) :

#include <cstdio>
int main() 
{
    int i; float f=1.0f;
    i = *reinterpret_cast<float*>(&f);
    printf("i = 0x%0x\n", i);
    return 0;
}

Compilando sin optimización funcionaba como se esperaba, y con optimización daba un resultado distinto. Al principio pensé “un error del gcc, seguro”, pero me he puesto a buscar y resulta que no es un bug, sino una feature. ISO C tiene reglas en contra del aliasing de punteros que dicen que acceder a una variable mediante un puntero de un tipo incompatible es ilegal, asi que al activar las optimizaciones asume que esas cosas no pasan y genera un resultado incorrecto. Una solucion típica es el clasico hack en el que se define una union de un int y un float, se escribe en un miembro y se lee en el otro, pero creo que el estandar dice que en este caso el resultado está definido por la implementación. Mala cosa. Tambien he leido por ahi que una buena opcion es usar memcpy, hacer una copia de lo que sea a otra posicion de memoria y te olvidas del aliasing.

Mas cositas. Programando en la uni el otro dia pensamos en lo bonito que seria disponer de tipos de tamaño fijo, como los uint8, int32, que todos hemos usado alguna vez. Pues bien, en C existen y en C++ no. Ahora alguien se puede preguntar “¿pero cómo? si C es un subconjunto de C++!”, pero esto no solo no es cierto, sino que desde la aparición de C99 hay incluso más diferencias (el estandar de C++ es del 98). En este caso, C99 especifica una cabecera stdint.h que define tipos con nombres como int8_t.

Por ultimo, una curiosidad del FAQ de C++: esta permitido hacer un cast de “Blah *” a “const Blah *”, pero no un cast de “Blah **” a “const Blah **”.

Y eso es todo por hoy. Si teneis alguna historia similar que contar son bienvenidos los comentarios.

3 Comments

  1. pplux said,

    October 24, 2006 @ 21:55 pm

    Ethoooo, ethooo me guuuutaaa.

    Genial, como siempre, esta son la clase de cosas que más me gusta leer de un tipo que sabe lo que tu. Pregunta, que no me ha quedado claro, aunque C++ no tiene tipos con tamaño fijo quiere decir que no hay forma de tenerlos? algun header por ahi multi-plataforma-multi-todo que lo permita?

  2. slack said,

    October 24, 2006 @ 23:32 pm

    Pues… por un lado tienes la opcion rustica y fiable que es usar limits.h, que te define cosas como UCHAR_MAX, USHRT_MAX, UINT_MAX y ULONG_MAX. Con eso y unos cuantos #ifdefs bien puestos puedes definirte los tipos.

    Por otro lado, que se que a ti te ponen esas cosas, te puedes hacer una paja mental con templates al estilo Alexandrescu y hacer algo como lo que hay en http://www.flipcode.com/cgi-bin/fcarticles.cgi?show=64171 aunque no se si merecera la pena.

    Por cierto, se me ha olvidado comentar en el post una pequeña perla que vi en las cabeceras de la SDL buscando info sobre esto mismo: una macro para verificar aserciones en tiempo de compilacion en C :D

    /* Make sure the types really have the right sizes */
    #define SDL_COMPILE_TIME_ASSERT(name, x) \
    typedef int SDL_dummy_ ## name[(x) * 2 – 1]

    SDL_COMPILE_TIME_ASSERT(uint8, sizeof(Uint8) == 1);

  3. pplux said,

    October 25, 2006 @ 17:26 pm

    Menuda joya de temlpate. Los asserts en tiempo de compilación también aparcen por el Alexandrescu, con un giro de tuerca más.

RSS feed for comments on this post

This blog is protected by Dave\'s Spam Karma 2: 17770 Spams eaten and counting...