아두이노용 라인 트레이서 센서 관련 정보를 찾아보던 중 도대체 잘 모르겠는 소스코드를 발견해서 한참 살펴보았습니다.

c언어는 까먹은지도 오래되었고 많이 써보지도 않아서 대체 이게 뭔 소린가 알 수가 없었네요.


출처는 https://m.blog.naver.com/gyurse/221042288937 이곳이며 소스는 일부만 발췌해서 분석해 보았습니다.


일단 시작부터 막혔습니다.

1
2
3
4
5
6
7
8
9
10
11
12
struct SENSOR{
  union{
    int R;
    struct{
      int sensor1:1;
      int sensor2:1;
      int sensor3:1;
      int sensor4:1;
      int sensor5:1;
    }B;
  }sen;
};
cs


구조체 구조도 까먹은지 오래되었고 union은 아예 처음보는(까먹은 건지도 모르겠지만 전혀 기억에 없는) 것이었거든요.

게다가 변수 선언 뒤에 콜론:1은 또 뭔지...


여기저기 찾아보니 union은 공용체라는 거라네요. 멤버변수끼리 메모리를 공유한다고 합니다. 참고: https://wowon.tistory.com/108

여기서는 int R과 struct B가 메모리를 공유하겠네요.


그럼 콜론:1 은 뭘까요? 이건 정말 한참 찾아보았습니다. 관련 내용도 많지가 않았네요. 참고: http://egloos.zum.com/taehyo/v/4129635

bit field라고 알려진 특별한 형식의 struct 멤버라고 합니다. 콜론 뒤의 숫자가 멤버 변수가 갖게될 bit 수를 나타낸다고 합니다.

여기서는 struct B의 멤버 변수 sensor1~5가 각각 1bit를 갖게 되겠네요.


확인차 테스트를 해 봅니다. c언어는 다뤄본지 오래되서 에디터나 컴파일러도 PC에 가지고 있질 않아서 웹컴파일러를 찾아보았습니다.

https://ideone.com 를 사용했습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
 
struct SENSOR{
  union{
    int R; // union으로 int R과 struct B가 메모리를 공유합니다. R은 B에 접근하기 위한 이름으로만 사용하는 걸로 보입니다.
    struct{
      unsigned sensor1:1// 참고한 사이트에서는 bit field 사용 시 unsigned인지 signed인지 명시하는 걸 권장한다고 합니다.
      unsigned sensor2:1// 따라서 unsigned 1bit로 변경해 주었습니다.
      unsigned sensor3:1;
      unsigned sensor4:1;
      unsigned sensor5:1;
    } B;
  } sen;
};
 
struct SENSOR testSensor; // 변수 선언
 
int main(void) {
  // your code goes here
  testSensor.sen.B.sensor1 = 1// 초기화
  testSensor.sen.B.sensor2 = 1;
  testSensor.sen.B.sensor3 = 1;
  testSensor.sen.B.sensor4 = 1;
  testSensor.sen.B.sensor5 = 1;
    
  printf("%d\n"sizeof(testSensor)); // 공용체를 사용한 결과를 보고 싶어서 사이즈를 구해보았습니다.
 
  return 0;
}
cs

웹컴파일러를 통해 실행해본 결과 다음과 같이 나왔습니다.


struct SENSOR의 사이즈는 4bytes네요. int형 한 개와 bit 5개가 공용체로 선언되니 크기가 큰 4bytes로 잡혔습니다.


다음으로는 이상한 switch-case문이 눈에 띄었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
 switch(tracesensor.sen.R){
    // 전부 흰색인 경우
    case 0b11111
      break;
 
    // 전부 검정색인 경우
    case 0b00000:
      break;
 
    // 정중앙에 검정선이 있는 경우
    case 0b11011:
      break;
}
cs

공용체 중 R을 이용해서 struct B에 접근하는 것은 알겠는데, case 0b11111: 이 이해가 안되더라고요.

생각 끝에 2진수 연산인가 싶어서

1
printf("%d\n"0b11111);
cs

로 찍어보았더니 결과값이 31이 나오는 걸 보고 드디어 이해했습니다.

공용체 struct B의 각 1bit짜리 멤버변수에 0 또는 1 값을 넣고, 공용체 R로 접근해서 내용을 확인하면 5bit의 값이 나오는 걸 이용해서 switch-case문을 깔끔하게 만들어 준 것이었습니다.


최종 테스트 결과는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
 
struct SENSOR{
  union{
    int R; // union으로 int R과 struct B가 메모리를 공유합니다. R은 B에 접근하기 위한 이름으로만 사용하는 걸로 보입니다.
    struct{
      unsigned sensor1:1// 참고한 사이트에서는 bit field 사용 시 unsigned인지 signed인지 명시하는 걸 권장한다고 합니다.
      unsigned sensor2:1// 따라서 unsigned 1bit로 변경해 주었습니다.
      unsigned sensor3:1;
      unsigned sensor4:1;
      unsigned sensor5:1;
    } B;
  } sen;
};
 
struct SENSOR testSensor; // 변수 선언
 
int main(void) {
  // your code goes here
  testSensor.sen.B.sensor1 = 1// 초기화
  testSensor.sen.B.sensor2 = 1;
  testSensor.sen.B.sensor3 = 1;
  testSensor.sen.B.sensor4 = 1;
//  testSensor.sen.B.sensor5 = 1;
  testSensor.sen.B.sensor5 = 0;
    
  printf("%d\n"sizeof(testSensor)); // 공용체를 사용한 결과를 보고 싶어서 사이즈를 구해보았습니다.
  printf("%d\n"0b11111); // 2진 연산 테스트
    
  switch(testSensor.sen.R){ // 공용체와 2진 연산을 이용한 스위치문
    case 0b11111:
      printf("11111");
      break;
      
    case 0b11110// sensor1만 0일 때
      printf("11110");
      break;
      
    case 0b01111// sensor5만 0일 때. bit field가 아래자리부터 쌓이는 듯
      printf("01111");
      break;
            
    default:
      printf("default");
      break;
  }
    
  return 0;
}
cs

최종 테스트에서 하나 더 중요한 것을 발견! bit field로 1bit 씩 struct B를 만들 때 뒤, 즉 아래자리부터 쌓이는 걸로 보이네요.

sensor5만 0으로 바꾸고 테스트를 했는데 case 0b11110: 으로 넘어가지 않고 case 0b01111: 로 넘어갔습니다.


마무리

오랜만의 c언어라서 그런 것도 있지만 전혀 모르던 것도 있어서 찾아보는데 애를 많이 먹었네요.

그래도 공용체와 bit field, 2진 계산을 이용한 스위치문은 메모리가 한정되어 있는 아두이노용 코딩에서는 유용히 쓰일 것 같습니다.

물론 그렇게까지 해야하는 커다란 프로젝트는 할 일이 없겠지만요 ^^;;

그래도 구조가 깔끔하고 코드가 이뻐서 종종 사용해서 제 것으로 만들어 놔야할 것 같네요.


+ Recent posts