2009-11-07 2 views
6

У меня есть несколько вопросов, относящихся к setjmp/longjmp использование -Обработка исключений в C - Какое использование setjmp() возвращает 0?

  1. Что такое использование setjmp (СПМ ___ Buf stackVariables) возвращающегося 0. Это по умолчанию, которые мы не можем повлиять.

  2. Единственное значение setjmp (stackVariables) - это толкать стек в stackVariables. И в основном 0 говорит нам, успешно ли стек стекается на stack_variables.

  3. Это один случай, когда значение равно ненулевому (любое ненулевое значение), когда вы возвращаетесь из longjmp. Что возвращается из lomgjmp, когда вы возвращаетесь из longjmp, когда обрабатывается ваше исключение. Это настройка очень запутанная.

  4. Возможно, некоторые из них свяжутся, чтобы попробовать/бросить и поймать. И было бы действительно здорово, если бы были представлены хорошие примеры setjmp/longjmp.

  5. Является longjmp как throw, и он называется сразу после того места, где может быть поднято исключение.

Спасибо.

ответ

10

Спецификация C99 дает:

Если возвращение от прямого вызова, то setjmp макрокоманда возвращает нулевое значение. Если возврат происходит от вызова функции longjmp, макрос setjmp возвращает ненулевое значение .

Таким образом, ответ на 1, что ноль означает, что вы назвали setjmp в первый раз, и не ноль указывает, что он возвращается из longjmp.

  1. Он подталкивает текущее состояние программы. После longjmp состояние восстанавливается, управление возвращается к точке, которую оно вызывало, а возвращаемое значение отличное от нуля.

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

  3. try/catch в C++ вызовет деструкторы на всех автоматических объектах между броском и уловкой. setjmp/longjmp не назовут деструкторов, так как они не существуют в C. Таким образом, вы сами по себе, если вызываете free на все, что у вас есть malloc ed в среднее время.

С этой оговоркой, это:

#include <stdio.h> 
#include <setjmp.h> 
#include <string.h> 
#include <stdlib.h> 

void foo (char** data) ; 
void handle (char* data) ; 
jmp_buf env; 

int main() 
{ 
    char* data = 0; 

    int res = setjmp (env); 
    // stored for demo purposes. 
    // in portable code do not store 
    // the result, but test it directly. 

    printf ("setjmp returned %d\n", res); 

    if (res == 0) 
     foo (&data); 
    else 
     handle (data); 

    return 0; 
} 


void foo (char** data) 
{ 
    *data = malloc (32); 

    printf ("in foo\n"); 

    strcpy (*data, "Hello World"); 

    printf ("data = %s\n", *data); 

    longjmp (env, 42); 
} 

void handle (char* data) 
{ 
    printf ("in handler\n"); 

    if (data) { 
     free (data); 
     printf ("data freed\n"); 
    } 
} 

примерно эквивалентно

#include <iostream> 

void foo () ; 
void handle () ; 

int main() 
{ 
    try { 
     foo(); 
    } catch (int x) { 
     std::cout << "caught " << x << "\n"; 
     handle(); 
    } 

    return 0; 
} 

void foo () 
{ 
    printf ("in foo\n"); 

    std::string data = "Hello World"; 

    std::cout << "data = " << data << "\n"; 

    throw 42; 
} 

void handle () 
{ 
    std::cout << "in handler\n"; 
} 

В случае С, вы должны делать явное управление памятью (хотя обычно вы бы бесплатно это в функции, которую malloc'd перед вызовом longjmp, поскольку это упрощает жизнь)

+0

awesome man, thanks –

+0

LongJmp как throw, и он называется сразу после того места, где может быть поднято исключение. А также почему вы используете номер 42, вы можете выполнить одно и то же задание 1 или 42 любой ненулевой положительный int. –

+2

Ключевым моментом является то, что функция setjmp() может возвращаться один раз или может возвращаться несколько раз. Он вернется один раз из «прямого вызова» (который вернет 0); любые последующие возвращения являются результатом longjmp() и возвращают ненулевое значение. –

6

setjmp используется для размещения маркера , где должен возвращаться вызов longjump, он возвращает 0, если он вызывается напрямую, он возвращает 1, если он вызван, потому что вызывается longjmp для этого setjmp.

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

Это пример, приведенный википедии:

#include <stdio.h> 
#include <setjmp.h> 

static jmp_buf buf; 

void second(void) 
{ 
    printf("second\n");   // prints 
    longjmp(buf,1);    // jumps back to where setjmp was called - making setjmp now return 1 
} 

void first(void) 
{ 
    second(); 
    printf("first\n");   // does not print 
} 

int main() 
{ 
    if (! setjmp(buf)) 
    { 
     first();    // when executed, setjmp returns 0 
    } 
    else 
    {     // when longjmp jumps back, setjmp returns 1 
     printf("main");   // prints 
    } 

    return 0; 
} 

в состоянии понять это Вы? При запуске программы setjmp выполняется в основном и возвращает 0 (потому что он непосредственно вызывается), поэтому вызывается first, который вызывает second, а затем он принимает longjmp, который переключает контекст, возвращаясь туда, где использовался setjmp, но на этот раз, поскольку он возвращается из прыжка, и это косвенно называется возвратом функции 1.

Полезная вещь подхода setjmp/longjmp заключается в том, что вы можете обрабатывать ситуации с ошибками, не заботясь о том, чтобы держать флаг между вызовами функций (особенно, когда у вас много , подумайте о рекурсивной процедуре для проверки типов в компиляторе). Если что-то пойдет не так, как правило, в методе проверки стека вызовов в стеке, вы должны вернуть флаг и продолжать его возвращать, чтобы предупредить вызывающего абонента о том, что проверка typecheck не удалась. С longjmp вы просто выходите и обрабатываете ошибку, не заботясь о пропущенных флажках. Единственная проблема заключается в том, что это приводит к переключению контекста, который не заботится о стандартном освобождении памяти стека/кучи, поэтому вы должны обращаться с ним самостоятельно.

+0

спасибо за пример –

+1

Проблема вы отмечаете довольно трудно справиться, потому что вы должны знать все, что было выделено до longjmp и как освободить его.Кроме того, IIRC, любые нелетучие локальные переменные в функции, вызывающей setjmp, могут быть повреждены. –

4

Первая часть почти проста: когда вы делаете longjmp, вы оказываетесь в точности после setjmp. Если возвращаемое значение равно 0, это означает, что вы просто сделали setjmp; если он отличен от нуля, вы знаете, что у вас там есть длинный микс из другого места. Эта информация часто полезна для контроля того, что делает ваш код после этого.

setjmp/longjmp - это старые предки броска/улова. setjmp/longjmp определены в C, тогда как throw/catch является более «современным» механизмом для восстановления ошибок на более объектно-ориентированных языках, таких как C++.

, называя longjmp говорит: «Я думаю, что здесь что-то не так, помогите, вытащите меня отсюда - я слишком смущен, чтобы очистить себя и вернуться через кучу вызовов функций, и если да, просто верните меня обратно где снова мир был в порядке, сразу после последнего набора.

Бросок говорит почти то же самое, за исключением того, что он более четко и чисто поддерживается в синтаксисе. Кроме того, хотя longjmp может привести вас практически к любому месту в программе (везде, где вы делали setjmp), бросок заканчивается прямо вверх в иерархии вызовов, где находится бросок.

+0

спасибо fort the last, 2 paras. –

0

Просто добавьте ответ (a nd замечание) Пит Киркхэм: поскольку стандарт C не позволяет сохранить возвращаемое значение setjmp, возможно, пример Пита может быть изменен вместо использования переключателя. Он по-прежнему демонстрирует, как различать разные значения возврата, но не нарушает стандарт.

#include <stdio.h> 
#include <setjmp.h> 
#include <string.h> 
#include <stdlib.h> 

void foo(char** data) ; 
void handle(char* data) ; 
jmp_buf env; 

int main(void) 
{ 
    char* data = 0; 
    switch(setjmp(env)) 
    { 
    case 0: 
    { 
     printf("setjmp returned 0\n"); 
     foo(&data); 
     break; 
    } 
    case 42: 
    { 
     printf("setjmp returned 42\n"); 
     handle (data); 
     break; 
    } 
    default: 
    { 
     printf("setjmp returned something else?\n"); 
    } 
    } 
    return 0; 
} 

void foo(char** data) 
{ 
    *data = malloc(32); 
    printf("in foo\n"); 
    strcpy(*data, "Hello World"); 
    printf("data = %s\n", *data); 
    longjmp(env, 42); 
} 

void handle(char* data) 
{ 
    printf("in handler\n"); 
    if(data) 
    { 
    free(data); 
    printf("data freed\n"); 
    } 
} 
Смежные вопросы