Безопасное использование longjmp/setjmp с volatile

Я думаю использовать макрос TRY/CATCH на основе setjmp/longjmp для обработки ошибок. В противном случае некоторые из моих хорошо структурированных функций будут разрушены уродливыми операторами if и флажками цикла.

Код подобен этому примеру:

int trycatchtest(int i)
{
    int result = 0;
    volatile int error = 100;
    volatile uint32_t *var = NULL;
    TRY
    {
        error = 0;
        var = os_malloc(4);
        *var = 11;
        if (i) THROW( i );
    }
    FINALLY
    {
        result = *var;
    }
    END;
    return result;
}

THROW на самом деле макрос

#define TRY do { jmp_buf buf; switch( setjmp(buf) ) { case 0:     while(1) {
#define FINALLY break; } default: {
#define END break; } } } while(0)
#define THROW(x) longjmp(buf, x)

Проблема:

Когда возникает исключение (например, i=1), указатель var сбрасывается в NULL, хотя я использовал ключевое слово volatile, которое должно избегать использования для него регистра. Из отладчика я вижу, что он все еще находится в регистре, а не в памяти.

Я сделал ошибку?

РЕДАКТИРОВАТЬ:

Я изменил объявление var на

uint32_t * volatile var = NULL;

Это работает ;-)

Я не очень понимаю, в чем разница:

volatile uint32_t * var = NULL;

означает, что ЗНАЧЕНИЕ является изменчивым, тогда как предыдущее объявление делает указатель изменчивым?


person MichaelW    schedule 12.09.2016    source источник
comment
Когда я компилирую этот код и printf("%d\n", trycatchtest(1)); он возвращает 11... Как вы компилировали и какие флаги использовали?   -  person tversteeg    schedule 12.09.2016
comment
Я согласен с @tversteeg, это работает и на моей машине. Какой это компилятор/платформа? Как определяется os_malloc, предположительно, вы делаете какие-то встроенные/RTOS вещи?   -  person Groo    schedule 12.09.2016
comment
см. мою правку выше!   -  person MichaelW    schedule 12.09.2016
comment
Это какой-то очень уродливый и опасный код. Не пишите такую ​​чушь, это намного хуже шаблона goto при ошибке. Что в свою очередь хуже функции, возвращающей код ошибки.   -  person Lundin    schedule 12.09.2016
comment
Я согласен с @Lundin, шаблон перехода при ошибке (который, например, широко используется в исходном коде ядра Linux) предпочтительнее этого дерьма. Ваша функция trycatchtest имеет утечку памяти, хотя вы используете ее только для проверки своих макросов. Поскольку buf является локальной переменной, эти макросы не будут работать при вызовах вложенных функций, так зачем вам нужно использовать setjmp/longjmp для этих макросов?   -  person Ian Abbott    schedule 12.09.2016
comment
В ответ на мой комментарий выше я понимаю, почему вам нужно setjmp/longjmp для этих макросов, а не goto и локальная метка, и это связано с метками, имеющими область действия, а не область действия блока. Я бы все равно не использовал эти макросы!   -  person Ian Abbott    schedule 12.09.2016


Ответы (1)


u32 *volatile var делает указатель изменчивым, а volatile u32 *var сообщает компилятору, что данные по этому адресу являются изменчивыми. Поэтому, поскольку в последнем примере указатель не является изменчивым, я не удивлюсь, если ваш компилятор полностью оптимизирует случай default до чего-то вроде result = NULL;.

Вероятно, он не ожидает волшебства setjmp, и они печально известны тем, что они даже «больше спагетти, чем goto».

person Groo    schedule 12.09.2016
comment
Непрекращающиеся споры о том, какой стиль лучше всего подходит для обработки ошибок в C. Поскольку я использую макросы только для обработки ошибок в больших программах тестирования, они упрощают мне жизнь. - person MichaelW; 12.09.2016
comment
stackoverflow.com/ вопросы/14685406/ - person MichaelW; 12.09.2016