Mais
O que é ":-!!" em código C?
Eu esbarrei neste estranho código macro em /usr/include/linux/kernel.h:
/* Force a compilation error if condition is true, but also produce a
result (of value 0 and type size_t), so the expression can be used
e.g. in a structure initializer (or where-ever else comma expressions
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
O que faz o :-!!
?
1623
3
Esta é, com efeito, **uma forma de verificar se a expressão e pode ser avaliada como 0, e se não, para falhar a construção***.
A macro é um pouco mal chamada; deveria ser algo mais como
BUILD_BUG_OR_ZERO
, em vez de...ON_ZERO
. (Tem havido discussões ocasionais sobre se este é um nome confuso.)Devias ler a expressão desta maneira:
(e)
: Expressão computativae
.!!(e)
: Logicamente negar duas vezes:0
see == 0
; caso contrário1
.-!!(e)
: Nega numericamente a expressão do passo 2:0
se fosse0
; caso contrário-1
.struct{int: -!!(0);} --> struct{int: 0;}
: Se era zero, então declaramos uma estrutura com um campo de bits inteiro anónimo que tem largura zero. Tudo está bem e procedemos normalmente.struct{int: -!!(1);} --> struct{int: -1;}
: Por outro lado, se não for não for zero, então será um número negativo. Declarar qualquer bitfield com negative width é um erro de compilação.Então vamos acabar com um bitfield que tem largura 0 em uma estrutura, o que é bom, ou um bitfield com largura negativa, que é um erro de compilação. Então pegamos
sizeof
nesse campo, então obtemos umsize_t
com a largura apropriada (que será zero no caso em quee
for zero).Algumas pessoas têm perguntado: **"Por que não usar apenas um "assert"?
resposta de keithmo aqui tem uma boa resposta:
Exatamente certo. Você não quer detectar problemas no seu kernel em tempo de execução que poderiam ter sido detectados mais cedo! É uma peça crítica do sistema operacional. Seja qual for a extensão dos problemas que possam ser detectados em tempo de compilação, tanto melhor.
O
:
é um bitfield. Quanto a!!
, isto é dupla negação lógica e assim retorna0
para falso ou1
para verdadeiro. E o-
é um sinal de menos, ou seja, negação aritmética.É tudo apenas um truque para levar o compilador a vomitar em entradas inválidas.
Considere
BUILD_BUG_ON_ZERO
. Quando-!!(e)
avalia para um valor negativo, isso produz um erro de compilação. Caso contrário o-!!(e)
avalia para 0, e um bitfield de largura 0 tem tamanho 0. E por isso a macro avalia para umsize_t
com valor 0.O nome é fraco no meu ponto de vista porque a construção de fato falha quando a entrada é não zero.
BUILD_BUG_ON_NULL
é muito semelhante, mas produz um ponteiro em vez de um
int`.Está criando um bitfield de tamanho
0
se a condição for falsa, mas um bitfield de tamanho-1
(-!!1
) se a condição for verdadeira/não-zero. No primeiro caso, não há erro e a estrutura é inicializada com um membro int. No segundo caso, há um erro de compilação (e não é criado um bitfield de tamanho-1
, é claro).