dev define c fixed-width misra uint32-t

dev - #define c++



¿Cuándo debo usar las macros UINT32_C(), INT32_C(),… en C? (2)

Cambié a tipos de enteros de longitud fija en mis proyectos principalmente porque me ayudan a pensar en los tamaños de enteros más claramente cuando los uso. #include <inttypes.h> través de #include <inttypes.h> también incluye un montón de otras macros como las macros de impresión PRIu32 , PRIu64 , ...

Para asignar un valor constante a una variable de longitud fija, puedo usar macros como UINT32_C() e INT32_C() . Comencé a usarlos cuando asignaba un valor constante.

Esto lleva a un código similar a este:

uint64_t i; for (i = UINT64_C(0); i < UINT64_C(10); i++) { ... }

Ahora vi varios ejemplos que no se preocuparon por eso. Uno es el archivo de inclusión stdbool.h :

#define bool _Bool #define false 0 #define true 1

bool tiene un tamaño de 1 byte en mi máquina, por lo que no parece un int . Pero 0 y 1 deben ser enteros que el compilador debe convertir automáticamente en el tipo correcto. Si usara eso en mi ejemplo, el código sería mucho más fácil de leer:

uint64_t i; for (i = 0; i < 10; i++) { ... }

Entonces, ¿cuándo debo usar las macros de longitud fija fija como UINT32_C() y cuándo debo dejar ese trabajo al compilador (estoy usando GCC)? ¿Qué pasaría si escribiera el código en MISRA C?


Como regla general, debe usarlos cuando el tipo de los literales importa. Hay dos cosas a considerar: el tamaño y la firmeza.

Respecto al tamaño:

Un tipo int está garantizado por los valores estándar de C hasta 32767 . Como no puede obtener un literal entero con un tipo más pequeño que int , todos los valores menores a 32767 no deberían usar las macros. Si necesita valores más grandes, entonces el tipo del literal comienza a importar y es una buena idea usar esas macros.

Respecto a la firmeza:

Los literales enteros sin sufijo son generalmente de un tipo firmado. Esto es potencialmente peligroso, ya que puede causar todo tipo de errores sutiles durante la promoción de tipo implícita. Por ejemplo, (my_uint8_t + 1) << 31 causaría un error de comportamiento no definido en un sistema de 32 bits, mientras que (my_uint8_t + 1u) << 31 no lo haría.

Esta es la razón por la que MISRA tiene una regla que establece que todos los literales enteros deben tener un sufijo u / U si la intención es usar tipos sin signo. Entonces, en mi ejemplo anterior, puedes usar my_uint8_t + UINT32_C(1) pero también puedes usar 1u , que es quizás el más legible. O bien debería estar bien para MISRA.

En cuanto a por qué stdbool.h define verdadero / falso como 1/0, es porque el estándar lo dice explícitamente. Las condiciones booleanas en C todavía usan el tipo int , y no el tipo bool como en C ++, por razones de compatibilidad hacia atrás.

Sin embargo, se considera un buen estilo para tratar las condiciones booleanas como si C tuviera un verdadero tipo booleano. MISRA-C: 2012 tiene un conjunto completo de reglas con respecto a este concepto, llamado tipo esencialmente booleano . Esto puede dar una mejor seguridad de tipo durante el análisis estático y también prevenir varios errores.


Es para usar literales enteros pequeños en los que el contexto no hará que el compilador se convierta en el tamaño correcto.

He trabajado en una plataforma integrada donde int es de 16 bits y long es de 32 bits. Si intentara escribir código portátil para trabajar en plataformas con tipos int 16 bits o de 32 bits, y desea pasar un "entero sin signo" de 32 bits a una función variadic, necesitará la conversión:

#define BAUDRATE UINT32_C(38400) printf("Set baudrate to %" PRIu32 "/n", BAUDRATE);

En la plataforma de 16 bits, el modelo crea 38400UL y en la plataforma de 32 bits solo 38400U . Estos coincidirán con la macro PRIu32 de "lu" o "u" .

Creo que la mayoría de los compiladores generarían un código idéntico para (uint32_t) X como para UINT32_C(X) cuando X es un literal entero, pero tal vez no haya sido el caso con compiladores tempranos.