들어가며
C++에서는 클래스가 있다면
C에서는 구조체가 있다.
뭔가 순서가 좀 바뀐 것 같지만
아무튼 기분 탓이다.
구조체도 어디서 많이 들어만 봤지
뭐하는 놈인지 감도 잘 안 잡히고
어디다 써먹는지도 모르는 게 우리의 현실이다.
어쨌든 이번에도 구조체를 박살 내 보도록 하자.
사용자 정의 타입, 구조체
구조체(Structure Type).
사용자가 C언어의 기본 타입을 가지고
새롭게 정의할 수 있는 자료형이라고 말할 수 있다.
구조체의 정의가 그렇다고 한다면,
그럼 어디다가 써먹을 수 있는가?
구조체는 기본 타입(자료형)만으로는 나타낼 수 없는
복잡한 데이터를 표현할 수 있다.
예를 들어, 배열이 같은 타입의 변수 집합이라고 한다면
구조체는 다양한 타입의 변수 집합을 '하나'의 타입으로 나타낸 것이다.
이때 구조체를 구성하는 변수를
구조체의 '멤버(member)' 또는 '멤버 변수(member variable)'라고 한다.
즉, 지금까지 자료형별로 변수를 하나씩 선언해서 사용했지만
상황에 따라 이것이 여간 귀찮지 않을 수가 없다.
예를 들어, 장비 아이템 중 무기 아이템에 관한 정보를 처리한다면
무기의 이름, 무기의 종류, 공격력, 공격 속도, 치명타 확률 등등
여러 가지 정보를 저장할 변수가 필요하다.
또한 이러한 아이템의 종류도 굉장히 많기 때문에,
이러한 상황에서 구조체를 사용하여
체계적으로 관리한다면 굉장히 효율적이다.
구조체의 정의와 선언 방법
C언어에서의 구조체는 'struct' 키워드를 사용하여 다음과 같이 정의한다.
- struct 구조체 이름
{
(멤버 변수 1의 자료형) (멤버 변수 1의 이름);
(멤버 변수 2의 자료형) (멤버 변수 2의 이름);
···
};
이렇게 보니 내가 못 알아먹을 것 같아서 그림으로 설명해 주겠다.
그럼 하나 하나씩 뜯어서
천천히 살펴보도록 하자.
우선 struct 키워드를 사용하여 구조체를 사용할 것을 알린다.
그리고 구조체의 이름은 'weapon'으로 정의하고 있다.
그다음엔 중괄호 사이에 멤버 변수들을 선언하고
마지막의 세미콜론으로 구조체 정의를 종료할 것을 알리고 있다.
이렇게 정의된 'weapon' 구조체는 사용자 정의 자료형이라고 한다.
이렇게 정의된 '구조체 타입'은 '구조체 변수'로 선언하여 사용할 수 있다.
- struct (구조체 이름) (구조체 변수 이름);
struct weapon sword;
또한 구조체의 정의와 주조체 변수의 선언을
동시에 할 수도 있다.
- struct (구조체 이름)
{
(멤버 변수 1의 타입) (멤버 변수 1의 이름);
(멤버 변수 2의 타입) (멤버 변수 2의 이름);
···
} (구조체 변수 이름);
struct weapon
{
char name[30];
int atk_dmg;
double atk_spd;
} sword;
이름도 내 마음대로 할 거임 ㅎㅎ
만약 이미 존재하는 타입에
새로운 이름을 붙이고 싶다면 어떻게 할 수 있을까.
일단 그 자료형(type)을 이름만 수정해서 다시 정의(define)해야 할 것 같다.
간단하지 않은가?
따라서 C언어의 'typedef' 키워드는
이미 존재하는 타입에 새로운 이름을 붙일 때 사용한다.
구조체 변수를 선언하거나 사용할 때
매번 struct 키워드를 사용하여 구조체임을 명시해야 한다.
하지만 'typedef' 키워드를 사용하여 구조체에
새로운 이름을 선언하면, 매번 struct 키워드를 사용하지 않아도 된다.
typedef 키워드의 사용법은 다음과 같다.
- typedef struct (구조체 이름) (구조체의 새로운 이름);
typedef struct weapon WEAPON;
또한, 구조체의 정의와 typedef 선언을 동시에 할 수도 있다.
- typedef struct (구조체 이름)
{
(멤버 변수 1의 타입) (멤버 변수 1의 이름);
(멤버 변수 2의 타입) (맵버 변수 2의 이름);
···
} (구조체의 새로운 이름);
typedef struct weapon
{
char name[30];
int atk_dmg;
double atk_spd;
} WEAPON;
이 경우에만 한해서 구조체 이름을 생략할 수 있다.
typedef struct
{
char name[30];
int atk_dmg;
double atk_spd;
} WEAPON;
제발 조직으로 돌아오십쇼, 형님.
배열에서는 인덱스를 이용하여 배열 요소에 접근할 수 있다.
그렇다면, 구조체에서는?
구조체에서는 멤버로 접근하려고 할 때는
멤버 연산자(.)를 사용해야 한다.
구조체에서 멤버로의 접근 방법은 다음과 같다.
- (구조체 변수 이름).(멤버 변수 이름);
sword.atk_dmg
참고) 구조체의 주솟값과 구조체의 첫 번째 멤버 변수의 주솟값은 같다.
우리 조직도 물갈이 할 때가 됐다.
구조체 변수를 초기화할 때는
멤버 연산자(.)와 중괄호({})를 사용한다.
구조체 변수의 초기화 방법은 다음과 같다.
- (구조체 변수 이름) = {.(멤버 변수 1의 이름) = (초깃값), .(맴버 변수 2의 이름 = (초깃값), ··· };
sword = {.name = "long sword", .atk_dmg = 8, .atk_spd = 1.2};
이 방법을 사용하면, 원하는 멤버 변수만을 초기화 할 수 있다.
이때 '멤버 변수가 정의된 순서'와 '초기화의 순서'는 아무런 상관이 없다.
그리고 초기화하지 않은 멤버 변수는 0으로 초기화된다.
또한 배열의 초기화 방법과 같은 방법으로
구조체 변수를 초기화할 수 있다.
- (구조체 변수 이름) = {(멤버 변수 1의 초깃값), (멤버 변수 2의 초깃값), ··· };
sword = {"Long sword", 8, 1.2}
이 방법은 구조체 정의에서 멤버 변수가 정의된 순서에 따라서
차례대로 초깃값이 설정되며,
초기화되지 않은 변수들은 자동으로 0으로 초기화 된다.
구조체 길들이기
일단 기본적인 구조체에 대한 사용법과 문법을 배웠으니
이제 직접 사용해보는 일만 남았다.
위에서 배운 것들을 종합적으로 적용시켜서
코드를 구성했으니 잘 살펴보길 바란다.
#include <stdio.h>
typedef struct
{
char name[30];
int atk_dmg;
double atk_spd;
} WEAPON;
struct potion
{
int HP;
int MP;
};
int main()
{
WEAPON longsword = { "Long Sword", 8, 1.2 };
WEAPON dagger = { .name = "Dagger", .atk_dmg = 4, .atk_spd = 1.6};
struct potion potion_red = { .HP = 50 };
printf("Weapon name: %s\n Attack Damage: %d\n Attakc Speed: %lf\n\n",
longsword.name, longsword.atk_dmg, longsword.atk_spd);
printf("Weapon name: %s\n Attack Damage: %d\n Attakc Speed: %lf\n\n",
dagger.name, dagger.atk_dmg, dagger.atk_spd);
printf("HP recovery: %d\n MP recovery: %d\n\n",
potion_red.HP, potion_red.MP);
return 0;
}
마치며
정말정말 간단하게 구조체가 뭐하는 놈인지
어떻게 써먹는 지만 알아본 시간이었다.
정말 한 말을 잘 지키지 않는가?
근데 그게 전부다.
진짜 이게 전부라서 딱히 뭘 더 가르칠 수가 없다.
아무튼 열심히 해서 C++의 클래스도 공부할 수 있도록 하라.
참고 및 출처
코딩의 시작, TCP School
코딩 도장