Je dirais que tout dépend de laquelle de l´expression " a" ou de l´expression " b" a le plus de chance d´être fausse.
Si a est fausse :
- if(a && ! b) a est faux, on n´évaluera même pas b
=> eval(a)
- if(!a || b) on va faire le négatif de a, vrai, et donc ne pas évaluer b ( soit une opération NON de plus)
=> eval(a), NON
Si a est vraie
- if(a && ! b) évaluera a, trouvera vrai, évaluera b, en fera le négatif avant de faire enfin le ET
=> eval(a), eval(b), NON, ET
- if(!a || b) évaluera a, trouvera vrai, en fera le négatif, faux, et seulement là décidera d´évaluer b et enfin le OU
=> eval(a), NON, eval(b), OU
Ma conclusion : Garde tes " !" ( NON) pour la fin
Note : Un compilateur moderne avec ses options d´optimisation au maximum cassera peut-être ton expression pour l´ordonner dans l´ordre le plus efficace.