C 관련 포스팅 목록
2021.12.06 - [C/응용] - C/C++ 함수 포인터 변수 활용 예제 - 1
2021.12.19 - [C/stdio] - C 파일 오프셋 위치 이동 예제 - 2(stdio/fseek/SEEK_END)
2021.12.18 - [C/stdio] - C 파일 오프셋 위치 이동 예제 - 1(stdio/fseek/SEEK_SET)
2021.12.05 - [C/stdio] - C 파일 실시간 쓰기 예제(stdio/fflush)
2021.12.01 - [C/stdio] - C 파일 생성 및 스트림 열기 예제(stdio/fopen)
C 구조체 멤버 변수로 함수 포인터 변수를 사용하는 이유
안녕하세요.
오늘은 이전 포스팅에서 다뤘던 함수 포인터 활용 심화 예제를 알아보도록 하겠습니다. 포인터는 곧 주소이고 컴퓨터도 사람처럼 각 기능별로 주소를 가진다고 포스팅하였습니다. 그리고 기본적인 함수 포인터 변수 선언 방법과 값 대입, 호출 방법에 대해 알아보았습니다.
함수 포인터 변수는 변수에 저장될 함수의 원형과 동일한 형식으로 선언되어야 했습니다. 즉 함수가 반환형이 void이고 인자가 없다면 함수 포인터 변수도 void에 인자가 없어야 합니다.
이게 이전에 배웠던 내용이었습니다.
오늘은 함수 포인터를 자주 사용하는 예제에 대해 알아보겠습니다. 구조체에 대해 아시나요? C에서 구조체는 뺄 수 없는 필수 키워드입니다. 변수를 연속적으로 붙이고 하나의 변수로 멤버로 접근하여 사용하는 구조체에 함수 포인터 변수가 자주 사용됩니다.
객체 지향 언어인 JAVA에서 클래스가 있고 클래스 안에 멤버 변수, 메소드 등이 있습니다. 만약 동물(Animal) 이란 클래스가 있고 울다(cry)라는 멤버 메소드가 있다고 생각해봅시다. 동물 클래스를 상속받은 새 객체도 cry 메소드를 가지고 있고 개구리 객체도 똑같이 동물 클래스를 상속받아 cry 메소드가 있습니다. 그리고 이 cry 메소드는 오버라이딩 하여 우는 소리가 다르게 구현할 수 있습니다.
C에서 이런 것들이 가능할까요?C는 절차 지향 언어로서 반드시 함수를 호출하기 전 위에 함수 원형이 선언되어있어야 합니다. 따라서 A 함수는 반듯 A기능을 해야하고 B 함수는 B 기능을 해야 합니다.
객체 지향 언어는 같은 클래스를 상속받아 다른 특성으로 구현이 가능한 장점이 있지만절차 지향 언어는 하나의 함수는 하나의 기능만 해야 합니다.
그런데 함수 포인터를 활용하면 이런 단점을 약간이나마 극복할 수 있습니다. 오늘은 구조체 변수를 통해 객체 지향 언어처럼 사용할 수 있는 방법을 알아보도록 하겠습니다.
만약 포인터와 함수 포인터에 대해 아직 잘 모르시는 분은 아래 포스팅을 참고하시면 빨리 이해하실 수 있습니다.
2021.12.06 - [C/응용] - C/C++ 함수 포인터 변수 활용 예제 - 1
두 동물이 우는 함수를 선언
#include <stdio.h>
#include <string.h>
void cry_rabbit() {
printf("으아악! 난 토끼다\n");
}
void cry_dog() {
printf("어흥!! 난 개다\n");
}
void main() {
}
라인 설명
4-6: 토끼가 울었을 때 출력할 함수입니다. 나중에 함수 포인터를 통해 토끼 구조체 변수가 호출할 함수입니다.
8-10: 개가 울었을 때 출력할 함수입니다.
위의 코드는 실행해도 아무것도 호출되지 않습니다. 자 이제 이 함수를 실제 어떻게 호출할까요? 확인해보겠습니다.
동물 구조체 선언
#include <stdio.h>
#include <string.h>
typedef struct {
void (*cry)();
} animal_t;
...
...
라인 설명
4: cry 함수 포인터 멤버로 가진 animal 구조체 타입을 선언합니다. 이제 animal이라는 타입 정의로 인해 여러 동물들 구조체를 쉽게 선언할 수 있습니다.
토끼, 개 구조체를 선언하고 cry 함수 포인터 멤버 변수에 각 맞는 함수를 사용
#include <stdio.h>
#include <string.h>
typedef struct {
void (*cry)();
} animal_t;
void cry_rabbit() {
printf("으아악! 난 토끼다\n");
}
void cry_dog() {
printf("어흥!! 난 개다\n");
}
void main() {
animal_t rabbit, dog;
rabbit.cry = cry_rabbit;
dog.cry = cry_dog;
rabbit.cry();
dog.cry();
}
라인 설명
17: animal_t 타입의 rabbit, dog 변수를 선언합니다.
19: rabbit 변수의 멤버 필드인 cry에는 위에서 만든 cry_rabbit 함수 주소를 지정합니다.
20: dog 변수의 멤버 필드인 cry에는 cry_dog 함수 주소를 지정합니다.
22-23: 각 cry 함수를 호출합니다.
결국 결과는 각 cry 함수 포인터가 가리키는 함수가 호출된 것을 아래의 결과를 보고 알 수 있었습니다.
마무리
오늘은 이전 포스팅에서 다룬 함수 포인터에 대해 심화 내용을 알아보았습니다. 오늘 알아본 예제와 같이 JAVA와 같은 객체 지향 언어 스타일을 그나마 비슷하게 따라 해 볼 수 있었습니다. 이런 방식의 사용이 좋은 것은 아니지만 개발을 편리하게 하고 효율적으로 사용할 수 있습니다.
다음 포스팅에선 함수 포인터의 예제 마지막으로 위에서 배운 예제를 조금 더 객체 지향 언어 스타일로 발전시키는 방법에 대해 알아보도록 하겠습니다.
C 관련 포스팅 목록
2021.12.06 - [C/응용] - C/C++ 함수 포인터 변수 활용 예제 - 1
2021.12.19 - [C/stdio] - C 파일 오프셋 위치 이동 예제 - 2(stdio/fseek/SEEK_END)
2021.12.18 - [C/stdio] - C 파일 오프셋 위치 이동 예제 - 1(stdio/fseek/SEEK_SET)