C Language Basic

C Language [핵심정리] - 40

cCcode 2021. 7. 20. 18:48

1. 재귀함수 사용하기

함수에서 다시 동일한 함수호출하는 재귀함수를 만들어보겠습니다.

재귀호출 최대 개수 구하기
경고
결과

위 파일은 결과처럼 3990번 함수를 호출하다가 프로그램종료됩니다. 그 이유는 함수를 호출할 때 사용하는 스택이 넘쳐 스택 오버플로우(Stack Overflow)가 발생했기 때문입니다. 따라서 재귀호출을 사용할 때는 아래와 같이 반드시 종료조건을 만들어줘야 합니다.

종료 조건 만들기
결과

재귀함수를 통해 흔히들 아는 팩토리얼을 구현해보겠습니다. 혹시나 모르는 분들을 위해 팩토리얼이란 1부터 n까지의 숫자를 곱한 이며, 기호로는 ! 를 사용합니다. ex) 5! = 5 x 4 x 3 x 2 x 1 = 120

팩토리얼 구현
결과
설명

 

2. 함수 포인터 사용하기

C언어에서는 함수명으로 함수를 직접 호출합니다. 그렇다면 함수를 배열이나 구조체에 멤버 또는 요소로 넣거나, 함수를 매개변수로 넘겨준 후 반환값으로 함수를 가져올 수는 없을까요? 이를 대비하여 C언어에선 함수 포인터라는 걸 만들었습니다. 함수 포인터는 함수를 가리키는 포인터를 의미하며, 함수 포인터를 주거나 받고, 함수포인터로도 함수를 호출할 수 있습니다.

함수 포인터의 활용

함수 포인터로 함수를 호출할 수 있다고 했죠. 실제로 함수를 호출할 때 사용하는 함수명을 출력해보면 메모리 주소가 나와서함수명이 포인터의 일종임을 알 수 있습니다.

함수명의 주소
결과

함수 포인터는 먼저 함수의 반환 값 자료형을 지정해주고, 함수 포인터명 앞에 *(Asterisk)를 사용한 뒤 괄호로 묶어줍니다. 그리고 다시 괄호를 사용해 함수임을 명시해줍니다.

함수 포인터 선언
함수 포인터 사용 - 1
함수 포인터 사용 - 2
결과

다시 한번 말씀드리지만, 함수 포인터선언할 때는 함수 포인터와 저장될 함수 간의 반환 값 자료형, 매개변수 자료형과 개수가 일치해야 합니다. 함수의 메모리 주소를 함수 포인터에 저장할 때 함수명 뒤 괄호를 사용하면 함수호출되기 때문에 주의해야합니다. 저장한 함수를 호출하려면 단지 함수 포인터 자체를 호출하면 됩니다. 즉, 함수 포인터를 사용하면 자동차의 타이어를 교체하는 듯이 함수를 교체할 수 있습니다.

 

매개변수 없는 함수 포인터를 만들었으니 이제, 매개변수가 있는 함수 포인터를 만들어보겠습니다. 다음과 같이 반환 값 자료형을 지정해주고, 맨 뒤 괄호에 매개변수의 자료형을 지정해주면 됩니다. (매개변수명은 생략해도 되고 생략하지 않아도 됩니다. 저는 오히려 함수 포인터만 보고 해당 함수의 특징을 알 수 있다는 점 때문에 매개변수 명을 쓰는 걸 선호합니다.)

매개변수 있는 함수 포인터
매개변수 있는 함수 포인터 사용 - 1
매개변수 있는 함수 포인터 - 2
결과

 

3. 함수 포인터 활용하기

함수 포인터 선언할 때 포인터명 뒤에 대괄호를 사용하고 그 대괄호 안에 크기를 지정해서 함수 포인터 배열을 사용할 수 있습니다. 

함수 포인터 배열

#include <stdio.h>

int ADD(int a, int b)
{
	return a + b;
}
int SUB(int a, int b)
{
	return a - b;
}
int MUL(int a, int b)
{
	return a * b;
}
int DIV(int a, int b)
{
	return a / b;
}

int main()
{
	char operand;
	int num, num_2;
	int (*Fp[4])(int a, int b);

	Fp[0] = ADD;
	Fp[1] = SUB;
	Fp[2] = MUL;
	Fp[3] = DIV;

	printf("수행할 계산을 입력해주세요 : ");
	scanf_s("%d %c %d", &num, &operand, (unsigned char)sizeof(operand), &num_2);

	switch (operand)
	{
	case '+': printf("\nresult : %d\n", Fp[0](num, num_2)); break;
	case '-': printf("\nresult : %d\n", Fp[1](num, num_2)); break;
	case '*': printf("\nresult : %d\n", Fp[2](num, num_2)); break;
	case '/': printf("\nresult : %d\n", Fp[3](num, num_2)); break;
	default: return 0;
	}

	return 0;
}

결과

함수 포인터 배열 또한 중괄호를 사용해 선언과 동시에 초기화할 수 있습니다.

초기화

 이제 함수 포인터구조체 멤버로 사용해보겠습니다. 함수 포인터구조체 멤버로 사용하려면 구조체를 정의할 때 멤버로 지정해주면 되요.

구조체 멤버
구조체 멤버 함수 포인터
결과

함수 포인터매개변수로 사용하고 싶다면 함수의 매개변수 부분함수 포인터를 넣은 뒤 정의하면 됩니다.

매개변수로 사용
매개변수로 사용
결과

매개변수까지는 딱히 어렵다는 생각이 들지 않습니다. 하지만 반환값에 함수의 포인터를 사용할 경우에는 지금까지 사용할 문법과 달라지기 때문에 어렵다고 느껴질 수 있습니다.

반환값으로 사용
반환값으로 함수 포인터 사용
결과

조금 헷갈릴 수도 있으니 차근차근 프로그램의 흐름을 따라가봅시다.

1) 우선 Bring_MUL 함수를 호출합니다.

그러면 Bring_MUL 함수에서 MUL 함수의 주소반환하는 데 MUL 함수는 int 반환값 1개, int 전달값 2개 입니다. 그래서 가장 앞에 함수 포인터 반환값 자리에 int 가 들어가고, 해당 함수를 호출할 때는 매개변수빈 공간(void)가 들어왔습니다. 따라서 함수명 이후에 나오는 괄호에는 아무것도 명시하지 않아요. 마지막 괄호는 함수 포인터의 매개변수를 적는 부분인데, 해당 함수 포인터는 MUL 함수를 가리키기에 MUL 함수의 매개변수 int 자료형 2개를 명시해줍니다.

2) 반환된 MUL 함수에 10, 10 이라는 전달값전달한 후 결과를 반환받습니다.

3) 반환된 결과를 출력합니다.

+ Bring_MUL은 *(Asterisk)가 있지만 함수 포인터가 아닌 함수입니다.

 

만약 매개변수가 있는 함수에서 함수 포인터를 반환하는 방법은 다음과 같습니다.

매개변수 있는 반환값 함수 포인터
결과