본문 바로가기

윤성우의 C Programming

C언어에서의 지역 변수와 스택 메모리

 

당신이 이태원의 아주 바쁜 식당에서 음식을 만드는 알바생이라고 생각해보자.

CPU라는 쉐프가 복잡한 식당의 주방에서 주문서를 받고 당신은 절차에 따라 음식을 만든다.  

 

각 주문서는 손님의 취향에 따라 메뉴가 제각각이고 한 요리가 테이블에 서빙되면 주문서는 쓰레기통에 버려진다. 

이는 마치 주문서가 지역변수이고, 함수가 완료되면 메모리 할당이 해제되면서 함수가 끝날 때 지역 변수가 폐기되는 것과 같다. 

 

 
바쁜 CPU와 여러 지역 변수들
C언어에서 함수 내의 지역 변수는 함수가 호출되는 동안에만 존재하고 함수가 반환되면 소멸된다. 
함수가 종료되면 해당 함수에 할당되었던 스택 메모리는 해제되어 다른 목적으로 재사용될 수 있다. 
 
다시 식당 알바생으로 돌아가자. 5명의 손님이 3번 테이블에서 재미있게 이야기를 나누다가 식사가 끝나고 주문서에 맞게 계산을 하고 나갔다. 알바생인 당신이 손님이 갔구나라고 생각하고 있었는데 갑자기 5명이 우르르 들어와 사실 담배피러 나간거였다고 들어오면 알바생으로서 당황하지 않겠는가?
 
아직 남은 음식을 치우지 않았다면 줄 수 있을 것이고, 남은 음식을 이미 치우고 3번 테이블에 다른 손님들이 이미 앉아있다면, 음식을 다시 대접할 순 없을 것이다. 지역 변수의 메모리를 다시 참조할 때도 같은 맥락이다. 
 
 
즉, 지역 변수의 메모리가 해제되었다고 해서 즉시 물리적인 메모리에서 데이터가 사라지는 것은 아니다. 기존 데이터는 여전히 메모리에 남아 있을 수 있지만, 프로그램의 실행 흐름 상에서는 더 이상 그 메모리 영역을 유효한 데이터로 간주하지 않는다. (테이블에 남은 음식을 치우는 것처럼) 
 

함수에서 반환된 지역 변수의 메모리를 다시 참조하게 되면, 운영체제 또는 C 런타임이 그 영역을 '쓰레기 값(garbage value)'으로 간주한다. 이는 그 영역이 공식적으로는 비어 있으며 언제든지 다른 데이터로 덮어 쓰여질 수 있다는 것을 의미한다. 만약 이전에 함수에서 사용하던 메모리 영역이 아직 다른 용도로 사용되지 않아서 원래 값이 남아 있는 경우, 그 값을 읽었을 때 우연히 '올바른' 값으로 보일 수 있다. 하지만 이는 운이 좋은 경우일 뿐이며, 안정적이거나 예측 가능한 프로그래밍 방식이 아니다.

 

 

 

이런 현상은 특히 반환된 포인터가 가리키는 메모리에 접근할 때 두드러진다. 포인터가 함수의 지역 변수를 가리킬 때, 그 함수가 끝나고 나면 포인터는 '댕글링 포인터(dangling pointer)'가 된다. 즉, 유효하지 않은 메모리 영역을 가리키게 되는 것이다. 이러한 포인터를 통해 메모리에 접근하는 것은 '정의되지 않은 동작(undefined behavior)'을 초래하며, 프로그램의 오류나 보안 취약점을 일으킬 수 있다.

 

따라서, 지역 변수나 그 메모리를 반환하는 것은 위험하고 피해야 하는 행위이다. 필요한 값이 있으면 항상 그 값을 복사하여 반환하거나, 더 안전하게 관리할 수 있는 malloc 같은 메모리(예: 동적 할당된 메모리)에 값을 저장해야 한다.

 

<예시>

#include <stdio.h>

int* returnLocalVariableAddress() {
    int localVariable = 5;
    printf("Local variable inside function: %d\n", localVariable);
    return &localVariable; // 경고: 함수가 끝난 후 지역 변수의 주소를 반환
}

int main() {
    int* ptr = returnLocalVariableAddress();
    // 함수 반환 후, ptr은 해제된 스택 메모리를 가리킴
    printf("Value at returned address: %d\n", *ptr); // 정의되지 않은 동작 (Undefined Behavior)
    return 0;
}

.

<결과는 A 혹은 B가 될 수 있다.> 

 

결과 A

Local variable inside function: 5
Value at returned address: 5

 

결과 B

Local variable inside function: 5
Value at returned address: 쓰레기 값 (또는 예측할 수 없는 값)

 

  • returnLocalVariableAddress 함수는 지역 변수 localVariable의 주소를 반환한다. 이 주소는 함수가 종료되면서 해제되는 스택 메모리를 가리키게 된다.
  • main 함수에서는 이 주소를 포인터 ptr을 통해 참조한다. 하지만, 이 시점에서 localVariable의 메모리는 공식적으로 해제되어 재사용될 수 있는 상태이다. 따라서 ptr을 통해 접근하는 것은 정의되지 않은 동작(Undefined Behavior)을 초래한다.
  • 실행 결과는 컴파일러와 실행 환경에 따라 달라질 수 있다. 때로는 운 좋게도 해제된 메모리에 남아 있던 값이 그대로 출력될 수도 있지만, 이는 안정적이거나 보장된 동작이 아니다. 대부분의 경우, 쓰레기 값이 출력되거나 프로그램이 예상치 못하게 동작할 수 있다.

이 예제는 함수에서 지역 변수의 주소를 반환하는 것이 왜 위험한지를 명확하게 보여준다. 안전하고 예측 가능한 프로그래밍을 위해서는 지역 변수의 값을 필요로 할 때 해당 값을 복사하여 반환하는 방식을 사용해야 한다.