Каковы преимущества назначения сдавливания и проверки ошибок в одной строке? - PullRequest
19 голосов
/ 23 мая 2010

Этот вопрос основан на этом вопросе , который содержит следующий фрагмент кода.

int s;
if((s = foo()) == ERROR)
    print_error();

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

int s = foo();
if(s == ERROR)
    print_error();

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

Ответы [ 8 ]

15 голосов
/ 23 мая 2010

Я думаю, что по истерическим причинам ранние компиляторы не были настолько умны в оптимизации.Помещая его в одну строку в виде одного выражения, он дает компилятору подсказку, что можно протестировать одно и то же значение, извлеченное из foo (), а не загружать его из s.

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

13 голосов
/ 23 мая 2010

Когда вы пишете цикл, иногда желательно использовать первую форму, как в этом известном примере из K & R:

int c;

while ((c = getchar()) != EOF) {
    /* stuff */
}

Не существует элегантного способа написания второй формы без повторения:

int c = getchar();

while (c != EOF) {
    /* stuff */
    c = getchar();
}

Или:

int c;

for (c = getchar(); c != EOF; c = getchar()) {
    /* stuff */
}

Теперь, когда присваивание c повторено, код более подвержен ошибкам, потому что нужно синхронизировать оба оператора.

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

Я склонен использовать первую форму в основном потому, что мне легко читать & mdash; как кто-то сказал, она гораздо ближе связывает вызов функции и проверку возвращаемого значения.

5 голосов
/ 23 мая 2010

Я делаю сознательную попытку объединить эти два, когда это возможно.«Штрафа» в размере недостаточно для того, чтобы преодолеть преимущество в ясности, IMO.

Преимущество в ясности проистекает из одного факта: для такой функции вы всегда должны думать о вызове функции и тестированиивозвращаемое значение как одиночное действие, которое нельзя разбить на две части («атомарные», если хотите).Вы должны никогда вызывать такую ​​функцию без немедленной проверки ее возвращаемого значения.

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

4 голосов
/ 23 мая 2010

Я бы всегда пошел на секунду. Его легче читать, нет опасности опустить круглые скобки вокруг присваивания и легче перейти к отладчику.

1 голос
/ 24 мая 2010

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

Это также позволяет мне легче делать такие вещи, как:

int rc = function();

DEBUG_PRINT(rc);

if (rc == ERROR) {
    recover_from_error();
} else {
    keep_on_going(rc);
}

Я предпочитаю этот стиль настолько, что в случае циклов я бы предпочел:

while (1) {
    int rc = function();
    if (rc == ERROR) {
        break;
    }
    keep_on_going(rc);
}

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

1 голос
/ 23 мая 2010

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

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

YMMV для компилятора и отладчика и, конечно, для оптимизированных сборок.

0 голосов
/ 07 октября 2011

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

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

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

0 голосов
/ 23 мая 2010

Я часто предпочитаю первую форму.Я не могу точно сказать, почему, но это как-то связано с семантической вовлеченностью.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...