다음은 C99 에서 구조체를 할당하는 예제입니다.
struct S { int a; int b; }; int main() { struct S s = {.a=1, .b=2}; printf("%d %d\n", s.a, s.b); return 0; }
이게 된다는 이야기는 이런 것도 가능하다는 말이죠.
void func1(struct S s) { printf("%d %d\n", s.a, s.b); } int main() { func1((struct S){.a=1, .b=2}); return 0; }
main() 함수에서 구조체에 메모리 공간을 할당하고, 이를 인자로 func1() 함수로 전달할 수 있다는 것은 이해할 수 있습니다.
그런데 아주 예전에 C 를 배울 때, 함수 내부에서 쓰던 배열을 리턴하지 말라고 들었던 것 같습니다. 함수가 종료되면 그 메모리 공간을 믿을 수 없다고요. 비슷한 이유로 아주 예전에 C++ 을 배울 때, 클래스의 복사와 대입 연산자를 만들어줘야 했었던 것도 같습니다. 뭐... 언어도 발전하니까 지금은 바뀐 것일수도 있고, 아니면 예전에 잘못 이해한 것일수도 있겠죠.
정확한 기억은 아닙니다만, 아주 예전에 gcc 에서 구조체를 그대로 리턴하지 못했었던 것 같은... 기억이 있어요. 컴팩 워크스테이션이 있었거든요. 컴팩 컴파일러로 구조체를 리턴했을 때 잘 돌아가던 코드가 gcc 에서 문제가 있었던 것 같은 기억이... 오래 전 일이라 정확하지는 않아요. 어, 이게 되네? 했다가 어, 안되잖아. 했던 기억과 malloc() 하고 포인터로 리턴하도록 바꿨던 것 같네요. 지금 생각하면 컴팩 컴파일러가 혹시 C++ 컴파일러였을지도 모르겠어요. 마이크로소프트의 컴파일러도 실제로는 C 컴파일러가 아니라 C++ 컴파일러니까요. 암튼...
위 두 코드가 모두 믿을만한 것이라면, 이런 것도 되지 않을까 싶었어요.
struct S func2(void) { struct S s = {.a=1, .b=2}; return s; } int main() { struct S s = func2(); printf("%d %d\n", s.a, s.b); return 0; }
이것도 되나 싶었고요.
struct S func3(struct S s) { s.a = 3; s.b = 4; return s; } int main() { struct S s0 = {.a=1, .b=2}; struct S s1 = func3(s0); printf("%d %d\n", s0.a, s0.b); printf("%d %d\n", s1.a, s1.b); return 0; }
둘 다 잘 되네요. 물론, 배열로 할당된 메모리 공간은 리턴하면 안되지만
int* func4(void) { int a[2] = {1, 2}; return a; } int main() { int* a = func4(); printf("%d %d\n", a[0], a[1]); return 0; }
구조체로 할당된 메모리 공간을 리턴한다면 (메모리 복사를 통해) 믿을 수 있는 것 일까요?
func2() 함수는 그 함수 내부에서 구제체 s 를 정적할당했습니다. 그리고 이렇게 정적할당된 s 를 리턴했고요. 조금 변형된 함수로...
struct S func2_1(void) {
struct S s = {.a=1, .b=2};
printf("%d %p %d %p\n", s.a, &(s.a), s.b, &(s.b));
return s;
}
int main() {
struct S s = func2_1();
printf("%d %p %d %p\n", s.a, &(s.a), s.b, &(s.b));
return 0;
}
확인하면, main() 함수의 s 와 func2_1() 함수의 s 가 다른 것을 알 수 있습니다. func2_1() 함수 스택에 할당되었던 메모리 공간이 main() 함수로 복사되었습니다. 그럼, 안전하게 쓸 수 있다고 볼 수 있을 것 같습니다. 조금 다른 코드로...
struct S func5(struct S s) {
printf("%d %p %d %p\n", s.a, &(s.a), s.b, &(s.b));
return s;
}
int main() {
struct S s = func5(({
struct S s = (struct S){.a=1, .b=2};
printf("%d %p %d %p\n", s.a, &(s.a), s.b, &(s.b));
s;
}));
printf("%d %p %d %p\n", s.a, &(s.a), s.b, &(s.b));
return 0;
}
확인하면, main() 함수에서는 언제나 같은 s 를 쓴다는 것도 알 수 있었고요. 이것을 확인해보고 싶었는데... 어떻게 확인해야 할지 잘 떠오르지 않았었거든요.