Оба являются синтаксически правильными C, и компилятор должен справиться с ним. Но компилятор может, в зависимости от конфигурации, выдать предупреждение или даже ошибку (например, -Werror в gcc), потому что один из них настолько подозрительный, что вы никогда не ожидали, что он будет преднамеренным. Когда вы используете что-то вроде if (x = 0) { ... }
(назначьте ноль x
и запустите блок, если нуль не равен нулю), вы почти всегда на самом деле означают if (x == 0) { ... }
(запустите блок, если x
равно нулю).
Теперь давайте почему if ((x = 0)) { ... }
не считается достаточно подозрительным, чтобы оправдать тот же тип предупреждения (именно этот код все еще с подозрением, потому что условие всегда принимает значение ноль, а тело не запуская) ...
Существует идиома, используемая некоторыми разработчиками C (я один из них), где вы помещаете назначение в круглые скобки и используете функцию, которая даже присваивает самому значению, и это назначенное значение.
Пример:
#include <stdio.h>
int main(int argc, char **argv)
{
int c;
while ((c = getchar()) != '\n')
printf("Character: '%c' (0x%02x)\n", c, c);
return 0;
}
Тест пример:
$ ./test
Hello!
Character: 'H' (0x48)
Character: 'e' (0x65)
Character: 'l' (0x6c)
Character: 'l' (0x6c)
Character: 'o' (0x6f)
Character: '!' (0x21)
Важной частью было условие (c = getchar()) != '\n'
, где сначала присвоить результат getchar()
к c
, а затем проверить его на определенное значение. В этом случае мы читаем символы один за другим со стандартного ввода до строки и (технически, пока не будем читать символ \n
). Главным преимуществом этого способа является то, что он позволяет вставить в тест getchar()
. В противном случае вам пришлось бы использовать нотацию запятой, бесконечный цикл с разрывом или поставить его как перед циклом, так и в конце цикла.
Иногда вы сравниваете ненулевые значения, например \n
, -1
и т. Д., Но иногда вы сравниваете с нолем или, при работе с указателями, до NULL
. Давайте найдем пример для NULL
, что довольно часто встречается при распределении памяти.
char *p;
if ((p = malloc(50)) == NULL) {
...handle error...
}
Конечно, вы могли бы написать:
char *p;
p = malloc(50);
if (p == NULL) {
...handle error...
}
Но в зависимости от вашего вкуса вы можете также использовать:
char *p;
if (!(p = malloc(50))) {
...handle error...
}
Или даже превратить его наоборот (что кстати идет против my предпочтение всегда относится к первому случаю ошибки):
char *p;
if ((p = malloc(50))) {
...do stuff...
} else {
...handle error...
}
В последнем случае условие (p = malloc(50))
, которое в точности эквивалентно p = malloc(50)
, но последний является весьма подозрительным из-за уже упомянутой общей ошибки выполнения задания вместо сравнения в C и производных языках. Обратите внимание, что речь идет не только о подозрительных компиляторах, но и о том, как люди читают код и смотрят на потенциальную ошибку.
Резервные круглые скобки - это просто средство рассказать читателям и компилятору о том, что это назначение явно преднамеренное и что это не случайность этой общей ошибки.
И я не вижу, почему кто-то захочет делать if if ((x = 0)) {...} ', поскольку это условие всегда будет ложным. (Или 'x = N' для любой константы' N'.) Если это в коде, вы столкнулись в дикой природе, возможно, кто-то пытался подавить предупреждение, не понимая, для чего это предупреждение. – fluffy
Кстати, простой трюк, помогающий обнаружить множество случаев, когда = использовался вместо ==, - это всегда ставить константу слева - «if (0 = x)» создаст can't-assign- постоянная ошибка. – keshlam
@keshlam Также известен как [условия Yoda] (https://en.wikipedia.org/wiki/Yoda_conditions). – Palec