운동선수정렬만들기(function pointer)

아무개·2024년 9월 8일

C/C++

목록 보기
2/6
post-thumbnail

강의 시간에 배운 함수포인터를 활용해 운동선수 정렬 프로그램을 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
profile
생각 정리

0개의 댓글