c

함수와 배열 일부

haniru 2025. 11. 4. 21:58

1. 매개변수가 있는 함수

  • main 스택 프레임을 생성한다. 여기에 지역변수 a, b, result 값이 저장된다.
  • result = sum (a, b); 를 실행하기 전 sum 함수 호출 준비를 한다. cpu는 현재 다음에 실행해야 할 명령어의 주소(sum 함수가 끝난 후 돌아와야 할 result에 값을 대입하는 명령의 주소)를 복귀주소로 스택에 저장한다.
  •  sum 함수로 제어가 이동되어 main 프레임 위에 sum 함수를 위한 새로운 스택 프레임이 쌓인다. 전달된 인자(10, 20)은 sum 함수의 매개변수 x와 y에 할당된다. 함수 본체가 실행된 후 x+y의 결과 30이 계산된다.
  • return x + y; 명령이 실행되면 계산된 결과값 30을 반환값으로 레지스터에 저장한다. sum 함수는 스택에 저장했던 복귀주소를 프로그램 카운터 레지스터에 로드한다. sum 함수의 스택프레임은 소멸되어 스택에서 제거된다.
  • 프로그램 제어가 복귀 주소로 돌아와 main 함수가 중단되었던 지점부터 다시 실행된다.

함수 선언을 주석하면 에러가 난다(리눅스 환경인 gcc에서 에러가 난다)

 

1-1. 매개변수가 있는 함수 - char 형과 int 형

 

2. 매개 변수가 없는 함수: int get_num(void) 또는 int get_num()

호출스택의 과정 → 함수 내부에 있을때에는 호출스택에 쌓여있다가, 함수를 빠져나오면 스택에서 빠져나오는 것을 알 수 있음

main 스택 → get_num 스택 쌓이고, 빠지고  → myPrint 스택 쌓이고, 빠

 

 

3. 재귀함수 - 스택 메모리는 크기 제한이 있어(1MB ~ 8MB 라고 함) 이 공간이 완전히 차면 프로그램은 스택 프레임을 쌓을 수 없게 되고 이때 "스택 오버플로우" 오류가 발생한다. 이 현상은 운영체제 수준의 오류인 세그먼트 폴트나 프로그램 강제 종료로 이어진다. 재귀는 메모리 소모가 크고 예측이 어려워 성능과 안정성을 위해 반복문 사용이 권장되기도 한다.

종료 조건이 없는 무한 재귀 코드: 스택오버플로우 발생 가능
return 을 줘서 무한재귀가 발생하지 않도록. LIFO 라서 가장 최근에 호출된 함수가 작업을 마치고 먼저 반환되어야 프로그램 제어가 이전 함수로 돌아올 수 있음.
무한 재귀코드 2

더보기

코드 구조

C
 
int main() {
    recursive(0); // n=0부터 시작
    return 0;
}

int recursive(int n) {
    printf("number: %d\n", n);
    
    if (n == 10) {
        return 0; // 기저 조건: n이 10이 되면 반환 (재귀 종료)
    }

    // 재귀 호출
    recursive(n + 1); 
    
    // 10 올라오고 스택이 빠지기 때문에 9, 8, 7, ... 이렇게 빠짐
}

호출 스택 (Call Stack)의 LIFO 동작 시뮬레이션

단계 실행 내용 (함수 호출) 스택 변화 (LIFO) 설명
1 main 호출 [main] main 스택 프레임이 가장 먼저 쌓임.
2 recursive(0) 호출 [main] <- [n=0] n=0 프레임이 쌓이고, "number: 0" 출력.
3 recursive(1) 호출 [main] <- [n=0] <- [n=1] n=1 프레임이 쌓이고, "number: 1" 출력.
... ... ... 이 과정이 n=10이 될 때까지 반복됨.
12 recursive(10) 호출 ... <- [n=9] <- [n=10] n=10 프레임이 쌓이고, "number: 10" 출력.
13 n == 10 (return 0) [n=10] 프레임이 제거됨 (Pop). 가장 나중에 들어온 n=10이 가장 먼저 나감. (LIFO)
14 복귀 (제어권 n=9로) ... <- [n=9] n=9 프레임이 실행을 재개. (하지만 더 이상 코드가 없어 즉시 종료)
15 n=9 종료 [n=9] 프레임이 제거됨 (Pop). n=9가 n=10 다음에 나감.
... ... ... n=8, 7, 6, ... 순서로 순차적으로 스택에서 빠져나감.
23 n=0 종료 [main] n=0 프레임이 가장 마지막에 나감.
24 main 종료 [ ] main 프레임이 제거되고 프로그램 종료.

 

 

재귀 함수가 헷갈려서 정리했는데, 틀린게 있을수도 있다.

더보기

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

int fruit(int n);

int main() {

fruit(1);

return 0;

}

int fruit(int count) {

printf("apple: %d\n", count);

if (count == 3)

return 0; // 함수를 끝낸다

fruit(count + 1);

printf("jam\n");

}

 

1. 스택 쌓기 (재귀 호출)

가장 깊은 호출이 발생할 때까지 스택에 쌓이는 과정

호출 count 값 출력 다음 행동
호출 count 값 출력 다음 행동
fruit(1) 1 apple: 1 fruit(2) 호출 (스택에 대기)
fruit(2) 2 apple: 2 fruit(3) 호출 (스택에 대기)
fruit(3) 3 apple: 3 if (count == 3) 조건 만족, return 0; 실행. => 제어권이 fruit(2)로 복귀.

2. 스택 풀기 (복귀 및 나머지 코드 실행)

fruit(3)이 종료된 후, 제어권이 이전 함수로 돌아가 나머지 코드를 순차적으로 실행

복귀 재개 함수 fruit 내부 재개 지점 출력 설명
복귀 재개 함수 fruit 내부 재개 지점 출력 설명
1차 복귀 fruit(2) fruit(3) 호출 직후 jam fruit(3) 종료 후, printf("jam\\n"); 실행. fruit(2) 종료.
2차 복귀 fruit(1) fruit(2) 호출 직후 jam fruit(2) 종료 후, printf("jam\\n"); 실행. fruit(1) 종료.

 

 

4. 배열 - 배열의 이름은 그 배열의 첫번째 요소의 메모리 주소(번지)를 나타낸다. 따라서 배열 이름은 상수형 포인터(const pointer) 처럼 동작한다.

  • 배열의 이름(arr) 자체는 arr[0]의 주소 즉 &arr[0]과 동일한 값을 가진다
  • 배열 이름이 포인터처럼 주소값을 가지더라도 이 주소값 자체를 변경할 수는 없다. 즉 arr = arr + 1; 과 같은 코드는 허용되지 않는다 (일반적인 변수형 포인터와의 가장 큰 차이라고는 한다.)
  • 배열 요소를 포인터로 접근하는 방법 - 배열 접근 방식(arr[i]), 포인터 접근 방식(*(arr + i))은 컴파일러 수준에서 동일하게 처리된다

 

5. 그 외 함수를 이용한 코딩

사칙연산 함수

 

평균을 구하는 함수

'c' 카테고리의 다른 글

포인터  (0) 2025.11.06
배열  (0) 2025.11.05
반복문 (while, for)  (0) 2025.11.03
c 언어  (0) 2025.11.02
C 언어 - 연산자, 조건문(1)  (0) 2025.10.30