공용체 (union)/ 에디안 방식 / 열거형 (enum)

CHOI·2021년 7월 14일
0

C 언어

목록 보기
21/28
post-thumbnail

사실 공용체(union)은 그리 많이 사용하는 기능은 아니다. 그래도 C 언어에서 제공하는 것들 중 하나이니까 살펴보자. 공용체는 구조체와는 달리 메모리를 '공유' 한다.

위 그림을 보아도 알 수 있듯이 공용체는 멤버들의 시작 주소가 동일하다. 따라서 j 의 값을 변경함으로써 i 의 값을 변경할 수 있고, i 의 값을 변경함으로써 j 의 값을 변경할 수 있다. 한번 실제로 그런지 확인해보자.

#include <stdio.h>
union A {
  int i;
  char j;
};
int main() {
  union A a;
  a.i = 0x12345678;
  printf("%x", a.j);
  return 0;
}

실행 결과

78

우리가 j 의 값을 전혀 설정해주지 않았는데도 i0x12345678 를 대입하니까 78 이라는 값이 나온다. 그런데 왜 78이 나왔을까? 0x12 가 나와야 하는게 아닌가? 분명 ij 의 시작 주소는 동일하다. i0x12345678 로 메모리상에 있다면 j 의 처음 두 개인 0x12 가 되어야 할 것 같은데 말이다. 물론, 우리가 생각하는게 맞다. 그러나 컴퓨터는 수를 이렇게 보관하지 않는다.

빅 에디안(Big Endian) / 리틀 에디안(Little Endian)

컴퓨터에 수를 보관할 때 우리가 생각하는 방법, 즉 낮은 주소값에 상위 비트를 적는 방식을 빅 에디안 방식이라고 한다. 그리고, 우리가 생각하는 방식의 반대로 높은 주소값의 상위 비트를 적는 방식을 리틀 에디안 방식이라고 한다. 현재 대부분의 x86 프로세서는 리틀 에디안 방식을 사용한다. 일부 컴퓨터에서만 빅 에디안 방식을 사용한다.

먼저 빅 에디안 방식이 어떻게 수를 보관하는지 보자


이와 같이 상식적으로 수가 저장 된다. 하지만 이건 빅 에디안 방식이다. 우리가 대부분 사용하는 리틀 에디안은 다음과 같다.

이와 같이 1 바이트 씩 역으로 보관 된다. 따라서 우리가 j 를 출력하면 0x78 이 출력된다. 그렇다면 만약 우리가 jint 형이 아니라 shor 형으로 선언했다면 과연 0x7856 이 나올까 아니면 0x5678 이 나올까??

#include <stdio.h>
union A {
  int i;
  short j;
};
int main() {
  union A a;
  a.i = 0x12345678;
  printf("%x\n", a.j);
  return 0;
}

실행 결과

5678

이번에는 0x5678 이 나온다. 여기서 혼란이 온다.. 메모리 상에 그대로 살펴보면 분명히 j 에 해당하는 부분은 0x7856 이 출력되어야 정상이지만 컴퓨터는 '지극히 정상적으로 0x5678 을 출력하였다. 왜 그럴까? 이유는 간단하다.

이번에는 jshort 형 (2 바이트) 이기 때문에 j 가 2 바이트를 차지하게 된다. 그런데 j 를 출력해보면 0x7856 이 아니고 0x5678 이 출력된다. 왜 그럴지 고민하고 있을 때 누군가 이러한 질문을 던졌다.

" i 의 값을 출력하면 얼마나 나올까?"

당연히 0x12345678 이 나오겠지. 라고 생각했을 것이다. 왜?? , 컴퓨터는 자신이 메모리에 수를 저장하는 방식이 리틀 에디안 방식이라는 것을 알고 있기 때문에 출력시에는 이를 적절하게 변환을 취해서 0x12345678 을 출력할 것이다.

j 의 경우도 마찬가지이다. j 는 현재 '78 56' 부분을 가리키고 있지만 컴퓨터는 j  가 리틀 에디안 형식으로 이루어져 있다는 것을 알기 때문에 j를 출력할 때 이를 적절히 변환을 취해서 0x5678 로 바꿔서 출력하게 된다.

여기서 구조체는 끝내겠다. 구조체는 잘 사용하지 않지만 한번쯤은 듣게 될 것이다. 구조체 보다 더 중요한 것은 에디안이다. 에디안에 대해서는 까먹지 않도록 공부하자.

열거형 (Enum)

프로그램밍을 하다가 보면 데이터를 수에 대응 시키는 경우가 많이 생긴다. 예를 들어서 남자는 1, 여자는 0에 대응시키거나 흰색은 0, 검은색은 1 에 대응시켜서 나타내게 된다. 그런데 이렇게 수를 대응해서 나타내면 다음과 같이 헷갈리는 경우가 발생한다.

if (human.gender == 0)  // 사람의 성별이 0 일 때

남자는 0, 여자는 1 임을 확실히 기억하고 있다면 상관이 없지만 기억을 못하게 된다면 성에 대해서 무엇을 대응 시켰는지 다시 찾아봐야 하는 번거로운 일이 발생하게 된다. 하지만 아래와 같이

if (human.gender == MALE)  // 사람의 성별이 남자 일 때

를 한다면 확실히 알아 듣기 쉬울 것이다. 하지만 문제는 MALE 이라는 상수를 설정해야 하고 이 때문에 메모리 공간 또한 낭비된다. 이는 프로그래머 입장에서 상당히 난처하다. 따라서 C 에서는 열거형 (Enum) 을 도입하여 이러한 문제를 깔끔하게 해결하였다.

#include <stdio.h>
enum { RED, BLUE, WHITE, BLACK };
int main() {
  int palette = RED;
  switch (palette) {
    case RED:
      printf("palette : RED \n");
      break;
    case BLUE:
      printf("palette : BLUE \n");
      break;

    case WHITE:
      printf("palette : WHITE \n");
      break;
    case BLACK:
      printf("palette : BLACK \n");
      break;
  }
}

실행 결과

palette : RED

여기선 다음 부분만 살펴보자

enum { RED, BLUE, WHITE, BLACK };

열거형을 나타내기 위해 enum 을 사용하고 중괄호 안에 각각에 대해 써주면 된다. 그렇다면 컴파일러는 열거형에 나타나 있는 각 원소에 0부터 차례대로 정수값을 매겨준다. 즉, RED = 0, BLUE = 1, WHITE = 2, BLACK = 3 이와 같이 말이다. 이제 우린 이것들을 자유롭게 이용하면 된다.

예를 들어서

if (palette == 0)  // 현재 파레트의 색이 빨강인지 확인한다.

if (palette == RED)  // 현재 파레트의 색이 빨강인지 확인한다.

로 바꿔서 사용해도 된다는 것이다. 사실 위의 의미는 정확하게 동일하지만, 프로그래머가 읽을 때는 큰 차이가 있는 것이다. 위와 같이 한다고 해서 메모리 상에 RED 라는 변수가 정해지는게 아니다. 컴파일시에 컴파일러는 RED 는 모두 0으로 바꾸고 BLUE 는 모두 1로 바꾸는 등의 변환 작업을 하게 된다.

열거형 팁

#include <stdio.h>
enum { RED = 3, BLUE, WHITE, BLACK };
int main() {
  int palette = BLACK;
  printf("%d \n", palette);
}

실행 결과

6

열거형의 처음 수를 0부터 시작하기 싫다면 단순히 원하는 숫자를 위와 같이 해주면 된다. 그러면 RED 는 3이 되고 그 뒤로는 순서대로 4 , 5, 6 이 된다. 또한

enum { RED = 3, BLUE, WHITE = 3, BLACK }

으로 한다면 수를 지정한 부분부터 다시 시작하는 방식으로 BLUE = 4, BLACK = 4 가 된다. 참고로 열거형은 언제나 정수형이여야 한다.

profile
벨로그보단 티스토리를 사용합니다! https://flight-developer-stroy.tistory.com/

0개의 댓글