¿Hay alguna forma de calcular el ancho de un tipo entero en tiempo de comstackción?

El tamaño de un tipo entero (o cualquier tipo) en unidades de char / bytes se calcula fácilmente como sizeof(type) . Una expresión común es multiplicar por CHAR_BIT para encontrar el número de bits ocupados por el tipo, pero en implementaciones con bits de relleno, no será igual al ancho en bits de valor. Peor aún, código como:

 x>>CHAR_BIT*sizeof(type)-1 

puede tener un comportamiento indefinido si CHAR_BIT*sizeof(type) es mayor que el ancho real del type .

Para simplificar, supongamos que nuestros tipos no están firmados. Entonces, el ancho del type es ceil(log2((type)-1) . ¿Hay alguna forma de calcular este valor como una expresión constante?

    Existe una macro de función que puede determinar los **** bits de valor **** de un tipo entero, pero solo si ya conoce el valor máximo de ese tipo. Si obtendrá o no una constante en tiempo de comstackción depende de su comstackdor, pero creo que en la mayoría de los casos la respuesta es sí.

    Gracias a Hallvard B. Furuseth por su macro de función similar a IMAX_BITS () que publicó en respuesta a una pregunta en comp.lang.c

     /* Number of bits in inttype_MAX, or in any (1<  

    IMAX_BITS (INT_MAX) calcula el número de bits en un int, y IMAX_BITS ((unsigned_type) -1) calcula el número de bits en unsigned_type. Hasta que alguien implemente enteros de 4 gigabytes, de todos modos 🙂

    Y acredite a Eric Sosman por esta versión alternativa que debería funcionar con menos de 2040 bits:
    (EDITAR 1/3/2011 11:30 PM EST: Resulta que esta versión también fue escrita por Hallvard B. Furuseth)

     /* Number of bits in inttype_MAX, or in any (1<  

    Recuerde que aunque el ancho de un tipo entero sin signo es igual a la cantidad de bits de valor, el ancho de un tipo entero con signo es uno mayor (§6.2.6.2 / 6). Esto es de especial importancia ya que en mi comentario original a su pregunta, había afirmado incorrectamente que la macro IMAX_BITS () calcula el ancho, cuando realmente calcula la cantidad de bits de valor. ¡Lo siento por eso!

    Entonces, por ejemplo, IMAX_BITS(INT64_MAX) creará una constante en tiempo de comstackción de 63. Sin embargo, en este ejemplo estamos tratando con un tipo firmado, por lo que debe agregar 1 a la cuenta para el bit de signo si desea el ancho real de un int64_t, que es por supuesto 64.

    En una discusión separada de comp.lang.c un usuario llamado blargg da un desglose de cómo funciona la macro:
    Re: utilizando pre-procesador para contar bits en tipos enteros ...

    Tenga en cuenta que la macro solo funciona con 2 ^ n-1 valores (es decir, todos los 1 en binario), como se esperaría con cualquier valor MAX. También tenga en cuenta que si bien es fácil obtener una constante en tiempo de comstackción para el valor máximo de un tipo de entero sin signo ( IMAX_BITS((unsigned type)-1) ), en el momento de escribir este documento, no sé cómo hacerlo lo mismo para un tipo entero con signo sin invocar el comportamiento definido de implementación. Si alguna vez me entero, responderé a mi propia pregunta relacionada, aquí:
    Pregunta C: off_t (y otros tipos de entero con signo) valores mínimos y máximos - Stack Overflow

    Compare las macros de con los valores máximos conocidos para anchos de enteros específicos:

     #include  #if UINT_MAX == 0xFFFF #define INT_WIDTH 16 #elif UINT_MAX == 0xFFFFFF #define INT_WIDTH 24 #elif ... #else #error "unsupported integer width" #endif 

    Primer acercamiento, si sabes qué tipo de estándar tienes (para que tu tipo no sea typedef ) ve con las macros {U}INT_MAX y {U}INT_MAX con los posibles tamaños.

    Si no tiene eso, para los tipos sin firmar esto es relativamente fácil conceptualmente. Para su tipo favorito T , simplemente haga (T)-1 y realice una macro de prueba de monstruo que verifique con todos los valores posibles con ?: . Dado que estos son expresiones de constante de tiempo de comstackción, cualquier comstackdor decente lo optimizará y lo dejará con el valor que le interesa.

    Esto no funcionaría en #if , etc., debido al tipo de #if , pero esto no se puede evitar de una manera simple.

    Para los tipos firmados, esto es más complicado. Para tipos al menos tan anchos como int puedes esperar hacer un truco para promocionar al tipo sin firmar correspondiente y obtener el ancho de ese tipo en ese momento. Pero para saber si su tipo firmado tiene solo un valor menor o no, no creo que haya una expresión genérica para saberlo.

    Editar: para ilustrar esto un poco, doy algunos extractos de lo que puedes hacer para que este enfoque (para tipos sin firmar) no genere expresiones demasiado grandes en P99. Tengo algo así como

     #ifndef P99_HIGH2 # if P99_UINTMAX_WIDTH == 64 # define P99_HIGH2(X) \ ((((X) & P00_B0) ? P00_S0 : 0u) \ | (((X) & P00_B1) ? P00_S1 : 0u) \ | (((X) & P00_B2) ? P00_S2 : 0u) \ | (((X) & P00_B3) ? P00_S3 : 0u) \ | (((X) & P00_B4) ? P00_S4 : 0u) \ | (((X) & P00_B5) ? P00_S5 : 0u)) # endif #endif #ifndef P99_HIGH2 # if P99_UINTMAX_WIDTH < = 128 # define P99_HIGH2(X) \ ((((X) & P00_B0) ? P00_S0 : 0u) \ | (((X) & P00_B1) ? P00_S1 : 0u) \ | (((X) & P00_B2) ? P00_S2 : 0u) \ | (((X) & P00_B3) ? P00_S3 : 0u) \ | (((X) & P00_B4) ? P00_S4 : 0u) \ | (((X) & P00_B5) ? P00_S5 : 0u) \ | (((X) & P00_B6) ? P00_S6 : 0u)) # endif #endif 

    donde las constantes mágicas se definen con una secuencia de #if al principio. Allí es importante no exponer las constantes demasiado grandes para los comstackdores que no pueden manejarlas.

     /* The preprocessor always computes with the precision of uintmax_t */ /* so for the preprocessor this is equivalent to UINTMAX_MAX */ #define P00_UNSIGNED_MAX ~0u #define P00_S0 0x01 #define P00_S1 0x02 #define P00_S2 0x04 #define P00_S3 0x08 #define P00_S4 0x10 #define P00_S5 0x20 #define P00_S6 0x40 /* This has to be such ugly #if/#else to ensure that the */ /* preprocessor never sees a constant that is too large. */ #ifndef P99_UINTMAX_MAX # if P00_UNSIGNED_MAX == 0xFFFFFFFFFFFFFFFF # define P99_UINTMAX_WIDTH 64 # define P99_UINTMAX_MAX 0xFFFFFFFFFFFFFFFFU # define P00_B0 0xAAAAAAAAAAAAAAAAU # define P00_B1 0xCCCCCCCCCCCCCCCCU # define P00_B2 0xF0F0F0F0F0F0F0F0U # define P00_B3 0xFF00FF00FF00FF00U # define P00_B4 0xFFFF0000FFFF0000U # define P00_B5 0xFFFFFFFF00000000U # define P00_B6 0x0U # endif /* P00_UNSIGNED_MAX */ #endif /* P99_UINTMAX_MAX */ #ifndef P99_UINTMAX_MAX # if P00_UNSIGNED_MAX == 0x1FFFFFFFFFFFFFFFF # define P99_UINTMAX_WIDTH 65 # define P99_UINTMAX_MAX 0x1FFFFFFFFFFFFFFFFU # define P00_B0 0xAAAAAAAAAAAAAAAAU # define P00_B1 0xCCCCCCCCCCCCCCCCU # define P00_B2 0xF0F0F0F0F0F0F0F0U # define P00_B3 0xFF00FF00FF00FF00U # define P00_B4 0xFFFF0000FFFF0000U # define P00_B5 0xFFFFFFFF00000000U # define P00_B6 0x10000000000000000U # endif /* P00_UNSIGNED_MAX */ #endif /* P99_UINTMAX_MAX */ . . . 

    Sí, dado que para todos los propósitos prácticos, el número de anchos posibles es limitado:

     #if ~0 == 0xFFFF # define INT_WIDTH 16 #elif ~0 == 0xFFFFFFFF # define INT_WIDTH 32 #else # define INT_WIDTH 64 #endif 

    Puede calcularlo en tiempo de ejecución con un bucle simple, bien definido y sin el peligro de UB:

     unsigned int u; int c; for (c=0, u=1; u; c++, u< <=1); total_bits = CHAR_BIT * sizeof(unsigned int); value_bits = c; padding_bits = total_bits - value_bits; 

    La forma más sencilla sería verificar las pruebas de su unidad (las tiene , ¿no?) Que value_bits es idéntico a su definición INT_WIDTH actual.

    Si realmente necesita calcularlo en tiempo de comstackción, iría con una de las cascadas # if- # elif dadas, probando UINT_MAX o su sistema objective.

    ¿Para qué lo necesitas? Tal vez YAGNI?

    Una observación general es que si confía en el ancho de un tipo de datos en sus cálculos, debe usar los tipos de datos de ancho explícitos definidos en por ejemplo, uint32_t .

    Tratar de contar los bytes en los tipos estándar es mendigar la pregunta de qué haría su código ‘portátil’ en caso de un desbordamiento.

    Usualmente, el tamaño de int es conocido para un comstackdor / plataforma dados. Si tiene macros que identifican el comstackdor / plataforma, puede usarlos para definir de manera INT_WIDTH .

    Puede echar un vistazo a y sus dependientes para ver ejemplos.