지역 스코프 전역 변수
지역 스코프 전역 변수 (Locally-Scoped Global Variables)
함수 안에서 평범한 변수는 함수가 끝나면 수명도 함께 끝나요. 그런데 가끔은 함수 안에 선언했지만 전역처럼 죽지 않고 계속 살아 있어야 하는 변수가 필요할 때가 있어요. Zig에서는 이걸 함수 안에 네임스페이스를 만들어서 해결하지요.
본문
함수 안에 struct를 선언하고, 그 안에 var로 변수를 두면 됩니다. struct는 그 자체가 네임스페이스라서, 안에 놓인 var 변수는 함수를 벗어나서도 전역 수명을 갖게 돼요. 이것이 '전역 수명을 가진 지역 변수'라는 말이 뜻하는 바예요.
const std = @import("std");
const expectEqual = std.testing.expectEqual;
test "static local variable" {
try expectEqual(1235, foo());
try expectEqual(1236, foo());
}
fn foo() i32 {
const S = struct {
var x: i32 = 1234;
};
S.x += 1;
return S.x;
}
foo()를 처음 부르면 S.x는 1234에서 시작해 1이 더해져 1235가 돼요. 그리고 함수가 끝나도 S.x는 사라지지 않고 그 값 1235를 계속 간직하지요. 그래서 두 번째 호출에서는 1235에 다시 1을 더한 1236이 반환됩니다. 이처럼 값이 호출 사이에 누적되는 모습을 실행 결과로도 확인할 수 있어요.
$ zig test test_locally_scoped_global_variable.zig
1/1 test_locally_scoped_global_variable.test.static local variable...OK
All 1 tests passed.
실행 흐름에서 놓치기 쉬운 점 하나만 짚어볼게요. foo() 안의 struct 선언은 함수를 부를 때마다 새로 만들어지는 게 아니라, 딱 한 번만 정의돼요. 그래서 S.x의 상태가 호출 사이에 계속 보존되는 거죠. 마치 C의 static 지역 변수처럼 움직인다고 생각하면 이해가 쉬워요.
더 알아보기
- 전역 수명을 가지면서 스레드마다 별도의 값이 필요한 상황이라면, 다음 섹션에서 다루는 Thread Local Variables를 보면 돼요.
- 네임스페이스 자체에 대한 설명은 관련 섹션에서 더 확인할 수 있어요.