Подтвердить что ты не робот

Почему эта конструкция используется? Безумный или гениальный?

Я работаю с большой SDK-кодовой базой, объединенных из разных источников различного качества/компетентности/здравомыслия от Линуса Торвальдса к неопознанным slave-модулям Elbonian.

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

Я только что столкнулся с кучей функций, которые неоднократно используют немного странный (для меня) стиль, а именно:

void do_thing(foo)
{
    do {
        if(this_works(foo) != success)
            break;
        return(yeah_cool);
    } while (0);
    return(failure_shame_death);
}

В этом коде нет ничего сложного (я не сократил 10 000 строк волшебства для этого сообщения), они могут так же легко сделать:

if(this_works(foo) == success)
    return(yeah_cool);
else
    return(failure_shame_death);

Что бы показалось, что-то приятнее/аккуратно/интуитивно понятно/легче читать.

Итак, теперь мне интересно, есть ли какая-то (хорошая) причина для этого по-другому, или это так, как они всегда это делают в Elbonian Code Mines?

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

4b9b3361

Ответ 1

Еще одно предположение: возможно, вы не указали оригинальный код правильно? Я видел тот же шаблон, используемый людьми, которые хотят избежать goto: они используют цикл do-while(0), который в конце возвращает значение успеха. Они также могут break выйти из цикла для обработки ошибок:

int doXandY() {
   do {
      if (!x()) {
         break;
     }

     if (!y()) {
         break;
     }
     return 0;
   } while( 0 );

   /* Error handling code goes here. */
   globalErrorFlag = 12345;
   return -1;
}

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

Ответ 2

Hm, код может быть предварительно обработан. do { } while(0) - это трюк, используемый в макросах препроцессора; вы можете определить их следующим образом:

#define some_macro(a) do { whatever(); } while(0)

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

Причиной этого является то, что если вы пишете

#define some_macro(a) { whatever(); }

if (some_condition)
    some_macro(123);
else
    printf("this can cause problems\n");

Так как перед выражением else имеется дополнительная точка с запятой, этот код недействителен. do { ... } while(0) будет работать где угодно.

Ответ 3

Некоторые люди используют конструкцию do{} while(0); с break; внутри цикла, чтобы соответствовать в некотором роде с правилом MISRA 14.7. Это правило говорит, что в функции может быть только одна точка входа и выхода. Это правило также требуется по норме безопасности ISO26262. Пожалуйста, найдите пример функции:

int32_t MODULE_some_function(bool first_condition,bool second_condition)
{
    int32_t ret = -1 ;

    do
    {
        if(first_condition)
        {
            ret = 0 ;
            break ;
        }

        /* some code here */ 

        if(second_condition)
        {
            ret = 0 ;
            break ;
        }

        /* some code here */ 

    } while(0) ;

    return ret ;
}

Обратите внимание, что такая конструкция, как я показываю выше, нарушает другое правило MISRA, которое является правилом 14.6. Написав такой код, вы будете совместимы с одним правилом MISRA, и насколько я знаю, люди используют такую ​​конструкцию, как обходной путь против использования нескольких возвратов функции.

По-моему, практическое использование конструкции do{}while(0); действительно существует в том, как вы должны создавать некоторые типы макросов. Пожалуйста, проверьте ниже вопрос, это было очень полезно для меня:

Зачем использовать явно бессмысленные инструкции do-while и if-else в макросах?

Следует также отметить, что в некоторых случаях конструкция do{}while(0); будет полностью оптимизирована, если вы скомпилируете свой код с подходящей опцией оптимизации.

Ответ 4

Я бы предположил, что этот код изначально был написан с помощью goto для обработки ошибок:

void do_thing(foo)
{
    if(this_works(foo) != success)
        goto error;
    return(yeah_cool);
error:
    return(failure_shame_death);
}

Но в какой-то момент эдикт спустился с высоты "ты не должен использовать goto", поэтому кто-то сделал полуавтоматический перевод из стиля goto в стиле петли (возможно, с простым script). Вероятно, когда код был объединен/перенесен из одного проекта в другой.

Ответ 5

do {...}, в то время как (0), организованный с "break", является своего рода "RAII для Plain C".

Здесь "break" рассматривается как ненормальный выход области (вид "исключений Plain C" ), поэтому вы можете быть уверены, что есть только одно место для освобождения ресурса: после "while (0)". Это кажется немного необычным, но на самом деле это очень распространенная идиома в мире простых C.