" Подводные камни" и " маленькие хитрости"
if (a=b) // вместо if (a==b)
while (a << 3) // вместо while (a < 3)
if (a && 0x10) // вместо if (a & 0x10)
Трудно обнаруживаемые ошибки возникают при неявных преобразованиях типов в операциях, особенно при сочетании знаковой и беззнаковой форм представления:
char c[80];
#define CODE 193
if (c[i] == CODE) // Эквивалентно (int)c[i] == 193
В данном примере идентификатором CODE обозначена целая константа, которая имеет смысл кода символа, на наличие которого затем проверяются элементы массива символов. Но дело в том, что такая операция будет давать значение "ложь" всегда. Дело в том, что тип char представляет символы как знаковые байты (целые минимальной длины), поэтому этому коду в данной форме представления соответствует отрицательное значение - 63 . Так как любая операция преобразует операнды char к int , то получится интересное сочетание "-63 == 193", имеющее значение "ложь" вместо планируемого "истина". В таких случаях, когда разрядности переменных меняются, лучше не смешивать знаковую и беззнаковую формы. В данном случае исправить ошибку можно несколькими способами
#define CODE -63 // Непонятно
#define CODE (char)193 // Приемлемо
#define CODE '\301' //
unsigned char c[80]; // Лучше всего для символов с кодами > 128
При выполнении операций с переменными различной разрядности нужно помнить, что последовательность преобразования разрядностей (типов) связана с последовательностью и приоритетами выполнения операций.
Поэтому само по себе наличие в выражении операнда большей разрядности еще не гарантирует правильности вычислений для больших значений. Это видно на примере, где используется переменная типа long для хранения произведения переменных типа int . Ошибка состоит в том, что операция умножения все равно будет производиться с целыми размерности int , что может привести к потере значащих цифр произведения:
int a,b; long c;
c = a * b; // Неправильно
c = (long)a * b; // Правильно
Операция присваивания, операция "запятая" и условная операция позволяют выполнять многие действия "на лету", не выходя за пределы синтaксиса выражения в условных выражениях оперaторов if, while , например:
while ((c=getchar()) !='*') {...c...}
Здесь в переменной c запоминается результат функции, вызванной во время проверки условия в операторе while, с целью дальнейшего его использования в теле оператора.
while (x0=x1, x0 > 0) {... x1 =f(x0) ...}
Присваивание выполняется во время проверки условия в операторе цикла.
for (...; d> 0 ? a>b : b>=a; ...) {...}
В зависимости от значения переменной d меняется условие продолжения цикла for .
При наличии в программе нескольких вариантов выбора по группе условий программа становится "сильно ветвистой", например:
if (a<b)
if (a<c)
if (b<c) {...} // a < b && a < c && b < c
else {...} // a < b && a < c && b >=c
else
if (b<c) {...} // a < b && a >=c && b < c
else {...} // a < b && a >=c && b >=c
else ...
Можно воспользоваться тем, что операция сравнения дает целый результат (1 или 0) и сформировать переменную, принимающую уникальное значение для каждой комбинации сравнений. Тогда программа примет хотя и менее понятный, но зато более регулярный вид:
int n; n = (a < b)*4 + (a < c)*2 + (b < c);
switch(n)
{
case 0:... break; // a >=b && a >=c && b >=c
case 7: ... break; // a < b && a < c && b < c
}