Coding/C

[C언어] 함수 가변인자, 재귀호출 - 코딩도장

GunP4ng 2024. 2. 10. 22:47

[코딩도장] Unit 66 ~ 67


1. 함수 가변인자

1. 함수에서 가변인자 사용하기

함수의 들어가는 인수(argument)의 개수가 변하는 것을

가변 인자(variable argument)라고 한다

함수에서 가변 인자를 정의할 때는 고정 매개변수가 한 개 이상 있어야 한다

고정 매개변수 뒤에 ... 를 붙여 가변 인자를 정의할 수 있다

반환값자료형 함수이름(자료형 고정매개변수, ...)
{
}

 

... 으로 들어온 가변 인자를 사용하려면 <stdarg.h> 헤더 파일을 사용해야 한다

stdarg.h 에 정의된 가변 인자 처리 매크로는 아래와 같다

  • va_list 가변인자 목록(가변 인자의 메모리 주소를 저장하는 포인터)
  • va_start 가변 인자를 가져올 수 있도록 포인터를 설정
  • va_arg 가변 인자 포인터에서 특정 자료형크기만큼 값을 가져옴
  • va_end 가변 인자 처리가 끝났을 때 포인터를 NULL로 초기화

 

void printNumbers(int args, ...)    // 가변 인자의 개수를 받음, ...로 가변 인자 설정

매개변수에서 인자의 개수를 받을 수 있도록 지정하고

두 번째 매개변수 부분에서 가변 인자를 받을 수 있도록 ...으로 지정한다

 

int main()
{
    printNumbers(1, 10);                // 인수 개수 1개
    printNumbers(2, 10, 20);            // 인수 개수 2개
    printNumbers(3, 10, 20, 30);        // 인수 개수 3개
    printNumbers(4, 10, 20, 30, 40);    // 인수 개수 4개

    return 0;
}

printNumbers 함수를 호출할 때는 인수 개수를 넣어준 뒤

인수 개수에 맞게 인수를 콤마로 구분하여 넣어준다

 

va_list ap;    // 가변 인자 목록 포인터

va_start(ap, args);    // 가변 인자 목록 포인터 설정

printNumbers 함수 안에서

va_start 매크로에 가변 인자 포인터 ap 와 가변인자 개수 args 를 넣어준다

 

va_start 로 ap 준비

가변 인자 4개가 들어있는 printNumbers(4, 10, 20, 30, 40); 를 호출한 뒤

va_start 매크로를 실행하면 위와 같은 모양이 된다

 

for (int i = 0; i < args; i++)    // 가변 인자 개수만큼 반복
{
    int num = va_arg(ap, int);    // int 크기만큼 가변 인자 목록 포인터에서 값을 가져옴
                                  // ap를 int 크기만큼 순방향으로 이동
    printf("%d ", num);           // 가변 인자 값 출력
}

반복문으로 가변 인자 개수만큼 반복하면서 va_arg 매크로로 값을 가져온다

va_arg 에는 가변 인자의 자료형을 지정해준다

 

va_arg 에서 ap로 값을 가져온 뒤 포인터 이동

반복문에서 반복할 때마다 ap 는 4바이트씩 순방향 이동하므로

10, 20, 30, 40을 순서대로 가져올 수 있다

 

va_end(ap);    // 가변 인자 목록 포인터를 NULL로 초기화

마지막으로 va_end 매크로를 사용하여 ap 를 NULL로 초기화 한다

 

 

2. 함수 재귀호출

1. 함수에서 재귀호출 사용하기

함수 안에서 자기자신을 호출하는 방식을 재귀호출(recursive call)이라고 한다

void hello()
{
    printf("Hello, world!\n");

    hello();    // hello 함수 안에서 hello 함수 호출
}

int main()
{
    hello();    // hello 함수 최초 호출

    return 0;
}

hello 함수 안에서 다시 hello 함수를 호출하는 코드이다

코드를 실행하면 hello 함수가 자기 자신을 계속 호출하다가

스택이 넘쳐 스택 오버플로우(stack overflow)에러가 발생한다

 

재귀호출과 스택 넘침 현상

※ 재귀호출을 사용하려면 반드시 종료 조건을 만들어주어야 한다

 

void hello(int count)
{
    if (count == 0)    // count가 0이면 다시 hello 함수를 호출하지 않고 끝냄
        return;

    printf("Hello, world! %d\n", count);

    hello(--count);    // count를 감소시켜서 다시 hello에 넣음
}

함수의 반복 횟수를 계산하기 위해 매개변수 count를 넣어준다

count 가 0이 되면 함수를 종료한다

 

hello(5);    // hello 함수 호출

main 함수에서는 hello 함수를 반복할 횟수를 넣어서 호출한다

 

재귀호출에서 종료 조건을 지정

 

 

3. 심사문제

66.6 가변 인자의 합 구하기

표준 입력으로 정수 두 개와 세 개가 입력됩니다. 다음 소스 코드를 완성하여 입력된 정수 두 개의 합과 세 개의 합이 출력되게 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdarg.h>

int sum(int args, ...)
{
    ____________________
    ____________________

    ____________________
    ____________________
    ____________________
    ____________________
    ____________________
    ____________________
    ____________________

    ____________________
}

int main()
{
    int num1, num2;
    int num3, num4, num5;

    scanf("%d %d", &num1, &num2);
    scanf("%d %d %d", &num3, &num4, &num5);

    printf("%d\n", sum(2, num1, num2));
    printf("%d\n", sum(3, num3, num4, num5));

    return 0;
}

 

정답

va_list ap;
int result = 0;

va_start(ap, args);
for (int i = 0; i < args; i++) {
    result += va_arg(ap, int);
}
va_end(ap);

return result;

va_start 매크로로 가변 인자 목록 포인터를 사용한다

반복문을 사용하여 va_arg 매크로로 인자를 가져온 뒤

result 에 계속 더한다


66.7 가변 인자의 정수 합 구하기

표준 입력으로 정수 세 개가 입력됩니다. 다음 소스 코드를 완성하여 getSum 함수에 들어간 가변 인자 중에서 정수의 합을 구하도록 만드세요.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

  • int : i
  • double : d
  • char * : s
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdarg.h>

__________________________
__________________________
__________________________
__________________________
__________________________

__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________
__________________________

__________________________
__________________________
__________________________

__________________________

int main()
{
    int num1, num2, num3;

    scanf("%d %d %d", &num1, &num2, &num3);

    printf("%d\n", getSum("isi", num1, "C", 10));
    printf("%d\n", getSum("sdsi", "Hello, world!", 5.3, "A", num2));
    printf("%d\n", getSum("iiss", 25, 38, "k", "R"));
    printf("%d\n", getSum("sidii", "Hello, C", num3, 2.234567, 878, 1291));

    return 0;
}

 

정답

int getSum(char *types, ...) {
    va_list ap;
    int i = 0;
    int result = 0;

    va_start(ap, types);
    while(types[i] != '\0') {
        switch (types[i]) {
            case 'i' :
                result += va_arg(ap, int);
                break;
            
            case 'd' :
                va_arg(ap, double);
                break;
            
            case 's' :
                va_arg(ap, char *);
                break;
        }
        i++;
    }
    va_end(ap);

    return result;
}

정수 이외의 자료형은 합을 구하지 않는다고 해서

va_arg 매크로를 사용하지 않으면가변 인자 포인터 ap 가

다음 인자로 이동하지 않는다


67.6 재귀호출로 피보나치 수 구하기

표준 입력으로 정수 한 개가 입력됩니다(입력 값의 범위는 10~30) 다음 소스 코드를 완성하여 입력된 정수에 해당하는 피보나치 수가 출력되게 만드세요.

피보나치 수는 0과 1로 시작하며, 다음 번 피보나치 수는 바로 앞의 두 피보나치 수의 합입니다.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

_________________________
_________________________
_________________________
_________________________
_________________________
_________________________
_________________________

int main()
{
    int num1;

    scanf("%d", &num1);

    printf("%d\n", fib(num1));

    return 0;
}

 

정답

int fib(int n) {
    if (n <= 1) {
        return n;
    }
    return fib(n - 1) + fib(n - 2);
}

67.7 재귀호출로 1부터 n까지 합 구하기

표준 입력으로 정수 한 개가 입력됩니다.(입력 값의 범위는 3~100) 다음 소스 코드를 완성하여 1부터 입력된 정수까지의 합이 출력되게 만드세요. 단, 재귀호출을 사용해야 합니다.

정답에는 밑줄 친 부분에 들어갈 코드만 작성해야 합니다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int sum(int n)
{
    if (n == 1)
        return 1;

    return _______________;
}

int main()
{
    int n = 0;

    scanf("%d", &n);

    printf("%d\n", sum(n));

    return 0;
}

 

정답

n + sum(n-1)