Search
Duplicate

구조체 내부변수에 포인터 연산으로 접근하기

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
C
C++
Scrap
태그
9 more properties
가끔 구조체에서 접근해야하는 변수가 많아 분기처리가 까다로울 때가 있다. 예를들어 아래의 구조체를 보자.
캐릭터 에셋들이 가져야 하는 sprite 정보들을 구조체에 저장해 두었다. 구조를 설계할 때, 특정 상황에서 어떤 sprite를 써야 하는지 직관적으로 알 수 있도록 하고 싶었다.
typedef struct s_asset { t_canvas *background_black; t_font *font_default; t_sprite *spr_player_blue_idle_right; t_sprite *spr_player_blue_idle_left; t_sprite *spr_player_blue_move_right; t_sprite *spr_player_blue_move_left; t_sprite *spr_player_blue_attack_right; t_sprite *spr_player_blue_attack_left; t_sprite *spr_player_blue_die_right; t_sprite *spr_player_blue_die_left; t_sprite *spr_player_red_idle_right; t_sprite *spr_player_red_idle_left; t_sprite *spr_player_red_move_right; t_sprite *spr_player_red_move_left; t_sprite *spr_player_red_attack_right; t_sprite *spr_player_red_attack_left; t_sprite *spr_player_red_die_right; t_sprite *spr_player_red_die_left; ... } t_asset;
C
복사

분기로 변수에 접근하기

각 상황마다 다른 sprite를 적용해야 하는데 이걸 분기로 처리한다면 아래와 같이 될 것이다.
t_sprite *scr_get_spr_player(t_asset *asset, int type, int color, int dir) { if (color) { if (type == 0) { if (dir == 1) return (asset->spr_player_blue_idle_right) else return (asset->spr_player_blue_idle_left) } else if (type == 1) { if (dir == 1) return (asset->spr_player_blue_move_right) else return (asset->spr_player_blue_move_left) } else if (type == 2) { ... } else { ... } } else { // 위랑 같음 } }
C
복사
물론 헤더 파일을 배열로 만든다면 해결할 수 있다. 아래의 코드를 살펴보자
typedef struct s_asset { t_canvas *background_black; t_font *font_default; t_sprite *spr_player[16]; ... }
C
복사
하지만 spr_player[16] 와 같이 배열을 사용한다면, 헤더 파일이 어떤 sprite들을 가지고 있는지 직관적으로 알 수 없다.
그리고 0, 1, 2와 같이 적어두면 이해하기 어렵기 때문에 #define BLUE_IDLE_RIGHT 0 와 같이 선언을 해주어야 하는데, 코드를 볼 때, 해당 선언이 되어있는 부분과 함께 보지 않으면 이해하는데 상당히 불편했다.
그렇다면 이러한 문제를 어떻게 해결할 수 있을까

포인터 연산으로 변수에 접근하기

분기로 처리하지 않고 포인터로 필요한 주소를 찾아 접근할 수 있다.
t_sprite *scr_get_spr_player(t_asset *asset, int type, int color, int dir) { int idx; t_sprite *spr; idx = 2; if (!color) idx += 8; idx += type * 2; if (dir != 1) idx += 1; spr = *(t_sprite **)((void *)asset + sizeof(void *) * idx); return (spr); }
C
복사
asset 구조체의 주소에서 (void *) 크기만큼 n번 이동하면 n번째 변수에 접근 할 수 있다.
예를 들어 2번 이동하면 spr_player_blue_idle_right 변수에 접근 할 수 있다.
마찬가지로 3번 이동하면 spr_player_blue_idle_left 변수에 접근 할 수 있다.
따라서 변수 순서에 따라서 바로 변수에 접근한다.
이제 위의 수식에서 분기를 없애고 조금 더 간략한 식으로 표현해보자.
t_sprite *scr_get_spr_player(t_asset *asset, int type, int color, int dir) { int idx; t_sprite *spr; idx = 2 + (!color) * 8 + type * 2 + (dir != 1); spr = *(t_sprite **)((void *)asset + sizeof(void *) * idx); return (spr); }
C
복사
이제 아래와 같이 타입, 색깔, 방향만 넣어주면 원하는 sprite의 주소를 바로 얻어올 수 있다.
void func() { change_sprite(this, scr_get_spr_player(SPR_ATTACK, this->color, this->dir)); }
C
복사