Непреднамеренное использование = вместо ==

Кажется, что

if (x=y) { .... }

вместо

if (x==y) { ... } 

- корень многих зол.

Почему не все компиляторы отмечают это как ошибку вместо настраиваемого предупреждения?

Мне интересно узнать случаи, когда полезно использовать конструкцию if (x=y).

Ответ 1

В большинстве случаев компиляторы очень стараются оставаться обратно совместимыми.

Изменение их поведения в этом вопросе, чтобы выбросить ошибки, нарушит существующий законный код и даже начнет бросать предупреждения об этом, что вызовет проблемы с автоматическими системами, которые отслеживают код, автоматически компилируя его и проверяя наличие ошибок и предупреждений.

Это зло, которое мы почти застряли с атмом, но есть способы обойти и уменьшить опасность этого.

Пример:

   void *ptr = calloc(1, sizeof(array));
   if (NULL = ptr) {
       // some error
   }

Это приводит к ошибке компиляции.

Ответ 2

одна полезная конструкция есть, например:

char *pBuffer;
if (pBuffer = malloc(100))
{
    //continue to work here
}

Редактирование:
как уже упоминалось ранее, и несколько раз опущен вниз, я мог бы добавить, что это не особенно хороший стиль, но часто видели достаточно, чтобы сказать, что это полезно. Я также видел это с помощью new, но у меня больше боли в груди.

другой пример, менее спорный, может быть:

while (pointer = getNextElement(context))
{
    //go for it, use the pointer to the new segment of data
}

что означает, что функция getNextElement() возвращает NULL, когда нет следующего элемента, так что цикл будет завершен.

Ответ 3

Потому что это не незаконно (в любом случае C или С++) и иногда полезно...

if ( (x = read(blah)) > 0)
{
    // now you know how many bits/bytes/whatever were read
    // and can use that info. Esp. if you know, say 30 bytes
    // are coming but only got 10
}

Большинство компиляторов поднимают настоящую вонь, если вы все равно не помещаете скобки вокруг назначения, что мне нравится.

Ответ 4

Простой ответ: операция присваивания, такая как x = y, имеет значение, которое совпадает с новым назначенным значением в x. Вы можете использовать это непосредственно в сравнении, поэтому вместо

x = y; if (x) ...

вы можете написать

if (x = y) ...

Это меньше кода для написания (и чтения), что иногда бывает хорошо, но в настоящее время большинство людей согласны с тем, что он должен быть написан каким-то другим способом для повышения удобочитаемости. Например, например:

if ((x = y) != 0) ...

Вот реалистичный пример. Предположим, вы хотите выделить некоторую память с помощью malloc и посмотреть, работает ли она. Его можно написать шаг за шагом следующим образом:

p = malloc(4711); if (p != NULL) printf("Ok!");

Сравнение с NULL является избыточным, поэтому вы можете переписать его следующим образом:

p = malloc(4711); if (p) printf("Ok!");

Но поскольку операция присваивания имеет значение, которое можно использовать, вы можете поместить все присваивание в условие if:

if (p = malloc(4711)) printf("Ok!");

Это делает то же самое, но более кратким.

Ответ 5

Стандартная идиома C для итерации:

list_elem* curr;
while ( (curr = next_item(list)) != null ) {
  /* ... */
}

Ответ 6

О допустимых действиях if (i = 0)

Проблема в том, что вы делаете проблему с ног на голову. Обозначение "if" не означает сравнение двух значений, как на некоторых других языках.

Инструкция C/С++ "if" ожидает выражения, которое будет оцениваться как логическое, так и значение null/non-null. Это выражение может включать в себя два сравнения значений и/или может быть намного сложнее.

Например, вы можете:

if(i >> 3)
{
   std::cout << "i is less than 8" << std::endl
}

Что доказывает, что в C/С++ выражение if не ограничено == и =. Все будет сделано, если оно может быть оценено как истинное или ложное (С++), или ноль ненулевой (C/С++).

Другое допустимое использование С++:

if(MyObject * pObject = dynamic_cast<MyInterface *>(pInterface))
{
   pObject->doSomething() ;
}

И это простое использование выражения if (обратите внимание, что это также можно использовать в строке объявления цикла for). Более сложные виды использования существуют.

О расширенном использовании if (i = 0) в С++ [Цитата из себя]

После обнаружения дубликата этого вопроса в В этом случае, если (a = b) является хорошей идеей?, я решил заполнить этот ответ дополнительным бонусом, то есть переменной инъекцией в область видимости, которая возможна в С++, потому что if будет оценивать ее выражение, включая объявление переменной, вместо того, чтобы ограничивать себя сравнением двух операнды, как это делается на других языках:

Итак, цитируя от себя:

Другим вариантом было бы использовать то, что называется С++ Variable Injection. В Java это ключевое слово cool:

synchronized(p)
{
   // Now, the Java code is synchronized using p as a mutex
}

В С++ вы тоже можете это сделать. У меня нет точного кода (ни точной статьи DDJ, где я его обнаружил), но этого простого определения должно быть достаточно для демонстрационных целей:

#define synchronized(lock) \
   if (auto_lock lock_##__LINE__(lock))

synchronized(p)
{
   // Now, the C++ code is synchronized using p as a mutex
}

Это то же самое, смешивая инъекцию с помощью if и для объявления, вы можете объявить примитивный макрос foreach (если вы хотите использовать foreach для промышленной прочности, используйте boost).

См. следующие статьи для менее наивной, более полной и более надежной реализации:

Сколько ошибок такого рода действительно происходит?

Редко. На самом деле, я еще не помню один, и я профессиональный с 8 лет. Я предполагаю, что это произошло, но потом, через 8 лет, я произвел значительное количество ошибок. Это просто, что таких ошибок не было достаточно, чтобы я помнил их в расстройстве.

В C у вас будет больше ошибок из-за переполнения буфера, например:

void doSomething(char * p)
{
   strcpy(p, "Hello World, how are you \?\n") ;
}

void doSomethingElse()
{
   char buffer[16] ;
   doSomething(buffer) ;
}

На самом деле Microsoft так сильно сгорела, что добавила предупреждение в Visual С++ 2008, осуждающее strcpy!!!

Как избежать большинства ошибок?

Самая первая "защита" от этой ошибки заключается в том, чтобы "развернуть" выражение: поскольку вы не можете назначить значение константе, это:

if(0 = p) // ERROR : It should have been if(0 == p). WON'T COMPILE !

Не компилируется.

Но я нахожу это довольно плохим решением, потому что он пытается скрыться за стилем, что должно быть общей практикой программирования, то есть: Любая переменная, которая не должна изменяться, должна быть постоянной.

Например, вместо:

void doSomething(char * p)
{
   if(p == NULL) // POSSIBLE TYPO ERROR
      return ;

   size_t length = strlen(p) ;

   if(length == 0) // POSSIBLE TYPO ERROR
      printf("\"%s\" length is %i\n", p, length) ;
   else
      printf("the string is empty\n") ;
}

Попытка "const" как можно больше переменных приведет к тому, что вы избежите большинства ошибок типографии, в том числе не внутри выражений "if" :

void doSomething(const char * const p) // CONST ADDED HERE
{
   if(p == NULL) // NO TYPO POSSIBLE
      return ;

   const size_t length = strlen(p) ; // CONST ADDED HERE

   if(length == 0) // NO TYPO POSSIBLE
      printf("\"%s\" length is %i\n", p, length) ;
   else
      printf("the string is empty\n") ;
}

Конечно, это не всегда возможно (поскольку некоторые переменные необходимо изменить), но я обнаружил, что большинство переменных, которые я использую, являются константами (я продолжаю их инициализацию один раз, а затем, только читая их).

Заключение

Обычно я вижу код, используя нотацию if (0 == p), но без const-notation.

Для меня это похоже на наличие корзины для вторсырья, а другое - для неперерабатываемого, а затем - конца, сбрасывает их вместе в одном контейнере.

Итак, не попугай, привыкший стиль, надеясь, что это сделает ваш код намного лучше. Это не так. Используйте как можно больше языковые конструкции, а это значит, что в этом случае используйте как if (0 == p) нотацию, доступную, так и максимально возможное использование ключевого слова const.

Ответ 7

Многие компиляторы обнаружат это и предупреждают вас, но только если уровень предупреждения установлен достаточно высоким.

Например:

 ~> gcc -c -Wall foo.c
foo.c: In function ‘foo’:
foo.c:5: warning: suggest parentheses around assignment used as truth value

Ответ 8

Идиома "if (0 = x)" находится рядом с бесполезной, поскольку она не помогает, когда обе стороны являются переменными ( "if (x = y)" ) и большинство (все?) того времени, когда вы должны быть используя постоянные переменные, а не магические числа.

Две другие причины, по которым я никогда не использую эту идиому, ИМХО делает код менее читаемым, и, честно говоря, я считаю, что сингл "=" является корнем очень маленького зла. Если вы тщательно проверите свой код (что мы все делаем, очевидно), такая синтаксическая ошибка возникает очень быстро.

Ответ 9

Это зависит от языка. Java помещает его как ошибку, так как в скобках if могут использоваться только булевы выражения (и если две переменные не являются логическими, в этом случае назначение также является логическим).

В C это довольно распространенная идиома для тестирования указателей, возвращаемых malloc, или если после вилки мы находимся в родительском или дочернем процессе:

if ( x = (X*) malloc( sizeof(X) ) {
   // malloc worked, pointer != 0

if ( pid = fork() ) {
   // parent process as pid != 0

Компиляторы C/С++ будут предупреждать с достаточно высоким уровнем предупреждения, если вы его попросите, но это не может считаться ошибкой, как это позволяет язык. Если, опять же, вы не попросите компилятор обработать предупреждения как ошибки.

При сравнении с константами некоторые авторы предлагают использовать переменную test constant ==, чтобы компилятор обнаружил, что пользователь забыл второй знак равенства.

if ( 0 == variable ) {
   // the compiler will complaint if you mistakenly 
   // write =, as you cannot assign to a constant

В любом случае, вы должны попытаться скомпилировать с максимально возможными настройками предупреждения.

Ответ 10

Это действительно такая распространенная ошибка? Я узнал об этом, когда узнал С сам, и как учитель я иногда предупреждал своих учеников и говорил им, что это распространенная ошибка, но я редко видел ее в реальном коде, даже от новичков. Разумеется, не чаще, чем другие ошибки оператора, такие как, например, запись "& &" вместо "||".

Поэтому причина, по которой компиляторы не отмечают ее как ошибку (за исключением того, что она является вполне допустимым кодом), возможно, это не корень очень многих зол.

Ответ 11

Вы можете использовать Условные обозначения Йоды:

alt text http://blogs.msdn.com/blogfiles/cdndevs/WindowsLiveWriter/NewProgrammingJargon_A38D/yoda%20conditional_219eb62d-b558-4e0f-ac81-38e9b0fc80dc.jpg

Тем не менее, многие считают их уродливыми и непроницаемыми, потому что они нарушают "глобальную область действия для локального правила области", считаются полезными для человеческого парсера.

Ответ 12

Я думаю, что разработчики языка C и С++ заметили, что это запрещено использовать, потому что

  • Компиляторы могут предупреждать об этом, если они хотят в любом случае
  • Отказ от использования этого языка приведет к добавлению особых случаев к языку и удалит возможную функцию.

Сложности в ее разрешении не существует. С++ просто говорит, что требуется выражение, неявно конвертируемое в bool. В C есть полезные случаи, детализированные другими ответами. В С++ они идут еще на один шаг и дополнительно допускают это:

if(type * t = get_pointer()) {
    // ....
}

Что фактически ограничивает область t только для if и его тел.

Ответ 13

Я лично считаю это наиболее полезным примером.

Скажите, что у вас есть функция read(), которая возвращает количество прочитанных байтов, и вам нужно использовать это в цикле. Это гораздо проще использовать

while((count = read(foo)) > 0) {
    //Do stuff
}

чем попытаться получить назначение из головы цикла, что приведет к таким вещам, как

while(1) {
    count = read(foo);
    if(!(count > 0))
        break;
    //...
}

или

count = read(foo);
while(count > 0) {
    //...
    count = read(foo);
}

Первая конструкция кажется неудобной, а второй повторяет код неприятным образом.

Если, конечно, я пропустил какую-то блестящую идиому для этого...

Ответ 14

Назначение как условное является законным C и С++, и любой компилятор, который этого не позволяет, не является реальным компилятором C или С++. Я надеюсь, что любой современный язык, не предназначенный для явной совместимости с C (как С++ был), будет считать его ошибкой.

Есть случаи, когда это позволяет сжатые выражения, такие как idiomatic while (*dest++ = *src++);, копировать строку на C, но в целом это не очень полезно, и я считаю ошибку ошибкой в ​​дизайне языка. По моему опыту, легко сделать эту ошибку и трудно обнаружить, когда компилятор не выдал предупреждение.

Ответ 15

 if ((k==1) || (k==2)) is a conditional
 if ((k=1)  || (k=2) ) is BOTH a conditional AND an assignment statement
  • Здесь объяснение *

Как и большинство языков, C работает от самого внутреннего к самому внешнему в порядке приоритета оператора.

Сначала он пытается установить k в 1 и преуспеть.

Result: k = 1 and Boolean = 'true'

Далее: он устанавливает k в 2 и преуспевает.

Result: k = 2 and Boolean = 'true'

Далее: он оценивает (true || true)

Result: k still = 2, and Boolean = true

Наконец, он разрешает условное: If (true)

  Result: k = 2 and the program takes the first branch.

За почти 30 лет программирования я не видел действительной причины для использования этой конструкции, хотя, если она существует, вероятно, это связано с необходимостью преднамеренного обфускации вашего кода.

Когда у одного из наших новых людей возникла проблема, это одна из вещей, которые я ищу, прямо, не придерживая терминатор на строке, и копируя отладку из одного места в другое и не меняя "% я до" % s", чтобы соответствовать новому полю, которое они сбрасывают.

Это довольно распространено в нашем магазине, потому что мы постоянно переключаемся между C и Oracle PL/SQL; if (k = 1) - правильный синтаксис в PL/SQL.

Ответ 16

Вы спросили, почему это было полезно, но продолжайте вопрошать примеры, которые люди предоставляют. Это полезно, потому что оно кратким.

Да, все примеры, которые его используют, могут быть переписаны - как более длинные фрагменты кода.

Ответ 17

Существует несколько тактик, чтобы помочь выявить это. Один уродлив, другой - макрос. Это действительно зависит от того, как вы читаете свой разговорный язык (слева направо, справа налево).

Например:

if ((fp = fopen("foo.txt", "r") == NULL))

Vs:

if (NULL == (fp = fopen(...)))

Иногда может быть проще читать/писать (сначала), для чего вы тестируете, что упрощает определение назначения и теста. Затем принесите большинство людей comp.lang.c, которые ненавидят этот стиль со страстью.

Итак, мы приводим assert():

#include <assert.h>

...
fp = fopen("foo.txt", "r");
assert(fp != NULL);
...

когда ваш посредине или конец свернутого набора условных выражений, assert() - ваш друг. В этом случае, если FP == NULL, то прерывается(), и строка/файл оскорбительного кода передается.

Итак, если вы oops:

if (i = foo)

insted

if (i == foo)

за которым следует

assert (i > foo + 1)

... вы быстро обнаружите такие ошибки.

Надеюсь, что это поможет:)

Короче говоря, реверсивные аргументы иногда помогают при отладке. assert() является вашим долгожителем и может быть отключен во флагах компилятора в выпусках выпуска.

Ответ 18

Это очень часто встречается с конструкциями цикла "низкий уровень" в C/С++, например, с копиями:

void my_strcpy(char *dst, const char *src)
{
    while((*dst++ = *src++) != '\0') { // Note the use of extra parentheses, and the explicit compare.
        /* DO NOTHING */
    }
}

Конечно, для циклов очень часто назначаются задания:

int i;
for(i = 0; i < 42; ++i) {
    printf("%d\n", i);
}

Я считаю, что читать задания легче, когда они находятся вне операторов if:

char *newstring = malloc(strlen(src) * sizeof(char));
if(newstring == NULL) {
    fprintf(stderr, "Out of memory, d00d!  Bailing!\n");
    exit(2);
}

// Versus:

if((newstring = malloc(strlen(src) * sizeof(char))) == NULL) // ew...

Убедитесь, что назначение очевидно, thuogh (как и в первых двух примерах). Не скрывайте это.

Что касается случайного использования... это не так много. Общая защита заключается в том, чтобы поместить вашу переменную (lvalues) в правую сторону сравнения, но это не очень хорошо работает с такими вещами, как:

if(*src == *dst)

потому что оба oprands to == являются lvalues!

Что касается компиляторов... кто может обвинить их? Написание компиляторов сложно, и вы все равно должны писать идеальные программы для компилятора (помните GIGO?). Некоторые компиляторы (наиболее известные наверняка) предоставляют встроенную проверку lint -style, но это, безусловно, не требуется. Некоторые браузеры не проверяют каждый байт HTML и Javascript, который он выбрал, поэтому зачем компиляторам?

Ответ 19

Попробуйте просмотреть

if( life_is_good() )
    enjoy_yourself();

а

if( tmp = life_is_good() )
    enjoy_yourself();

Ответ 20

Размещение константы в левой части сравнения - это защитное программирование. Уверен, что вы никогда не сделаете глупую ошибку, забыв об этом "=", но кто знает про ДРУГОГО парня.

Ответ 21

Язык программирования D указывает на это как на ошибку. Чтобы избежать проблемы с желанием использовать значение позже, оно позволяет объявлениям вроде С++ разрешает с помощью циклов for.

if(int i = some_fn())
{
   another_fn(i);
}

Ответ 22

Отчасти это связано с личным стилем и привычками. Я агностик для чтения либо if (kConst == x), либо if (x == kConst). Я не использую константу слева, потому что исторически я не делаю эту ошибку, и я пишу код, как я бы сказал, или хотел бы его прочитать. Я рассматриваю это как личное решение как часть личной ответственности за самосознание, совершенствование инженера. Например, я начал анализировать типы ошибок, которые я создавал, и начал реорганизовывать мои привычки, чтобы не создавать их - подобно константе слева, просто с другими вещами.

Тем не менее, предупреждения компилятора, исторически, довольно дрянные, и хотя эта проблема хорошо известна годами, я не видел ее в производственном компиляторе до конца 80-х. Я также обнаружил, что работа над портативными портами помогла мне значительно упростить мой C, как разные компиляторы и разные вкусы (т.е. Предупреждения) и различные тонкие смысловые различия.

Ответ 23

В условном выражении существует много отличных применений оператора присваивания, и было бы огромной болью в заднице, чтобы постоянно видеть предупреждения о каждом. Что было бы неплохо, это была бы функция в вашей среде IDE, позволяющая выделить все места, где было назначено назначение, вместо проверки равенства - или - после того, как вы напишете что-то вроде этого:

if (x = y) {

то эта строка несколько раз мигает. Достаточно, чтобы вы знали, что сделали что-то не совсем стандартное, но не настолько, что это раздражает.

Ответ 24

Компилятор не будет отмечать его как ошибку, поскольку он действителен C/С++. Но то, что вы можете сделать (по крайней мере, с Visual С++), - это повысить уровень предупреждения, чтобы он помещал его как предупреждение, а затем сообщал компилятору относиться к предупреждениям как к ошибкам. В любом случае, это хорошая практика, чтобы разработчики не игнорировали предупреждения.

Если бы вы на самом деле означали = вместо ==, тогда вам нужно быть более явным. например.

if ((x = y) != 0)

Теоретически, вы должны это сделать:

if ((x = y))

чтобы отменить предупреждение, но это, похоже, не всегда работает.

Ответ 25

вы можете использовать

if (0=x)

что приведет к ошибке компиляции.

Ответ 26

Вот почему лучше писать:

0 == CurrentItem

Вместо:

CurrentItem == 0

чтобы компилятор предупреждал вас, если вы введете = вместо ==.

Ответ 27

На практике я этого не делаю, но хороший совет:

if (true == $x)

В случае, если вы оставите равным, присваивание переменной $x истинно, очевидно, вернет ошибку.

Ответ 28

У меня была эта опечатка только один раз за последние 15 лет. Я бы не сказал, что это на вершине моего списка вещей, которые нужно искать. Я также избегаю этой конструкции.

Обратите внимание, что некоторые компиляторы (тот, который я использую) выдает предупреждение об этом коде. Предупреждения могут рассматриваться как ошибки для любого компилятора, заслуживающего его соли. Их также можно игнорировать.

Ответ 29

Рассмотрите возможность дополнения вашего компилятора статическими инструментами анализа, которые обнаруживают такой "злой" код, например. lint.

Ответ 30

Как указывалось в других ответах, есть случаи, когда использование присваивания в рамках условия предлагает краткую, но читаемую часть кода, которая делает то, что вы хотите. Кроме того, многие современные компиляторы будут предупреждать вас, если они видят назначение, в котором они ожидают условия. (Если вы поклонник подхода с нулевыми предупреждениями к разработке, вы увидите их.)

Одна из привычек, которые я разработал, которая мешает мне укусить это (по крайней мере, на языках C-ish), состоит в том, что если одно из двух значений, которые я сравниваю, является константой (или иначе не легальной переменной lvalue) Я положил его на левую сторону компаратора: if (5 == x) { whatever(); } Тогда, если я случайно наберу if (5 = x), код не будет компилироваться.