Este es el código,
#include<stdio.h> #include<stdbool.h> #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) struct my_struct { int a, b; // char c; }; int main() { bool cond = 1; BUILD_BUG_ON((sizeof(struct my_struct) % 8) != 0); BUILD_BUG_ON(cond); return 0; }
El primer uso de la macro BUILD_BUG_ON(condición) arroja un error de compilación si el tamaño de la estructura no es igual a 8, ya que la condición se evaluará como verdadera. pero el segundo uso de la macro no arroja un error de compilación incluso si estoy proporcionando una condición verdadera. No soy capaz de entender este comportamiento. ¿Alguien puede explicar?
La macro BUILD_BUG_ON
está diseñada para implementar una aserción en tiempo de compilación.
Dado un argumento que se puede evaluar en tiempo de compilación, provoca un error en tiempo de compilación si el argumento es distinto de cero (verdadero) y no hace nada si el argumento es distinto de cero (falso).
No funciona para un argumento que se evalúa en tiempo de ejecución.
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
El !!
son dos operadores lógicos "no"; tienen el efecto de normalizar un valor de 0
a 0
y cualquier valor distinto de cero a 1
.
Si la condición resultante es 1
(verdadera), entonces el valor de 1 - 2*!!(condition)
es -1
. Si la condición es 0
(falsa), el valor es 1
.
Una matriz no puede tener un tamaño negativo (o cero). Algunos compiladores pueden admitir matrices de longitud cero como una extensión; esta macro asegura que incluso dicho compilador diagnostique un error. Si el tamaño es una expresión constante, una matriz con un tamaño negativo es una infracción de restricción y requiere un diagnóstico en tiempo de compilación.
Si la expresión es falsa, entonces no hay error; la macro se expande a una expresión que no hace nada. Si la expresión es verdadera y es una expresión constante , la expansión de la macro intenta definir una matriz de tamaño negativo, lo que genera un error en tiempo de compilación.
Si la expresión no es constante, la macro no funciona. C (C99 y posterior) permite matrices de longitud variable (VLA). No se permiten VLA de longitud cero o negativa, pero la definición de dicho VLA no se puede detectar en general en el momento de la compilación. Es un comportamiento indefinido y, en este caso, es probable que no haga nada. (Solo para complicar las cosas, los VLA no están permitidos en el alcance del archivo).
La macro debería, idealmente, ir acompañada de documentación que explique cómo usarla. Esa documentación debe explicar que el argumento debe ser una expresión en tiempo de compilación.
En pocas palabras: solo debe usar esta macro con un argumento de expresión constante. (Para probar una expresión en tiempo de ejecución, puede usar assert()
.) Si usa una expresión no constante con un valor cero, el comportamiento no está definido; el resultado más probable es que la "afirmación" prevista no se active y no se detecte el error.
Cambio
BUILD_BUG_ON(cond);
para
BUILD_BUG_ON(1);
para obtener el comportamiento esperado.
Cuando el valor de cond
está disponible solo en tiempo de ejecución, se genera un código que calcula el sizeof
requerido (y se descarta su resultado) en compilaciones no optimizadas, o se ignora por completo la expresión completa en compilaciones optimizadas.
Creo que tu método es completamente antinatural.
Es un código incorrecto, incluso si funcionara.
Puede probar enfoques más naturales para manejar los errores.
afirmar
La afirmación de assert
prueba una condición en tiempo de ejecución y muestra un mensaje de error si la prueba falla.
#include <assert.h> int main(void) { assert((sizeof(struct my_struct) % 8) != 0); }
afirmación_estática
En los compiladores de quejas de C11, static_assert(condition, "Error message")
hace lo que desea para las expresiones constantes:
static_assert((sizeof(struct my_struct) % 8) != 0, "Wrong struct");
#si y #error
También se podrían usar las directivas del compilador #if
y #error
, pero no le aconsejo en este caso, ya que #if
no entiende sizeof
.
La sintaxis sería:
#if some_condition // Put an integer constant expression readable by an #if. # error This is wrong. #endif
Algunos de estos tres métodos tendrían que satisfacer sus necesidades.