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

Это кажется этим

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

вместо

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

корень многого зла.

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

Я интересуюсь обнаружением случаев где конструкция if (x=y) полезно.

12
задан 4 revs, 4 users 55% 30 December 2008 в 15:20
поделиться

27 ответов

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

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

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

Пример:

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

Это вызывает ошибку компиляции.

16
ответ дан 2 December 2019 в 02:49
поделиться

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

0 = = CurrentItem

Вместо:

CurrentItem == 0

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

0
ответ дан 2 December 2019 в 02:49
поделиться

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

if ( true == $x )

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

0
ответ дан 2 December 2019 в 02:49
поделиться

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

Если Вы на самом деле имели в виду = вместо == затем, необходимо быть более явными об этом. например.

if ((x = y) != 0)

Теоретически, Вы, как предполагается, можете сделать это:

if ((x = y))

для переопределения предупреждения но это, кажется, всегда не работает.

1
ответ дан 2 December 2019 в 02:49
поделиться

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

if(int i = some_fn())
{
   another_fn(i);
}
1
ответ дан 2 December 2019 в 02:49
поделиться

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

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

0
ответ дан 2 December 2019 в 02:49
поделиться

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

1
ответ дан 2 December 2019 в 02:49
поделиться

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

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

1
ответ дан 2 December 2019 в 02:49
поделиться

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

Например:

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

По сравнению с:

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

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

Так, мы вводим, утверждают ():

#include <assert.h>

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

то, когда Ваш в среде или конец замысловатого набора условных выражений, утверждает () является Вашим другом. В этом случае, если FP == ПУСТОЙ УКАЗАТЕЛЬ, аварийное прекращение работы () повышено, и строка/файл незаконного кода передается.

Таким образом, если Вы ой:

if (i = foo)

insted

if (i == foo)

сопровождаемый

assert (i > foo + 1)

... Вы быстро определите такие ошибки.

Надежда это помогает :)

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

1
ответ дан 2 December 2019 в 02:49
поделиться

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

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

1
ответ дан 2 December 2019 в 02:49
поделиться

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

Скажите, что у Вас есть функция 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);
}

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

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

2
ответ дан 2 December 2019 в 02:49
поделиться

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

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

2
ответ дан 2 December 2019 в 02:49
поделиться

Это очень распространено с "низкоуровневыми" конструкциями цикла в C/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 к == lvalues!

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

1
ответ дан 2 December 2019 в 02:49
поделиться

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

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

7
ответ дан 2 December 2019 в 02:49
поделиться

Попытайтесь просмотреть

if( life_is_good() )
    enjoy_yourself();

как

if( tmp = life_is_good() )
    enjoy_yourself();
2
ответ дан 2 December 2019 в 02:49
поделиться

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

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

2
ответ дан 2 December 2019 в 02:49
поделиться

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

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

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

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

Который на самом деле ограничивает объем t к только если и его тела.

3
ответ дан 2 December 2019 в 02:49
поделиться

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

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

4
ответ дан 2 December 2019 в 02:49
поделиться

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

list_elem* curr;
while ( (curr = next_item(list)) != null ) {
  /* ... */
}
5
ответ дан 2 December 2019 в 02:49
поделиться

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

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

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

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

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

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

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

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

4
ответ дан 2 December 2019 в 02:49
поделиться

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

Например:

 ~> gcc -c -Wall foo.c
foo.c: In function ‘foo’:
foo.c:5: warning: suggest parentheses around assignment used as truth value
5
ответ дан 2 December 2019 в 02:49
поделиться

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

if (x = y) {

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

2
ответ дан 2 December 2019 в 02:49
поделиться

Поскольку это не недопустимо (в C или 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
}

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

11
ответ дан 2 December 2019 в 02:49
поделиться

О допустимом использовании если (я = 0)

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

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

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

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

Который доказывает, что, в C/C++, если выражение не ограничено == и =. Что-либо сделает, пока это может быть оценено как TRUE или FALSE (C++) или обнулить ненулевой (C/C++).

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

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

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

Об усовершенствованном использовании если (я = 0) в C++ [Заключенный в кавычки от меня]

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

Так, заключение в кавычки от меня:

Другое использование должно было бы использовать то, что называют Инжекцией Переменной C++. В Java существует это прохладное ключевое слово:

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

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

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

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

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

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

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

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

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

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

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

На самом деле Microsoft была записана настолько трудно из-за этого, они добавили предупреждение в Visual C++ 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") ;
}

При попытке к "константе" как можно больше переменные заставят Вас избежать большинства ошибок опечатки, включая тех не внутри "если" выражения:

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") ;
}

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

Заключение

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

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

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

8
ответ дан 2 December 2019 в 02:49
поделиться

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

x = y; if (x) ...

можно записать

if (x = y) ...

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

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

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

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

Сравнение с ПУСТЫМ УКАЗАТЕЛЕМ избыточно, таким образом, можно переписать его как это:

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

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

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

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

13
ответ дан 2 December 2019 в 02:49
поделиться

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

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

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

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

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

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

28
ответ дан 2 December 2019 в 02:49
поделиться
 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'

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

Result: k still = 2, and Boolean = true

Наконец, он затем разрешает условие: Если (истина)

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

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

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

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

2
ответ дан 2 December 2019 в 02:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: