C언어 학습 노트 : 12. 함수 응용#

1. 매개변수 전달 방식#

  • 32비트 환경(x86)

    • 대부분의 ABI에서 스택을 통해 인자를 전달한다.
    • 오른쪽에서 왼쪽 순서로 스택에 푸시(push)된다.
    • 호출자(caller)가 스택을 정리하는 규약(cdecl 등)과 피호출자(callee)가 정리하는 규약(stdcall 등)이 있다.
  • 64비트 환경(x86-64, SysV, Windows)

    • 주요 인자는 레지스터로 전달된다.

      • SysV (리눅스): RDI, RSI, RDX, RCX, R8, R9 → 이후는 스택
      • MS ABI (윈도우): RCX, RDX, R8, R9 → 이후는 스택
    • 나머지는 스택에 전달된다.


2. Call by Value (값 호출)#

  • 함수에 값의 복사본을 전달한다.
  • 함수 내부에서 값이 변경되어도 원본에는 영향을 주지 않는다.

3. Call by Reference (참조 호출)#

  • C 언어는 직접적인 참조 호출을 지원하지 않으며, 포인터를 전달하는 방식으로 구현한다.
  • 주소를 넘겨줌으로써 함수 내부에서 원본 데이터를 수정할 수 있다.
  • 설계 시 메모리 관리(수명, 해제 책임 등)를 명확히 고려해야 한다.

4. 재귀 호출#

  • 함수가 자기 자신을 호출하는 구조.
  • 기저 조건(base case) 을 반드시 명시하여 무한 재귀에 빠지지 않도록 해야 한다.
  • 재귀 호출은 스택 프레임을 계속 쌓기 때문에, 깊이가 깊으면 스택 오버플로우가 발생한다.
  • 따라서 입력 크기와 재귀 깊이를 고려해야 한다.

5. 스택 프레임과 지역 변수 주소 반환#

  • 함수 호출 시 스택 프레임에는 반환 주소, 매개변수, 지역 변수가 저장된다.
  • 함수가 종료되면 스택 프레임은 해제(pop)되며, 지역 변수의 주소는 더 이상 유효하지 않다.
  • 따라서 지역 변수의 주소를 반환하면 정의되지 않은 동작(UB) 이 발생한다.
  • 스택 메모리는 해제되지만 값이 즉시 덮어씌워지는 것은 아니므로, “쓰레기 값"이 남아 보이는 문제가 생길 수 있다.

6. 메모리 동적 할당과 관리 이슈#

  • malloc으로 메모리를 할당하고 free로 해제하는 책임을 누가 가지는지 호출자와 피호출자 간 명확히 정의해야 한다.
  • 함수가 메모리를 할당하여 반환한다면, 이를 받은 쪽에서 해제해야 한다.
  • 반대로 호출자가 메모리를 할당해 전달하면, 함수는 그 안에서만 사용하고 해제는 하지 않는다.
  • 문서화명확한 인터페이스 정의가 필수적이다.
  • 또한 메모리 크기를 알 수 없는 경우 사고가 발생할 수 있으므로, 크기도 함께 전달하는 것이 안전하다.

7. 스택 프레임 손상 문제#

  • C 언어에서는 버퍼 오버플로우가 발생할 수 있다.

  • 지역 배열에 크기보다 더 큰 데이터를 저장하면, 스택에 이미 존재하는 다른 데이터(반환 주소 등)를 덮어쓸 수 있다.

  • 이를 스택 프레임 손상(stack smashing) 이라 하며, 보안 취약점으로 악용될 수 있다.

  • 방지 기법:

    • 안전한 함수 사용 (strcpy 대신 strncpy 등)
    • 컴파일러 옵션 (Stack Protector, ASLR)
    • 정적 분석/런타임 검사 도구 활용

학습 포인트 정리#

  • 32비트는 스택 기반, 64비트는 레지스터 + 스택 혼합 기반으로 매개변수를 전달한다.
  • 값 호출은 복사본을, 참조 호출은 포인터를 통해 원본을 수정한다.
  • 재귀 호출은 기저 조건이 필수이며, 깊은 호출은 스택 오버플로우 위험을 가진다.
  • 지역 변수 주소 반환은 금지된다.
  • 동적 메모리 관리에서 할당/해제 책임은 반드시 문서화해야 한다.
  • 버퍼 오버플로우는 스택 프레임을 손상시키므로 항상 주의해야 한다.