Contexto
Estamos portando código C que foi compilado originalmente usando um compilador C de 8 bits para o microcontrolador PIC. Um idioma comum que foi usado para impedir que variáveis globais não assinadas (por exemplo, contadores de erro) retornem ao zero é o seguinte:
if(~counter) counter++;
O operador bit a bit aqui inverte todos os bits e a instrução só é verdadeira se counter
for menor que o valor máximo. Importante, isso funciona independentemente do tamanho da variável.
Problema
Agora, estamos direcionando um processador ARM de 32 bits usando o GCC. Percebemos que o mesmo código produz resultados diferentes. Até onde sabemos, parece que a operação de complemento bit a bit retorna um valor com um tamanho diferente do que seria de esperar. Para reproduzir isso, compilamos no GCC:
uint8_t i = 0;
int sz;
sz = sizeof(i);
printf("Size of variable: %d\n", sz); // Size of variable: 1
sz = sizeof(~i);
printf("Size of result: %d\n", sz); // Size of result: 4
Na primeira linha de saída, obtemos o que esperávamos: i
é 1 byte. No entanto, o complemento bit a bit de i
na verdade é de quatro bytes, o que causa um problema, porque comparações com isso agora não fornecerão os resultados esperados. Por exemplo, se estiver fazendo (onde i
está devidamente inicializado uint8_t
):
if(~i) i++;
Veremos i
"envolver" de 0xFF para 0x00. Esse comportamento é diferente no GCC em comparação com quando ele costumava funcionar como pretendíamos no compilador anterior e no microcontrolador PIC de 8 bits.
Estamos cientes de que podemos resolver isso lançando da seguinte forma:
if((uint8_t)~i) i++;
Ou por
if(i < 0xFF) i++;
No entanto, em ambas as soluções alternativas, o tamanho da variável deve ser conhecido e é propenso a erros para o desenvolvedor de software. Esses tipos de verificação dos limites superiores ocorrem em toda a base de código. Existem vários tamanhos de variáveis (por exemplo., uint16_t
E unsigned char
etc.) e mudando estes em uma base de código outra forma de trabalho não é algo que nós estamos olhando para frente.
Questão
Nosso entendimento do problema está correto e existem opções disponíveis para resolver isso que não exigem uma nova visita a cada caso em que usamos esse idioma? Nossa suposição está correta, de que uma operação como complemento bit a bit deve retornar um resultado que é do mesmo tamanho que o operando? Parece que isso iria quebrar, dependendo da arquitetura do processador. Sinto que estou tomando pílulas malucas e que C deve ser um pouco mais portátil que isso. Novamente, nossa compreensão disso pode estar errada.
Na superfície, isso pode não parecer um grande problema, mas esse idioma que funcionava anteriormente é usado em centenas de locais e estamos ansiosos para entender isso antes de prosseguir com alterações caras.
Nota: Há uma pergunta duplicada aparentemente semelhante, mas não exata, aqui: A operação bit a bit no char fornece um resultado de 32 bits
Não vi o ponto crucial da questão discutido lá, ou seja, o tamanho do resultado de um complemento bit a bit sendo diferente do que é passado para o operador.