2018년 6월 20일 수요일

C 언어 비교문에서 == 사용 방법

C 언어에서 '='는 assignment, 즉 변수에 값을 넣어줄 때 사용하고 '=='는 compare, 즉 앞과 뒤의 값이 같은가를 비교하기 위해 사용한다.

하지만 '=='는 코딩을 하다 보면 타이핑 실수로 '='를 하나 빼 먹는 실수를 범하기 쉬운데, 별거 아닌거 같은 실수가 아주 치명적인 결과를 가져올 수가 있다.

먼저 아래 코드를 보자.

void setup() {
  Serial.begin(115200); 
  pinMode(13, OUTPUT);
}

void loop() {
  if (Serial.available()) {
    byte ch = Serial.read();
    if (ch == '1') {
      digitalWrite(13, HIGH);
    } else {
      digitalWrite(13, LOW);
    }
  }
}


시리얼 포트로 글자를 입력 받아 '1'이면 아두이노에 있는 LED를 켜고, 그 이외 글자인 경우 LED를 꺼 주는 매우 단순한 코드이다.

이 부분에서 시리얼 포트에서 입력받은 글자가 '1'인지 비교하고 있다. 그런데 저기서 실수로 '='를 하나 빼 먹으면 어떻게 될까?


이렇게 해도 컴파일을 하면 아무 문제 없이 컴파일이 완료된다.


이 코드를 아두이노에 업로드해 실행을 하면 어떻게 될까?

원래대로라면 업로드가 완료되면 시리얼 모니터에서 '1'을 보내 줘야만 불이 켜지게 된다. 하지만 위의 코드가 업로드 되면 시리얼 모니터에서 어떤 글자를 보내건 무조건 불이 켜지고 그 이후에는 어떤 글자를 보내는가 상관 없이 불이 계속 켜저 있고 끌수가 없게 된다.

왜 이런 문제가 발생하는 것일까?

답은 맨 위에서 설명했던 것 처럼 '='와 '=='의 의미 차이 때문이다.

ch == '1' 라고 하는 경우는 ch에 들어 있는 글자와 '1'라는 글자를 비교해 같으면 True, 다르면 False라는 값이 출력된다. 그래서 if 구문이 ch에 들어 있는 글자에 따라 불을 켜거나 끌 수 있게 된다.

그런데 ch = '1' 의 경우는 ch에 어떤 글자가 들어 있는가는 상관 없이 ch에 '1'이라는 글자를 새로 넣어주라는 의미가 된다. 즉 시리얼 포트에서 어떤 글자가 들어왔는가는 그냥 무시되어 버리는 것이다. 게다가 C 언어에서 '=' (assignment)의 경우 무조건 결과값으로 True가 나오게 되기 때문에

if (ch = '1') {
  digitalWrite(13, HIGH); 
} else {
  digitalWrite(13, LOW);
}

저 코드에서 시리얼 포트에서 받은 값과 상관 없이 항상 digitalWrite(13, HIGH); 만 실행되게 된다.

게다가 바둑이나 장기를 둘 때 자기는 못보지만 훈수를 두는 사람은 쉽게 묘수를 볼 수 있는것처럼 저런 실수는 코딩을 한 당사사는 찾지 못하는 경우가 많다. 실제 코드가 길어지면 심한 경우 디버깅을 한다고 몇일, 몇달동안 코드를 한줄 한줄 뒤져보면서도 저 실수가 눈에 안 들어온다.


이것이 원하는 동작을 하는 코드인데 두개를 보고 한눈에 차이점을 찾을 수 있는가?? 지금까지 구구절절히 설명을 했으니 쉽게 찾겠지만 만일 따로 설명 없이 두개의 스크린샷만 놓고 차이점을 찾으라고 한다면 쉽지 않을 것이다.

그래서 비교문을 쓸 때 순서를 바꿔 쓰라고 권장하는 것이다.

즉 if (ch == '1') 대신 if ('1' == ch) 이렇게 쓰는 습관을 들이는 것이 좋다.

그럼 단지 순서만 바뀌 쓰는 것인데 뭐가 좋다는 것일까?

만일 둘 다 '='를 하나 빼먹었다고 생각을 해 보자.

if (ch ='1') 는 문법상 틀린 부분이 없기 때문에 정상적으로 컴파일이 완료되고 실행된다. 물론 동작은 원하는것과 다르겠지만...
하지만 if ('1' = ch) 는 C언어 문법 자체가 틀렸기 때문에 컴파일을 하면


이렇게 에러가 발생하기 때문에 바로 실수를 바로잡아 줄 수가 있게 된다.

* C언어 문법에서 '='로 변수에 값을 넣어주는 경우 변수 이름은 '='의 왼쪽에, 변수에 넣어 줄 값은 '='의 오른쪽에 와야만 한다. "lvalue required as left operand of assignment" 이 문구가 그게 잘못되었다고 알려주는 것이다.

바로 잘못된 부분을 수정하고 컴파일 하면 이제 정상적으로 동작하게 된다.


프로그래밍을 할 때 컴파일러가 에러를 내 주는건 해결하기도 훨씬 쉽다. 하지만 위와 같이 실수를 했는데도 불구하고 컴파일은 정상적으로 되지만 생각하는것과 다르게 동작하는걸 찾는건 훨씬 힘들기 때문에 코딩할 때 부터 그 가능성을 없애줄 수 있는 습관을 들여 놓는 것이 좋다.