`longjmp` 함수
longjmp 함수 (longjmp)
일반 함수 호출은 스택을 따라 한 단계씩 반환하면서 올라오지만, 가끔은 깊숙이 들어간 함수에서 여러 단계를 한 번에 건너뛰어 미리 찍어 둔 위치로 돌아가고 싶을 때가 있어요. 그걸 가능하게 해 주는 게 longjmp와 짝꿍인 setjmp예요. 다만 자유도가 큰 만큼, 올바르게 쓰지 않으면 동작이 정의되지 않으니 조심해야 해요.
출처: cppreference
본문
함수 원형
<setjmp.h>에 정의돼 있어요.
void longjmp( jmp_buf env, int status ); // (until C11)
_Noreturn void longjmp( jmp_buf env, int status ); // (since C11, until C23)
[[noreturn]] void longjmp( jmp_buf env, int status ); // (since C23)
설명 (Explanation)
이전 setjmp 호출이 저장해 둔 실행 문맥 env를 되살려요. 이 함수는 반환하지 않고, env를 설정한 setjmp 매크로의 호출 지점으로 제어를 옮겨요. 그 setjmp는 status로 넘긴 값을 반환값으로 돌려주죠.
여기서 제약이 있어요. setjmp를 호출한 함수가 이미 반환했다면(스택 더 위쪽의 다른 longjmp 때문이라도) 동작은 정의되지 않아요. 다시 말해, 호출 스택을 따라 위쪽으로만 점프할 수 있어요.
C11부터: setjmp를 호출한 함수가 다른 스레드에서 실행됐다면 스레드 간 점프도 동작이 정의되지 않아요.
C99부터: setjmp 호출 시점에 VLA나 다른 가변 수정 타입 변수가 유효 범위 안에 있었고 제어가 그 범위를 벗어났다면, 함수 안에 머물렀더라도 그 setjmp로 돌아가는 longjmp는 동작이 정의되지 않아요. 스택을 거슬러 올라가는 동안 longjmp는 어떤 VLA도 해제하지 않아서, 이런 경로로 수명이 끝나면 메모리 누수가 생길 수 있어요:
void g(int n)
{
int a[n]; // a may remain allocated
h(n); // does not return
}
void h(int n)
{
int b[n]; // b may remain allocated
longjmp(buf, 2); // might cause a memory leak for h's b and g's a
}
매개변수 (Parameters)
주의할 점 (Notes)
longjmp는 함수가 의미 있게 값을 반환할 수 없는 예상치 못한 오류 상황을 처리하기 위한 도구예요. 다른 언어의 예외 처리와 비슷한 역할을 한다고 생각하면 이해가 쉬워요.
예제 (Example)
센티널 값 5와 비교하며, setjmp가 몇 번 되돌아가는지 세는 예시예요.
#include <setjmp.h>
#include <stdio.h>
#include <stdnoreturn.h>
jmp_buf my_jump_buffer;
noreturn void foo(int status)
{
printf("foo(%d) called\n", status);
longjmp(my_jump_buffer, status + 1); // will return status+1 out of setjmp
}
int main(void)
{
volatile int count = 0; // modified local vars in setjmp scope must be volatile
if (setjmp(my_jump_buffer) != 5) // compare against constant in an if
foo(++count);
}
출력:
foo(1) called
foo(2) called
foo(3) called
foo(4) called
setjmp 범위에서 수정되는 지역 변수는 volatile로 선언해야 한다는 점과, setjmp의 반환값을 상수와 비교해야 한다는 점이 이 함수를 다룰 때 놓치기 쉬운 부분이에요.
같이 보기 (See also)
setjmp— 문맥 저장(함수 매크로)