-
Notifications
You must be signed in to change notification settings - Fork 1
00_03.StructsAndUnions
C언어에서는 int x = 0;
, float y = 0.0f
와 같이 단 하나의 값을 저장할 수 있는 변수도 있지만, 여러 개의 값을 저장할 수 있는 변수도 있다. 여기에 해당하는 변수가 바로 배열 (array)과 구조체 (structure) 변수이다. 배열은 같은 종류의 자료형을 가진 여러 값의 집합을 의미하며, 가장 많이 사용되는 대표적인 자료 구조 (data structure)이다.
이제 int
형의 값 10개를 저장할 수 있는 배열을 만들어보자.
#include <stdio.h>
/*
배열의 길이는 반드시 `#define`를 사용하여 정의해야
나중에 프로그램을 수정해야 할 때 편하다.
`int values[10];` 멈춰!!!
*/
#define N 10
int main(void) {
// 배열의 길이가 `N`인 배열을 생성한다.
int values[N];
// 배열의 모든 원소를 `-1`로 초기화한다.
for (int i = 0; i < N; i++)
values[i] = -1;
// 배열의 모든 원소를 출력한다.
for (int i = 0; i < N; i++)
printf("values[%d]: %d\n", i, values[i]);
return 0;
}
이제 배열을 선언과 동시에 초기화하는 방법에 대해 알아보자.
#include <stdio.h>
// 배열의 길이를 나타내는 매크로 상수.
#define N 5
int main(void) {
/*
배열을 선언과 동시에 초기화할 때 초기화 배열의 길이가
실제 배열의 길이보다 짧을 경우, 나머지 원소의 값은
0이 된다.
즉, `int values[N] = { 1, 2, 3, 0, 0 };`과 같은 뜻!
*/
int values[N] = { 1, 2, 3 };
// 배열의 모든 원소를 출력한다.
for (int i = 0; i < N; i++)
printf("values[%d]: %d\n", i, values[i]);
return 0;
}
C99부터는 특정 원소만을 선택해서 값을 지정할 수 있게 되었다.
#include <stdio.h>
// 배열의 길이를 나타내는 매크로 상수.
#define N 5
int main(void) {
// 첫 번째 원소와 세 번째 원소만을 초기화한다.
int values[N] = { [0] = 1, [2] = 3 };
// 배열의 모든 원소를 출력한다.
for (int i = 0; i < N; i++)
printf("values[%d]: %d\n", i, values[i]);
return 0;
}
일반적으로 배열의 크기는 배열의 자료형이 T
고 배열의 길이가 N
일 때, N * sizeof(T)
가 된다. 따라서, int values[10];
이라는 배열이 있다면, 이 배열의 크기는 10 * sizeof(int)
가 되는 것이다. 이를 통해, 배열의 길이는 sizeof(values) / sizeof(T)
, 즉 sizeof(values) / sizeof(values[0])
이 됨을 알 수가 있다.
구조체 (structure)는 다양한 종류의 자료형을 가진 여러 값의 집합을 의미하며, 배열만큼 자주 사용되는 자료 구조이다. 자료형의 각 원소는 멤버 (member) 또는 멤버 변수 (member variables)라고 하며, 각 원소는 배열처럼 인덱스로 접근하는 대신 그 원소의 이름으로 접근한다.
#include <stdio.h>
/* 원을 나타내는 구조체. */
struct circle {
int x, y; // 원의 중심점.
int radius; // 원의 반지름.
}; // 제발!!! 구조체 선언할 때 세미콜론 빼먹지 마!!!
/* 직사각형을 나타내는 구조체. */
struct rectangle {
int x, y; // 직사각형의 시작점.
int width, height; // 직사각형의 가로 및 세로 길이.
};
int main(void) {
// 이름이 없는 구조체의 변수를 생성한다.
struct /* 오잉? 이름이 없네? */ {
int value;
} s;
// `circle` 구조체 변수를 생성한다.
struct circle c = { 0, 0, 7 };
/*
`r.width`를 제외한 나머지 원소는 자동으로 0으로
초기화된다. (C99 표준의 'Designated Initializers')
*/
struct rectangle r = { .width = 10, .height = 8 };
// `s.value`의 값을 변경한다.
s.value = -1;
// `c`의 반지름을 출력한다.
printf("c.radius: %d\n", c.radius);
// `r`의 시작점 좌표를 출력한다.
printf("r.x: %d\n", r.x);
printf("r.y: %d\n", r.y);
return 0;
}
typedef
라는 키워드를 사용하면 구조체에 또다른 이름을 붙일 수 있다.
#include <stdio.h>
/* 원을 나타내는 구조체. */
typedef struct circle {
int x, y; // 원의 중심점.
int radius; // 원의 반지름.
} Circle; // 이제 `struct circle`을 `Circle`이라고 쓸 수 있다!
int main(void) {
// `circle` 구조체 변수를 생성한다.
Circle c = { 0, 0, 7 };
// `c`의 반지름을 출력한다.
printf("c.radius: %d\n", c.radius);
return 0;
}
유니온 (union)은 구조체와 비슷하게 다양한 종류의 자료형을 가질 수 있는 자료형이지만, 각 멤버에 공간을 따로 할당해주는 구조체와는 다르게 유니온의 모든 멤버는 하나의 공간을 같이 사용하며, 유니온의 크기는 유니온에서 가장 큰 원소의 크기가 된다. 이게 무슨 뜻이냐면... 아래 코드에서 sizeof(union u)
는 sizeof(int)
또는 sizeof(float)
중에서 더 큰 값이 된다는 뜻이다.
/* `int`형 값 또는 `float`형 값을 저장할 수 있는 유니온. */
union u {
int i;
float f;
};
유니온은 주로 어떤 자료형인지 확실히 알 수 없는 단 하나의 값을 저장할 때 사용하거나, 저수준 또는 임베디드 프로그래밍 (low-level/embedded programming)에서 비트와 바이트 관련 연산을 할 때 사용된다.
#include <stdio.h>
/* 실수 (real number)를 나타내는 구조체. */
typedef struct {
unsigned char type; // 저장된 값의 종류.
union { // 구조체에 저장된 값.
int i;
float f;
};
} Real;
int main(void) {
/*
`x.type`가 0이면 정수이고, 1이면 실수를
나타낸다고 가정하자.
*/
Real x = { .type = 0 };
printf("sizeof(x): %lu\n\n", sizeof(x));
// `x.i`와 `x.f`의 값을 둘 다 변경한다.
x.i = 10;
printf("x.i의 값은 %d이다.\n", x.i);
printf("x.f의 값은 %f이다.\n\n", x.f);
x.type = 1;
// `x.i`와 `x.f`의 값을 둘 다 변경한다.
x.f = 7.77;
printf("x.i의 값은 %d이다.\n", x.i);
printf("x.f의 값은 %f이다.\n", x.f);
return 0;
}
열거형 (enumeration)은 서로 관련이 있는 여러 상수 값의 집합이다. 우리가 픽셀 그래픽의 RPG 게임을 만든다고 생각해보자. 우리가 캐릭터를 움직일 때는 땅이나 잔디처럼 지나다닐 수 있는 곳도 있지만, 벽이나 물처럼 지나갈 수 없는 곳도 존재한다.
이것을 열거형으로 나타내면 다음과 같다.
/* 게임 맵의 타일 종류를 나타내는 열거형. */
enum tile_type {
GROUND, // 그냥 땅.
GRASS, // 그냥 잔디.
WATER, // 그냥 물.
WALL // 그냥 벽.
};
typedef enum tile_type TileType;
/* ... */
// enum tile_type mool = WATER;
TileType mool = WATER;
열거형의 값은 별도의 값이 정해져 있지 않으면 0부터 시작한다. 즉, GROUND
는 0, GRASS
는 1, WATER
는 2가 되고, WALL
은 3이 되는 것이다. 이제 유니온의 마지막 예제 프로그램을 열거형을 사용해 다시 작성해보자.
#include <stdio.h>
/* `Real` 구조체에 저장된 값의 종류를 나타내는 열거형. */
typedef enum {
INTEGER, // 정수 값.
FLOATING_POINT // 부동 소수점 값.
} RType;
/* 실수 (real number)를 나타내는 구조체. */
typedef struct {
RType type; // 저장된 값의 종류.
union { // 구조체에 저장된 값.
int i;
float f;
};
} Real;
int main(void) {
/*
`x.type`가 0이면 정수이고, 1이면 실수를
나타낸다고 가정하자.
*/
Real x = { .type = INTEGER };
printf("sizeof(x): %lu\n\n", sizeof(x));
// `x.i`와 `x.f`의 값을 둘 다 변경한다.
x.i = 10;
printf("x.i의 값은 %d이다.\n", x.i);
printf("x.f의 값은 %f이다.\n\n", x.f);
x.type = FLOATING_POINT;
// `x.i`와 `x.f`의 값을 둘 다 변경한다.
x.f = 7.77;
printf("x.i의 값은 %d이다.\n", x.i);
printf("x.f의 값은 %f이다.\n", x.f);
return 0;
}
- 오픈 소스 소프트웨어란?
- Git과 버전 관리 시스템
- GitHub를 이용한 저장소 호스팅
- 프로젝트의 기여 및 관리
- 라이브러리 소개
- 개발 환경 구축
- 첫 번째 프로그램
- 게임 창과 커서 관리
- 프레임, 시간과 타이머
- 픽셀, 선분과 기본 도형
- 마우스와 키보드 입력
- 벡터 글꼴과 비트맵 글꼴
- 이미지와 텍스처의 사용
- 카메라와 렌더 텍스처
- 충돌 감지와 충돌 해결
- 효과음과 음악 재생
- 그 외 유용한 함수