Recentemente, escrevi um programa que classifica uma matriz. Para isso, eu precisava escrever uma função de comparação, que passarei para ela. Minha função de comparação deveria ter retornado 1 (se x> y), -1 (se x <y) ou 0 (se x = y). Escrevi uma função regular (Função 1) usando expressões condicionais, mas fui aconselhado a escrever de maneira diferente (Função 2). É melhor escrever assim? Uma condição booleana sempre retornará 1 para a verdade? (Quero dizer, se x = 0 ey = 0 sempre teremos (x == y) == 1?)
Função 1:
int Icmp(void* x, void* y)
{
int a = *(int*)x;
int b = *(int*)y;
if (a > b)
return 1;
else if (a < b)
return -1;
else
return 0;
}
Função 2:
int Icmp(void* x, void* y)
{
return (*(int*)x > * (int*)y) - (*(int*)x < *(int*)y);
}
Respostas:
A maneira preferida de escrever o código sem ramificação seria usar uma variável local para os operandos:
A expressão é um idioma comum nas funções de comparação e, se escrita usando variáveis em vez de desreferências de ponteiros no local, também é legível.
O código baseia-se no fato de que o resultado de uma comparação usando
>
,<
ou mesmo,==
é do tipoint
1 ou 0. Isso é exigido pelo padrão C - qualquer compilador que gere valores como 42 ou -1 é, por definição, não um compilador C .É fácil ver que máx. um de
a > b
oua < b
pode ser verdade em um dado momento, e o resultado é tanto1 - 0
,0 - 1
ou0 - 0
.Quanto ao motivo do código sem ramificação - embora os compiladores possam gerar exatamente o mesmo código para ambas as funções, geralmente não o fazem. Por exemplo, o GCC e o ICC mais recentes parecem gerar uma ramificação para a primeira função no x86-64, mas um código sem ramificação com execução condicional para a última. E para quem disser que as ramificações não importam, remeto para o controle de qualidade mais votado de todos os tempos no Stack Overflow .
fonte
if
declaração-não significa que nenhuma ramificação será gerada, nem que, se houver umaif
declaração -de que haverá uma ramificação. De fato, depende se o compilador pode otimizá-los.Eu diria que não.
Para desempenho; ou isso não importa (provavelmente para compiladores modernos), ou não deve ser uma função separada (e deve ser incorporada ao código usado para classificação), ou você não deve classificar de forma alguma (por exemplo, dados classificados na criação e não classificado após a criação).
Para facilitar a leitura (manutenção do código, chance de ver erros na versão original, risco de apresentar erros posteriormente), eu prefiro sua versão original; especialmente ao trabalhar em equipe, e especialmente quando outros membros da equipe estão mais familiarizados com outras 10 linguagens de programação, cada uma com regras muito diferentes para C.
Especificamente; Eu gosto disso (porque os lançamentos no código real tornam as coisas mais difíceis de ler):
..e eu reescreveria o resto para ficar assim:
..ou para ficar assim:
..porque
else
é desnecessário após areturn
; e porque "if sem chaves seguido de declaração em sua própria linha" cria o risco de alguém inserir acidentalmente uma nova linha sem perceber e quebrar tudo (por exemplo, consulte https://dwheeler.com/essays/apple-goto- fail.html ).fonte
Se você estiver usando a função de comparação com
qsort
, a função precisará apenas retornar valores + ve, -ve ou zero.Nesse caso, você pode apenas subtrair os números
fonte
(a > b) - (a < b)
, mas o problemaa - b
é que a subtração pode ser insuficiente. Portanto, essa é uma expressão ruim.Inteiros
Flutuadores
Cordas
fonte