강의 시간에 배운 함수포인터를 활용해 운동선수 정렬 프로그램을 C언어로 짜보자
player.c(선수 print)와 player.h(데이터 타입)으로 나누었다. main.c에는 정렬알고리즘과 선수정보를 하드코딩했다.
player.h
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include <stdio.h>
#include <stdint.h>
#define MAX_PLAYER (10)
typedef struct _player_t {
char name[64]; // 이름
double avg; //타율
int r; // 득점
int hit; // 안타
int hr // 홈런
} player_t;
void print_all_player(char* title, player_t* player);
void print_player(player_t* player);
#endif
player.c
#include "player.h"
static void print_line(); // 이 파일에서만 쓸 수 있게 정적 선언
void print_player(player_t* player){
printf("%-10s", player->name);
printf("%8.3f", player->avg);
printf("%6d", player->r);
printf("%6d", player->hit);
printf("%6d", player->hr);
printf("\r\n");
}
static void print_line(){
printf("-----------------------------------\r\n");
}
void print_all_player(char* title, player_t* player){
printf(" %s\r\n", title);
print_line();
for(int i = 0; i < MAX_PLAYER; i++){
print_player(&player[i]);
}
print_line();
}
static void print_line();
이 함수는 player.c안에서만 사용하므로 정적 선언을 해 주었다.
main.c
#include "player.h"
void sort_avg(player_t* dst, const player_t* src, const int count){
player_t temp;
memcpy(dst, src, sizeof(player_t) * count);
for(int i = 0; i < count - 1; i++){ // 버블정렬
for(int j = 0; j < count - 1 - j; j++){
if(dst[j].avg > dst[j + 1].avg){
temp = dst[j];
dst[j] = dst[j + 1];
dst[j + 1] = temp;
}
}
}
}
int main(){
player_t players[MAX_PLAYER] = {
{"레이예스", 0.355, 78, 176, 14},
{"에레디아", 0.351, 68, 168, 15},
{"김도영", 0.344, 127, 168, 35},
{"송성문", 0.343, 75, 158, 17},
{"박민우", 0.336, 70, 137, 7},
{"로하스", 0.331, 99, 172, 30},
{"도슨", 0.330, 69, 126, 11},
{"김혜성", 0.322, 76, 145, 11},
{"허경민", 0.321, 67, 125, 7},
{"구자욱", 0.320, 79, 144, 26}
};
player_t sorted_players[MAX_PLAYER];
sort_avg(sorted_players, players, MAX_PLAYER);
print_all_player("avg로 선수 정렬", sorted_players);
return (0);
}

내림차순을 해봤으니 오름차순도 해보자
if(dst[j].avg > dst[j + 1].avg) 여기에서 부등호만 바꾸면 되는데 함수가 2개 늘어난다 --> enum parameter를 추가해주자
들어오는 파라미터가 의미가 있을 경우 enum을 사용해서 타입을 만들면 코드 안전성이 올라감
오류가 생길 여지(범위 이외의 값)를 만들지 말기!!
추가로 avg뿐만 아니라 hit, hr, r도 비교해 보자 --> function pointer 이용(sort_avg를 sort로 바꾸기)
player.h와 player.c는 같고 sort.h, sort.c 만들고 main.c에 코드를 추가한다.
sort.h
#ifndef __SORT_H__
#define __SORT_H__
#include "player.h"
typedef enum _order_t {
asc, // 오름차순
desc // 내림차순
} order_t;
void sort(player_t* dst, const player_t* src, const int count, order_t order, int (*fp_compare)(player_t* a, player_t* b));
//sort()에 인자 추가
#endif
sort.c
#include "sort.h"
static void swap(player_t* a, player_t* b);
static void swap(player_t* a, player_t* b){
player_t tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
void sort(player_t* dst, const player_t* src, const int count, order_t order, int (*fp_compare)(player_t* a, player_t* b)) {
if (fp_compare == NULL) {
printf("정렬할 함수를 인자로 넣어줘야 합니다.\r\n");
return;
}
player_t temp;
memcpy(dst, src, sizeof(player_t) * count);
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
if (order == asc) {
if (fp_compare(&dst[j], &dst[j + 1]) > 0) {
swap(&dst[j], &dst[j + 1]);
}
}
else if (order == desc) {
if (fp_compare(&dst[j], &dst[j + 1]) < 0) {
swap(&dst[j + 1], &dst[j]);
}
}
}
}
}
main.c
#include "player.h"
#include "sort.h"
int compare_avg(player_t* a, player_t* b);
int compare_r (player_t* a, player_t* b);
int compare_hit (player_t* a, player_t* b);
int compare_hr (player_t* a, player_t* b);
int compare_avg(player_t* a, player_t* b) {
if (a->avg > b->avg) {
return 1;
}
else return -1;
}
int compare_r(player_t* a, player_t* b) {
if (a->r > b->r) {
return 1;
}
else return -1;
}
int compare_hit(player_t* a, player_t* b) {
if (a->hit > b->hit) {
return 1;
}
else return -1;
}
int compare_hr(player_t* a, player_t* b) {
if (a->hr > b->hr) {
return 1;
}
else return -1;
}
int main(){
player_t players[MAX_PLAYER] = {
{"레이예스", 0.355, 78, 176, 14},
{"에레디아", 0.351, 68, 168, 15},
{"김도영", 0.344, 127, 168, 35},
{"송성문", 0.343, 75, 158, 17},
{"박민우", 0.336, 70, 137, 7},
{"로하스", 0.331, 99, 172, 30},
{"도슨", 0.330, 69, 126, 11},
{"김혜성", 0.322, 76, 145, 11},
{"허경민", 0.321, 67, 125, 7},
{"구자욱", 0.320, 79, 144, 26}
};
player_t sorted_players[MAX_PLAYER];
sort(sorted_players, players, MAX_PLAYER, desc, compare_hit);
print_all_player("hit로 선수 정렬(내림차순)", sorted_players);
return (0);
}
int (fp_compare)(player_t a, player_t b)에
int compare_avg(player_t a, player_t b);
int compare_r (player_t a, player_t b);
int compare_hit (player_t a, player_t b);
int compare_hr (player_t a, player_t* b);
를 대입해 각각 다른 데이터를 비교한다.(전략패턴)
이번에는 야구선수 뿐만 아니라 축구선수도 받아보자 --> void*포인터 이용
player_t라는 타입을 더이상 이용하지 못하므로 player_t를 void*로 받아야 한다.
player.h
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define MAX_PLAYER (10)
typedef enum _player_type_t {
baseball,
soccer
} player_type_t ;
typedef struct _baseball_player_t {
char name[64];
double avg; // 타율
int r; // 득점
int hit; // 안타
int hr; // 홈런
} baseball_player_t;
typedef struct _soccer_player_t {
char name[64];
int goal; // 골
int assist; // 도움
int point; // 공격포인트
} soccer_player_t;
void print_all_player(char* title, void* player, player_type_t type);
void print_player(void* player, player_type_t type);
#endif
player_type_t라는 enum을 추가하고 player_t를 baseball_player_t, soccer_player_t 로 대체했다.
player.c
#include "player.h"
static void print_line(); // 이 파일에서만 쓸 수 있게 정적 선언
void print_player(void* player, player_type_t type){
if(type == baseball){
baseball_player_t* b_player = (baseball_player_t*)player;
printf("%-10s", b_player->name);
printf("%8.3f", b_player->avg);
printf("%6d", b_player->r);
printf("%6d", b_player->hit);
printf("%6d", b_player->hr);
printf("\r\n");
}
else if(type == soccer){
soccer_player_t* s_player = (soccer_player_t*)player;
printf("%-10s", s_player->name);
printf("%6d", s_player->goal);
printf("%6d", s_player->assist);
printf("%6d", s_player->point);
printf("\r\n");
}
}
static void print_line(){
printf("-----------------------------------\r\n");
}
void print_all_player(char* title, void* player, player_type_t type){
printf(" %s\r\n", title);
print_line();
if (type == baseball) {
baseball_player_t* b_player = (baseball_player_t*)player;
for(int i = 0; i < MAX_PLAYER; i++){
print_player(&b_player[i], type); // void라서 배열접근이 안됨 캐스팅 필수
}
}
else if (type == soccer) {
soccer_player_t* s_player = (soccer_player_t*)player;
for(int i = 0; i < MAX_PLAYER; i++){
print_player(&s_player[i], type);
}
}
print_line();
}
baseball_player_t b_player = (baseball_player_t)player;
void로 받았기 때문에 casting을 해줘야 속성에 접근이 가능해진다.
baseball_player_t b_player = (baseball_player_t)player;
void은 배열접근이 안되기에 casting을 해줬다. 앞으로 이런 코드가 계속 나온다.
sort.h
#ifndef __SORT_H__
#define __SORT_H__
#include "player.h"
typedef enum _order_t {
asc, // 오름차순
desc // 내림차순
} order_t;
void sort(void* dst, const void* src, const int count, order_t order, int (*fp_compare)(void* a, void* b), player_type_t type);
#endif
type parameter추가했다.
sort.c
#include "sort.h"
static void swap(void* a, void* b, player_type_t type);
static void swap(void* a, void* b, player_type_t type){
if(type == baseball){
baseball_player_t tmp;
tmp = *(baseball_player_t*)a;
*(baseball_player_t*)a = *(baseball_player_t*)b;
*(baseball_player_t*)b = tmp;
}
else if(type == soccer){
soccer_player_t tmp;
tmp = *(soccer_player_t*)a;
*(soccer_player_t*)a = *(soccer_player_t*)b;
*(soccer_player_t*)b = tmp;
}
}
void sort(void* dst, const void* src, const int count, order_t order, int (*fp_compare)(void* a, void* b), player_type_t type) {
if (fp_compare == NULL) {
printf("정렬할 함수를 인자로 넣어줘야 합니다.\r\n");
return;
}
if(type == baseball){
memcpy(dst, src, sizeof(baseball_player_t) * count);
}
else if(type == soccer){
memcpy(dst, src, sizeof(soccer_player_t) * count);
}
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
void* p1 = NULL;
void* p2 = NULL;
if (type == baseball) {
baseball_player_t* b_dst = (baseball_player_t*)dst;
p1 = &b_dst[j];
p2 = &b_dst[j + 1];
} else if (type == soccer) {
soccer_player_t* s_dst = (soccer_player_t*)dst;
p1 = &s_dst[j];
p2 = &s_dst[j + 1];
}
if (order == asc) {
if (fp_compare(p1, p2) > 0) {
swap(p1, p2, type);
}
}
else if (order == desc) {
if (fp_compare(p1, p2) < 0) {
swap(p1, p2, type);
}
}
}
}
}
fp_compare인자도 void로 바꿧다. swap에서도 void로 인자를 받기 때문에 p1, p2를 void*로 선언했고 dst를 casting해 값을 주었다.
main.c
#include "player.h"
#include "sort.h"
int compare_avg(void* a, void* b);
int compare_r (void* a, void* b);
int compare_hit (void* a, void* b);
int compare_hr (void* a, void* b);
int compare_goal(void* a, void* b);
int compare_assist (void* a, void* b);
int compare_point (void* a, void* b);
int compare_avg(void* a, void* b) {
baseball_player_t* A = (baseball_player_t*)a;
baseball_player_t* B = (baseball_player_t*)b;
if (A->avg > B->avg) {
return 1;
}
else return -1;
}
int compare_r(void* a, void* b) {
baseball_player_t* A = (baseball_player_t*)a;
baseball_player_t* B = (baseball_player_t*)b;
if (A->r > B->r) {
return 1;
}
else return -1;
}
int compare_hit(void* a, void* b) {
baseball_player_t* A = (baseball_player_t*)a;
baseball_player_t* B = (baseball_player_t*)b;
if (A->hit > B->hit) {
return 1;
}
else return -1;
}
int compare_hr(void* a, void* b) {
baseball_player_t* A = (baseball_player_t*)a;
baseball_player_t* B = (baseball_player_t*)b;
if (A->hr > B->hr) {
return 1;
}
else return -1;
}
int compare_goal(void* a, void* b){
soccer_player_t* A = (soccer_player_t*)a;
soccer_player_t* B = (soccer_player_t*)b;
if (A->goal > B->goal) {
return 1;
}
else return -1;
}
int compare_assist (void* a, void* b){
soccer_player_t* A = (soccer_player_t*)a;
soccer_player_t* B = (soccer_player_t*)b;
if (A->assist > B->assist) {
return 1;
}
else return -1;
}
int compare_point (void* a, void* b){
soccer_player_t* A = (soccer_player_t*)a;
soccer_player_t* B = (soccer_player_t*)b;
if (A->point > B->point) {
return 1;
}
else return -1;
}
int main(){
baseball_player_t baseball_players[MAX_PLAYER] = {
{"레이예스", 0.355, 78, 176, 14},
{"에레디아", 0.351, 68, 168, 15},
{"김도영", 0.344, 127, 168, 35},
{"송성문", 0.343, 75, 158, 17},
{"박민우", 0.336, 70, 137, 7},
{"로하스", 0.331, 99, 172, 30},
{"도슨", 0.330, 69, 126, 11},
{"김혜성", 0.322, 76, 145, 11},
{"허경민", 0.321, 67, 125, 7},
{"구자욱", 0.320, 79, 144, 26}
};
baseball_player_t sorted_baseball_players[MAX_PLAYER];
soccer_player_t soccer_players[MAX_PLAYER] = {
{"무고사", 14, 1, 15},
{"일류첸코", 12, 5, 17},
{"야고", 11, 2, 13},
{"이승우", 10, 2, 12},
{"이상헌", 10, 6, 16},
{"이동경", 9, 5, 14},
{"이호재", 9, 5, 14},
{"주민규", 8, 4, 12},
{"정재희", 8, 3, 11},
{"정승원", 8, 6, 14}
};
soccer_player_t sorted_soccer_players[MAX_PLAYER];
sort(sorted_baseball_players, baseball_players, MAX_PLAYER, asc, compare_hr, baseball);
print_all_player("hr로 야구 선수 정렬(오름차순)", sorted_baseball_players, baseball);
sort(sorted_soccer_players, soccer_players, MAX_PLAYER, desc, compare_point, soccer);
print_all_player("point로 축구 선수 정렬(내림차순)", sorted_soccer_players, soccer);
return (0);
}
compare함수를 여러개 선언했다.
이로써 sort함수 1개로 (축구선수, 야구선수), (오름차순, 내림차순), (여러가지 속성)중 1개씩 선택해 정렬할 수 있게 됐다.

결론
funtion pointer까지는 코드 추가가 얼마 없었는데 player자료형을 void로 하니까 type이라는 새로운 자료형도 만들고 모든 함수에 casting을 해 코드가 많이 추가가 됐다.
C++이 오버로딩 가능한 것을 C는 불가능하기에 void로 모든 타입 받아주고 enum으로 구분정보를 보내줘야 한다(C언어의 한계)
수정
포인터선언 최소화 + const추가
main.c
#include "player.h"
#include "sort.h"
int compare_avg(const void* a, const void* b);
int compare_r(const void* a, const void* b);
int compare_hit(const void* a, const void* b);
int compare_hr(const void* a, const void* b);
int compare_goal(const void* a, const void* b);
int compare_assist(const void* a, const void* b);
int compare_point(const void* a, const void* b);
int compare_avg(const void* a, const void* b){
if (((baseball_player_t*)a)->avg > ((baseball_player_t*)b)->avg) {
return 1;
}
else return -1;
}
int compare_r(const void* a, const void* b){
if (((baseball_player_t*)a)->r > ((baseball_player_t*)b)->r) {
return 1;
}
else return -1;
}
int compare_hit(const void* a, const void* b){
if (((baseball_player_t*)a)->hit > ((baseball_player_t*)b)->hit) {
return 1;
}
else return -1;
}
int compare_hr(const void* a, const void* b){
if (((baseball_player_t*)a)->hr > ((baseball_player_t*)b)->hr) {
return 1;
}
else return -1;
}
int compare_goal(const void* a, const void* b){
if (((soccer_player_t*)a)->goal > ((soccer_player_t*)b)->goal) {
return 1;
}
else return -1;
}
int compare_assist(const void* a, const void* b){
if (((soccer_player_t*)a)->assist > ((soccer_player_t*)b)->assist) {
return 1;
}
else return -1;
}
int compare_point(const void* a, const void* b){
if (((soccer_player_t*)a)->point > ((soccer_player_t*)b)->point) {
return 1;
}
else return -1;
}
int main() {
baseball_player_t baseball_players[MAX_PLAYER] = {
{"레이예스", 0.355, 78, 176, 14},
{"에레디아", 0.351, 68, 168, 15},
{"김도영", 0.344, 127, 168, 35},
{"송성문", 0.343, 75, 158, 17},
{"박민우", 0.336, 70, 137, 7},
{"로하스", 0.331, 99, 172, 30},
{"도슨", 0.330, 69, 126, 11},
{"김혜성", 0.322, 76, 145, 11},
{"허경민", 0.321, 67, 125, 7},
{"구자욱", 0.320, 79, 144, 26}
};
baseball_player_t sorted_baseball_players[MAX_PLAYER];
soccer_player_t soccer_players[MAX_PLAYER] = {
{"무고사", 14, 1, 15},
{"일류첸코", 12, 5, 17},
{"야고", 11, 2, 13},
{"이승우", 10, 2, 12},
{"이상헌", 10, 6, 16},
{"이동경", 9, 5, 14},
{"이호재", 9, 5, 14},
{"주민규", 8, 4, 12},
{"정재희", 8, 3, 11},
{"정승원", 8, 6, 14}
};
soccer_player_t sorted_soccer_players[MAX_PLAYER];
sort(sorted_baseball_players, baseball_players, MAX_PLAYER, asc, compare_hr, baseball);
print_all_player("hr로 야구 선수 정렬(오름차순)", sorted_baseball_players, baseball);
sort(sorted_soccer_players, soccer_players, MAX_PLAYER, desc, compare_point, soccer);
print_all_player("point로 축구 선수 정렬(내림차순)", sorted_soccer_players, soccer);
return (0);
}
player.c
#include "player.h"
static void print_line(); // 이 파일에서만 쓸 수 있게 정적 선언
void print_player(void* player, player_type_t type) {
if (type == baseball) {
printf("%-10s", ((baseball_player_t*)player)->name);
printf("%8.3f", ((baseball_player_t*)player)->avg);
printf("%6d", ((baseball_player_t*)player)->r);
printf("%6d", ((baseball_player_t*)player)->hit);
printf("%6d", ((baseball_player_t*)player)->hr);
printf("\r\n");
}
else if (type == soccer) {
printf("%-10s", ((soccer_player_t*)player)->name);
printf("%6d", ((soccer_player_t*)player)->goal);
printf("%6d", ((soccer_player_t*)player)->assist);
printf("%6d", ((soccer_player_t*)player)->point);
printf("\r\n");
}
}
static void print_line() {
printf("-----------------------------------\r\n");
}
void print_all_player(char* title, void* player, player_type_t type) {
printf(" %s\r\n", title);
print_line();
if (type == baseball) {
for (int i = 0; i < MAX_PLAYER; i++) {
print_player(&((baseball_player_t*)player)[i], type); // void라서 배열접근이 안됨 캐스팅 필수
}
}
else if (type == soccer) {
for (int i = 0; i < MAX_PLAYER; i++) {
print_player(&((soccer_player_t*)player)[i], type);
}
}
print_line();
}
sort.c
#include "sort.h"
static void swap(void* a, void* b, player_type_t type);
static void swap(void* a, void* b, player_type_t type) {
if (type == baseball) {
baseball_player_t tmp;
tmp = *(baseball_player_t*)a;
*(baseball_player_t*)a = *(baseball_player_t*)b;
*(baseball_player_t*)b = tmp;
}
else if (type == soccer) {
soccer_player_t tmp;
tmp = *(soccer_player_t*)a;
*(soccer_player_t*)a = *(soccer_player_t*)b;
*(soccer_player_t*)b = tmp;
}
}
void sort(void* dst, const void* src, const int count, order_t order, int (*fp_compare)(const void* a, const void* b), player_type_t type) {
if (fp_compare == NULL) {
printf("정렬할 함수를 인자로 넣어줘야 합니다.\r\n");
return;
}
if (type == baseball) {
memcpy(dst, src, sizeof(baseball_player_t) * count);
}
else if (type == soccer) {
memcpy(dst, src, sizeof(soccer_player_t) * count);
}
for (int i = 0; i < count - 1; i++) {
for (int j = 0; j < count - 1 - i; j++) {
void* p1 = NULL;
void* p2 = NULL;
if (type == baseball) {
p1 = &((baseball_player_t*)dst)[j];
p2 = &((baseball_player_t*)dst)[j + 1];
}
else if (type == soccer) {
p1 = &((soccer_player_t*)dst)[j];
p2 = &((soccer_player_t*)dst)[j + 1];
}//배열이기 떄문에 자료형 표시를 해줘야 함
if (order == asc) {
if (fp_compare(p1, p2) > 0) {
swap(p1, p2, type);
}
}
else if (order == desc) {
if (fp_compare(p1, p2) < 0) {
swap(p1, p2, type);
}
}
}
}
}
player.h
#ifndef __PLAYER_H__
#define __PLAYER_H__
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define MAX_PLAYER (10)
typedef enum _player_type_t {
baseball,
soccer
} player_type_t;
typedef struct _baseball_player_t {
char name[64];
double avg; // 타율
int r; // 득점
int hit; // 안타
int hr; // 홈런
} baseball_player_t;
typedef struct _soccer_player_t {
char name[64];
int goal; // 골
int assist; // 도움
int point; // 공격포인트
} soccer_player_t;
void print_all_player(char* title, void* player, player_type_t type);
void print_player(void* player, player_type_t type);
#endif
sort.h
#ifndef __SORT_H__
#define __SORT_H__
#include "player.h"
typedef enum _order_t {
asc, // 오름차순
desc // 내림차순
} order_t;
void sort(void* dst, const void* src, const int count, order_t order, int (*fp_compare)(const void* a, const void* b), player_type_t type);
#endif