У C і C ++, які методи можуть запобігти випадковому використанню завдання (=) там, де потрібна еквівалентність (==)?


15

У C і C ++ дуже просто написати наступний код із серйозною помилкою.

char responseChar = getchar();
int confirmExit = 'y' == tolower(responseChar);
if (confirmExit = 1)
{
    exit(0);
}

Помилка полягає в тому, що оператор if повинен був:

if (confirmExit == 1)

Як закодований, він буде виходити кожен раз, тому що відбувається присвоєння confirmExitзмінної, а потім confirmExitвикористовується як результат виразу.

Чи є хороші способи запобігти такій помилці?


39
Так. Увімкніть попередження компілятора. Трактуйте попередження як помилки, і це не проблема.
Мартін Йорк


9
У наведеному прикладі рішення легко. Ви привласнити йому логічне значення, таким чином , використовувати його як логічне значення: if (confirmExit).
Безпечний

4
Проблема полягає в тому, що помилка була зроблена "дизайнерами" мови С, коли вони вирішили використовувати = для оператора призначення і == для порівняння рівності. ALGOL використовував: =, тому що вони спеціально хотіли використовувати = для порівняння рівності, а PASCAL і Ada дотримувались рішення ALGOL. (Варто зауважити, що, коли DoD звернувся із заявою на базі С до виходу пакета DoD1, що в підсумку привело до Ада, Bell Labs відмовився, сказавши, що "C не було зараз і ніколи не буде достатньо надійним для критичних місій DoD" програмне забезпечення. "
Хочеться,

4
@John, вибір символів не є проблемою, це факт, що призначення також є виразом, який повертає призначене значення, дозволяючи a = bабо a == bв умовному, або всередині нього.
Карл Білефельдт

Відповіді:


60

Найкращий прийом - це підвищити рівень попередження вашого компілятора. Потім він попередить вас про можливе призначення у випадку, якщо це умовно.

Переконайтеся, що ви компілюєте свій код із нульовими попередженнями (що ви все одно повинні робити). Якщо ви хочете бути педантичними, тоді встановіть компілятор, щоб трактувати попередження як помилки.

Використання умовних умов Yoda (поставлення константи на ліву сторону) була ще однією технікою, популярною близько десятиліття тому. Але вони ускладнюють читання коду (і таким чином підтримують через неприродний спосіб, який вони читають (якщо ви не Йода)) і не дають більшої користі, ніж підвищення рівня попередження (що також має додаткові переваги від більшої кількості попереджень).

Попередження справді логічні помилки в коді, і їх слід виправити.


2
Однозначно йдіть з попередженнями, а не умовами Yoda - ви можете забути спочатку робити умовну константу так само легко, як ви можете забути зробити ==.
Майкл Коне

8
Мене приємно здивувало, що найбільш голосованою відповіддю на це питання є "перетворити попередження компілятора в помилки". Мене очікував if (0 == ret)жах.
Джеймс

2
@James: ... не кажучи вже про це не вийде a == b!!
Еміліо Гаравалья

6
@EmilioGaravaglia: Для цього є легке рішення: Просто напишіть 0==a && 0==b || 1==a && 1==b || 2==a && 2==b || ...(повторіть для всіх можливих значень). Не забувайте про обов'язкову ...|| 22==a && 22==b || 23==a && 24==b || 25==a && 25==b ||... помилку, інакше програмісти з обслуговування не будуть веселитися.
користувач281377

10

Ви завжди можете зробити щось радикальне, як тестування свого програмного забезпечення. Я навіть не маю на увазі автоматизовані тестові одиниці, просто тести, які виконує кожен досвідчений розробник, запускаючи свій новий код два рази, один раз підтверджуючи вихід і один раз ні. Саме тому більшість програмістів вважають це не питанням.


1
Це легко зробити, коли поведінка вашої програми є повністю детермінованою.
Джеймс

3
Ця проблема може бути важко перевірити. Я бачив людей, куплених rc=MethodThatRarelyFails(); if(rc = SUCCESS){не раз, особливо якщо метод не вдається лише в умовах, на які важко перевірити.
Gort the Robot

3
@StevenBurnap: Для цього потрібні макетні об’єкти. Правильне тестування включає тестування режимів відмов.
Ян Худек

Це правильна відповідь.
Гонки легкості з Монікою

6

Традиційний спосіб запобігти неправильному використанню завдань в рамках виразу - розмістити константу зліва та змінну праворуч.

if (confirmExit = 1)  // Unsafe

if (1=confirmExit)    // Safe and detected at compile time.

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

.\confirmExit\main.cpp:15: error: C2106: '=' : left operand must be l-value

Переглянуто, якщо б умовою було:

  if (1==confirmExit)    

Як показано в коментарях нижче, багато хто вважає це невідповідним методом.



13
Слід зазначити, що робити такі речі, ви зробите вас досить непопулярними.
riwalk

11
Будь ласка, не рекомендуйте цю практику.
Джеймс

5
Він також не працює, якщо вам потрібно порівняти дві змінні між собою.
dan04

Деякі мови насправді дозволяють призначити 1 новому значенню (так, я знаю, що Q - це поєдинок c / c ++)
Матсман

4

Я погоджуюся з тим, що всі кажуть "попередження компілятора", але хочу додати ще одну техніку: Code reviews. Якщо у вас є політика щодо перегляду всього коду, який виконуються, бажано, перш ніж він буде скоєний, то, ймовірно, ця річ буде спіймана під час перегляду.


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

2

По-перше, підвищення рівня попередження ніколи не шкодить.

Якщо ви не хочете, щоб ваше умовне тестувало результат завдання в самому операторі if, тоді протягом багатьох років працювали з великою кількістю програмістів C і C ++, і ніколи не чули, щоб порівнювати константу спочатку if(1 == val) було погано, можна спробувати цю конструкцію.

Якщо ваш керівник проекту схвалює ваше виконання, не хвилюйтеся, що думають інші. Реальним доказом є те, чи можете ви чи хтось інший зрозуміти ваш код місяцями та роками.

Однак якщо ви мали намір перевірити результат завдання, то використання вищих попереджень, можливо, спричинило б це призначення на постійну.


Я піднімаю скептично брову у будь-якого програміста-початківця, який бачить Yoda умовно і не відразу це розуміє. Це просто фліп, не важкий для читання, і, звичайно, не такий вже й поганий, як стверджують деякі коментарі.
підкреслюй_

@underscore_d У більшості моїх роботодавців накази в умовному режимі були нахмурені. Мислення було краще розділити завдання від умовного. Причиною ясності навіть за рахунок іншого рядка коду став стійкий інженерний фактор. Я працював у місцях, де були великі бази кодів і багато технічного обслуговування. Я повністю розумію, що хтось може захотіти призначити значення та розгалужити умови, що є результатом присвоєння. Я бачу, що це робиться частіше в Perl, і, якщо наміри будуть зрозумілі, я дотримуюся схеми дизайну автора.
восьминоги граббус

Я думав про Yoda conditionals наодинці (як ваша демонстрація тут), а не з дорученням (як в ОП). Я не проти перших, але останній теж не дуже подобається. Єдиною формою, яку я навмисно використовую, є те if ( auto myPtr = dynamic_cast<some_ptr>(testPtr) ) {, що вона дозволяє уникнути непотрібності nullptrв області застосування, якщо виклик не вдається - що, мабуть, є причиною того, що C ++ має цю обмежену здатність призначати в умовному режимі. В іншому, так, визначення повинно мати свою лінію, я б сказав, що набагато простіше побачити за одним поглядом і менш схильне до різного промахування розуму.
підкреслюй_

@underscore_d Відредагована відповідь на основі ваших коментарів. Гарна думка.
восьминоги граббус

1

Запізнився на вечірку, як ніколи, але ключовим тут є статичний аналіз коду

Більшість IDE тепер надають SCA над і синтаксичною перевіркою компілятора, а також доступні інші інструменти, в тому числі ті, що реалізують вказівки MISRA (*) та / або CERT-C.

Декларація: Я є членом робочої групи MISRA C, але я працюю в особистій якості. Я також незалежний від будь-якого постачальника інструментів


-2

Просто використовуйте призначення лівої руки, попередження компілятора можуть допомогти, але вам потрібно переконатися, що ви отримаєте потрібний рівень, інакше ви будете завалені безглуздими попередженнями, або вам не скажуть, що ви хочете бачити.


4
Ви були б здивовані тим, як мало безглуздих попереджень генерують сучасні компілятори. Ви можете не вважати, що попередження важливе, але в більшості випадків ви просто помиляєтесь.
Прорік Крістоф

Перегляньте книгу "Написання твердого коду" amazon.com/Writing-Solid-Microsoft-Programming-Series/dp/… . Це починається з великої дискусії про компілятори та про те, яку користь ми могли б отримати від попереджувальних повідомлень та ще більш широкого статичного аналізу.
DeveloperDon

@KristofProvost Мені вдалося отримати візуальну студію, яка дала мені 10 копій такого ж попередження від того ж рядка коду, тоді, коли ця проблема була "виправлена", це призвело до 10 однакових попереджень про той самий рядок коду, що оригінальний метод був кращим.
Перевернута лама
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.