2009-11-07 40 views
6

Tôi có một số câu hỏi liên quan đến setjmp/longjmp sử dụng -Xử lý ngoại lệ trong C - Việc sử dụng setjmp() trả về 0 là gì?

  1. là gì việc sử dụng setjmp (jmp ___ buf stackVariables) trở về 0. Đó là một mặc định, mà chúng ta không thể ảnh hưởng.

  2. Ý nghĩa duy nhất của setjmp (stackVariables) là đẩy ngăn xếp trong stackVariables. Và về cơ bản 0 cho chúng ta biết nếu ngăn xếp được đẩy lên stack_variables thành công.

  3. Đó là một dịp khi giá trị không khác (bất kỳ số không khác) khi bạn quay trở lại từ longjmp. Quay trở lại từ một lomgjmp, khi nào bạn quay trở lại từ longjmp, khi ngoại lệ của bạn được xử lý. Đây là thiết lập thực sự khó hiểu.

  4. Có thể một số hãy liên hệ để thử/ném và nắm bắt. Và sẽ thực sự tuyệt vời, nếu một số ví dụ tốt về setjmp/longjmp có thể được cung cấp.

  5. LongJmp như ném, và nó được gọi ngay sau khi nơi ngoại lệ có thể được nâng lên.

Cảm ơn.

Trả lời

10

Các C99 đặc tả cho:

Nếu sự trở lại là từ một gọi trực tiếp, macro setjmp trả về giá trị zero. Nếu trả lại là từ một cuộc gọi đến hàm longjmp, macro setjmp trả về giá trị không đồng nhất.

Vì vậy, câu trả lời cho 1 là số không cho biết bạn đã gọi số setjmp lần đầu tiên và khác 0 cho biết nó đang quay về từ longjmp.

  1. Nó đẩy trạng thái chương trình hiện tại. Sau khi một longjmp, trạng thái được khôi phục, kiểm soát trở về điểm nó được gọi, và giá trị trả về là khác không.

  2. Không có ngoại lệ trong C. Đó là loại tương tự như fork trả về các giá trị khác nhau tùy thuộc vào việc bạn đang trong quá trình gốc hay quy trình thứ hai đã thừa kế môi trường, nếu bạn quen thuộc với điều đó.

  3. try/catch trong C++ sẽ gọi destructors trên tất cả các đối tượng tự động giữa ném và bắt. setjmp/longjmp sẽ không gọi destructors, vì chúng không tồn tại trong C. Vì vậy, bạn đang ở trên của riêng bạn như xa như gọi free trên bất cứ điều gì bạn đã malloc ed trong thời gian có nghĩa là.

Với điều kiện là, này:

#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"); 
    } 
} 

là tương đương với

#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"; 
} 

Trong trường hợp C, bạn phải làm quản lý bộ nhớ rõ ràng (mặc dù thông thường bạn muốn miễn phí nó trong chức năng mà malloc'd nó trước khi gọi longjmp vì nó làm cho cuộc sống đơn giản)

+0

người đàn ông tuyệt vời, cảm ơn –

+0

Là longJmp như ném, và nó được gọi là ngay sau khi nơi mà ngoại lệ có thể được nâng lên. Và cũng tại sao bạn sử dụng số 42, bạn có thể thực hiện cùng một công việc 1, hoặc là 42 bất kỳ số nguyên dương nào khác 0. –

+2

Điểm quan trọng là hàm setjmp() có thể trả về một lần, hoặc nó có thể trở lại nhiều lần. Nó sẽ trở lại một lần từ 'lời gọi trực tiếp' (sẽ trả về 0); bất kỳ trở về sau là kết quả của một longjmp() và sẽ trả về một giá trị khác không. –

6

setjmp được sử dụng để đặt một dấu hiệu đến nơi cuộc gọi của longjump sẽ trả về, nó trả về 0 nếu nó được gọi trực tiếp, nó trả về 1 nếu được gọi vì longjmp đến setjmp đó được gọi.

Bạn phải suy nghĩ về setjmp như một cái gì đó có thể được gọi bình thường và không làm bất cứ điều gì (trở về 0) trong hoạt động bình thường trong khi trả về 1 và nó gián tiếp gọi (và trở về từ đó) khi một bước nhảy dài được gọi. Tôi biết những gì bạn có nghĩa là về bối rối vì nó thực sự khó hiểu ..

này được ví dụ do wikipedia:

#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; 
} 

Bạn có thể hiểu được nó? Khi chương trình được khởi chạy setjmp được thực hiện bằng chính và trả về 0 (vì nó được gọi trực tiếp), do đó, first được gọi, gọi second và sau đó đến longjmp chuyển ngữ cảnh quay trở lại nơi setjmp đã được sử dụng, nhưng lần này, vì nó trở lại từ một bước nhảy và nó được gián tiếp gọi là hàm trả về 1.

Điều hữu ích của phương pháp setjmp/longjmp là bạn có thể xử lý các tình huống lỗi mà không cần giữ cờ giữa các cuộc gọi hàm (đặc biệt là khi bạn có nhiều , suy nghĩ về một thủ tục đệ quy cho typechecking trong một trình biên dịch). Nếu có điều gì đó sai trong việc đánh máy sâu trong ngăn xếp cuộc gọi bình thường, bạn phải trả lại một lá cờ và tiếp tục trả lại nó để cảnh báo người gọi rằng việc gõ lỗi đã thất bại. Với longjmp bạn chỉ cần đi ra ngoài và xử lý lỗi mà không cần quan tâm đến việc truyền lại cờ. Vấn đề duy nhất là điều này buộc một chuyển đổi ngữ cảnh không quan tâm đến deallocation chuẩn của bộ nhớ stack/heap, do đó bạn nên xử lý nó một mình.

+0

cảm ơn ví dụ –

+1

Vấn đề bạn chỉ ra là khó xử lý vì bạn phải biết mọi thứ được phân bổ trước longjmp và cách deallocate nó.Ngoài ra, IIRC, bất kỳ biến cục bộ không biến động nào trong hàm có vấn đề setjmp có thể bị hỏng. –

4

Phần đầu tiên gần như là đơn giản: Khi bạn thực hiện một longjmp, bạn sẽ kết thúc chính xác sau khi setjmp. Nếu giá trị trả về là 0, có nghĩa là bạn đã thực hiện setjmp; nếu nó không phải là nonzero, bạn biết bạn đã có từ một longjmp từ nơi khác. Thông tin đó thường hữu ích trong việc kiểm soát mã của bạn sau đó.

setjmp/longjmp là tổ tiên cũ của trò ném/nắm bắt. setjmp/longjmp được định nghĩa trong C, trong khi throw/catch là cơ chế "hiện đại" hơn để thực hiện khôi phục lỗi trong nhiều ngôn ngữ hướng đối tượng như C++.

longjmp gọi là: "Tôi nghĩ có gì đó không ổn ở đây, giúp tôi đưa tôi ra khỏi đây - Tôi quá lúng túng để dọn dẹp sau bản thân mình và trở lại qua một loạt các cuộc gọi chức năng và nếu có, hãy đưa tôi quay lại ngay đến nơi thế giới lại OK, ngay sau setjmp cuối cùng. "

ném nói khá nhiều điều tương tự, ngoại trừ nó được hỗ trợ rõ ràng và rõ ràng hơn trong cú pháp. Ngoài ra, trong khi longjmp có thể đưa bạn đến bất cứ nơi nào thực tế trong chương trình (bất cứ nơi nào bạn đã làm setjmp), ném kết thúc lên trực tiếp trong hệ thống phân cấp cuộc gọi của nơi ném.

+0

cảm ơn pháo đài cuối cùng, 2 ký tự. –

0

Chỉ cần thêm vào câu trả lời (một nd nhận xét) bởi Pete Kirkham: kể từ khi C tiêu chuẩn không cho phép lưu trữ giá trị trả về của setjmp, có lẽ ví dụ của Pete có thể được thay đổi để sử dụng chuyển đổi để thay thế. Nó vẫn thể hiện cách phân biệt giữa các giá trị trả về khác nhau nhưng không vi phạm tiêu chuẩn.

#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"); 
    } 
} 
Các vấn đề liên quan