C 언어(6) - 연산자 이해하기

C 언어 강의, 산술, 대입, 비트연산자

Featured image

🔚 짧게 하는 복습

✅ 1. C언어에서 문자형은 0 ~ 127까지 ASCII 코드, 혹은 0~256까지 Extended ASCII 코드로 할당된다.

혹시 기억이 안 난다면, 다시 돌아가자


저번 강의로 길고 험난했던 자료형 강의가 끝났다.

사실 누군가는 이런 근본적인 원리 같은 거 몰라도 코딩 잘만 한다고 하는 사람도 있을 수 있다.

하지만 필자의 생각은 앞으로는 코딩을 얕고 다양하게 하는 사람보다, 깊고 좁게 아는 사람이 유리할 수 있다고 생각한다.

그리고 모든 언어나 프로그래밍에 앞서, C언어는 프로그램이 작동하는 방식이나 변수, 논리 등에 대한 인사이트를 주기 때문에 깊게 배울 필요가 있다고 생각한다.

오늘부터는 연산자에 대해서 이해해보자.

참고로 오늘 연산자, 내일 입력을 배우면 여러분도 실질적인 프로그램을 만들 수 있게 된다!


연산자란?

연산자란 수학에서 쓰는 것처럼 +, -, _, / 등을 의미한다. (컴퓨터에서 곱하기는 _, 나누기는 /이다.)

그런데 수학에서 쓰는 연산자가 C언어에서는 다른 의미가 있기도 하고, 수학에서 아예 사용하지 않는 연산자가 C언어에서는 쓰이기도 한다.

C언어에서 연산자는 산술 연산, 증감 연산, 부호 연산, 대입(축약 대입) 연산, 비트 연산, 관계 연산, 논리 연산, 조건 연산 총 8개가 있다.

오늘은 이 중 비트 연산까지만 살펴보는 시간을 알아보고, 다음 시간에는 관계, 논리, 조건 연산을 배워보도록 하겠다.


대입 연산

대입 연산은 =이다. 수학에서는 이 기호가 같다는 것으로 쓰이지만, C언어에서는 연산자 오른쪽의 값을 왼쪽에 대입한다는 뜻이다.

저번 시간까지 우리는 이런 코드를 써왔다.

int integer = 3;

사실은 이 과정은 아래 코드처럼 2가지가 축약되어있다. 하나는 변수를 선언하는 그것과 그 변수에 값을 대입하는 것이다. (저번 강의에서 언급했듯이 프로그램은 위에서 아래로 진행된다.)

int integer;
integer = 3;

위의 코드처럼 한 번 변수를 선언했다면, 그 뒤로는 변수 이름 앞에 자료형을 쓸 필요가 없다.

실제로 printf를 통해 코드를 확인해봐도 같은 결과가 나올 것이다.

그런데 흥미로운 점이 있다. 만약 이렇게 코드를 작성하면 어떨까?

int integer;
3 = integer;

수학을 배운 사람이라면 문제가 없어 보이는 코드이다.

하지만 C언어에서는 아까 말한 것처럼 integer의 값을 3에 넣겠다는 것이니, 문제가 생길 수밖에 없다.

3은 값을 저장하는 공간, 즉 변수가 아니기 때문이다.

값을 대입할 수 없으므로 문제가 생길 수밖에 없다.

자 그럼, 아래의 코드 실행 결과를 예측해보자. 제발 읽으시는 여러분들이 실행하기 전에 생각하고 답을 확인하는 습관을 지니면 좋겠다.

(중요 : 프로그램은 무조건 위에서 아래로 진행한다.)

순서대로 이는 15, 30, 20, 45가 대입됐다가 마지막 값인 45만 출력되는 코드이다.

그림으로 그려보면 이런 그림이다.


부호 연산

부호 연산은 변수 앞에 붙는 -이다. 변수 앞에가 강조된 이유는 뒤에 나올 산술 연산과 헷갈릴 수 있기 때문이다.

이는 직관적으로 이해가 쉬운데, 변수에서 불러온 값에 앞에 붙어 값의 부호를 바꿔준다.

하지만 중요한 점이 있는데, 변수 안의 값이 직접적으로 바뀌는 것은 아니다.

역시 헷갈릴 때는 코드를 직접 실행해보자.

negative = -integer;

여기 이 부분에 integer 앞의 ‘-‘가 부호 연산이다.

순서대로 뜯어보면, integer의 값을 불러와서 부호를 바꿔준다. (부호 연산)

그리고 그 값을 negative라는 변수에 대입한다. (대입 연산)

integer에 15를 대입한 후 다른 값을 대입하는 연산은 없다.

그렇기에 integer의 값은 바뀌지 않는 것이다. (변수의 값을 바꾸려면 무조건 대입 연산이 필요하다)

그럼 잘 이해했는지 확인하기 위해, 아래 코드의 계산을 예측해보자.

어째 빙글빙글 도는 것 같지만, 정신 차리면 어렵지 않다.

그림으로 보면 이렇다.

마지막 integer의 15 색깔이 다른 이유는, 다른 값이 대입이 일어났기 때문이다.

물론 결과는 같지만, integer에 negative2의 값 -15에서 부호가 반전된 15가 대입된 것이므로 분명히 다른 값으로 대입 연산이 일어난 것이다.


산술 연산

산술 연산은 위의 연산들보다 훨씬 익숙하다.

+, -, *, /, %인데 *는 우리가 익숙한 곱하기라고 생각하면 된다.

아래 코드를 보자.

우리가 예상하는 대로 진행됨을 알 수 있다.

물론, 연산 과정에서 number1과 number2에서 값만 가져온 거지, number1과 number2 안의 값이 바뀌진 않는다.

그런데 / 연산자는 변수의 자료형에 따라 결과가 다르다.

a/b라고 했을 때, a나 b 중에서 하나라도 실수 자료형을 가진다면 우리가 익히 아는 나눗셈이 되고, 둘 다 정수 자료형이라면 몫을 구하는 연산이 된다.

이는 자료형의 연산 방법에서 한계가 나타나는 것인데, 정수형 자료형들 간의 연산의 결과는 오직 정수, 실수형 자료 간의 연산 결과는 실수가 나오기 때문이다.

직접 코드를 보자.

분명 같은 10/20 연산이지만 자료형에 따라 다른 값을 가지는 것을 알 수 있다.

그런데 여기서 “정수형과 실수형을 연산하면 어떻게 되나요?”라는 훌륭한 질문을 할 수 있다. 이는 추후 강의에서 다루도록 하겠다. 결과만 미리 말하자면 실수를 결괏값으로 가진다.

마지막으로는 % 연산인데 a%b라고 하면, 이는 a/b 연산의 나머지를 계산하는 연산이다.

컴퓨터에서는 나머지를 계산할 일이 생각보다 많아서, 필요 때문에 만들어진 연산이다.

예제를 직접 보자.

간단한 수학, 대입 연산이기에 넘어가도록 하겠다.


비트연산

비트 연산은 평소에 사용하지 않아서 조금 생소할 수 있다.

위의 산술 연산이나 부호 연산은 변수에서 을 불러와서 연산하는 단위 연산이다.

그런데 비트 연산은 말 그대로 비트 단위로 연산을 하는 연산자이다.

우선은 코드를 먼저 보고 설명하는 것이 편해 보인다.

7은 이진수로 1112, 18은 10102이다.

우선 첫 번째 연산자 &부터 보자. &는 and 연산이라고 하고 연산자 양 옆의 값이 모두 1일 때만 1의 결과를 가지고, 하나라도 0이라면 0의 결과를 가진다.

이렇게 한 자리씩, 즉 한 bit씩 계산이 돼서 결과가 나온다.

두 번째 연산자 |는 or 연산이라고 하고 연산자 양옆의 값 중 하나라도 1이라면 1의 결과를 가지고, 둘 다 0일 때만 0의 결과를 가진다.

세 번째 연산자 ^는 xor 연산 혹은 exclusive or 연산이라고 하고 두 값이 다를 때만 1의 결과를 가지고, 나머지는 모두 0의 결과를 가진다.

네 번째 연산자는 두 개의 값으로 연산을 하는 것이 아니라, 부호 연산처럼 하나의 값으로 연산을 한다. (이를 어려운 말로 단항 연산자라고도 한다.)

~는 not 연산이라고 하고 비트를 모두 반전시킨다. 즉 모든 비트의 0은 1, 1은 0으로 바꾼다.

여기서 모든 비트가 중요한데, int형 같은 경우 4바이트 = 32비트를 가진다고 했다. 즉 숫자가 들어가는 4비트 말고도 앞의 28비트도 빈 것이 아니라, 0으로 채워져 있다.

~ 연산은 그 0들마저도 모두 1로 바꾼다. (2의 보수법에서 최상단 비트는 부호를 의미하고, 7이라는 양수에서 -8이라는 음수로 바뀐 것을 알 수 있다.)

마지막으로 «와 »의 연산에 대해서 알아보자.

이는 의외로 간단한데, 비트를 « n는 왼쪽으로 n칸, »n은 오른쪽으로 n칸 이동시킨다는 뜻이다.

그런데 빨간색으로 된 0이 보이는가? n칸을 이동시키면 빈 곳이 생기기 마련이다.

그렇게 만들어지는 공간은 0으로 채운다.


축약 대입 연산

위에서 언급한 것처럼 C언어에서 산술 연산 혹은 비트 연산만 해서는 변수 안의 값을 바꿀 수 없다.

그렇게 하기 위해서는 값을 불러와서 산술 연산을 한 후, 다시 대입해줘야 한다.

근데 이 사실은 생각보다 코드를 짜면 굉장히 귀찮고 번거로운 일이 된다.

예를 들어, 변수의 이름이 number_of_students_in_harvard라고 하고, 30을 더하는 연산을 한다고 하자.

number_of_students_in_harvard = number_of_students_in_harvard + 30;

뭐 한두번은 복사 붙여넣기를 하겠지만, 이 코드를 나올 때마다 적는건 상당히 귀찮은 일이다.

이런 상황을 해결하기 위해 나온 것이 축약 대입 연산자이다.

변수 그 자체에 연산하고 대입을 하는 과정을 하나의 연산자로 나타내는 것이다.

num = num + 3;
num = num - 3;
num = num * 3;
num = num / 3;
num = num % 3;
num = num<<2;
num = num>>2;

위 코드의 각각의 연산자들을 아래처럼 축약이 된다.

num += 3;
num -= 3;
num *=* 3;
num /= 3;
num %= 3;
num <<= 2;
num >>= 2;

증감연산자

특히 변수 안의 값에 1을 더하거나 1을 빼서 다시 대입 연산은 증감연산자라는 것으로 쉽게 처리할 수 있다.

++는 변수 안의 값을 1 증가, –는 변수 안의 값을 1 감소이다.

그런데 이 연산자는 위치에 따라 증가가 먼저인지, 결과를 알려주는지가 다르다.

연산자가 변수 앞에 위치하면 이를 전위 연산이라고 부르고, 먼저 계산 후 변수 안의 값을 준다.

반면, 연산자가 변수 뒤에 위치하면 이를 후위 연산이라고 부르고, 변수 안의 값을 준 후 계산을 한다.

그렇기에 궁극적으로는 변수 안의 값이 1이 증가하거나 감소하는 것은 같으나, 연산자가 연산되는 그 부분에서는 값이 달라지는 것이다.


📖 오늘의 핵심(다 알기 전까지는 넘어가지 말자❗)

✅ 1. 산술 연산, 부호 연산자, 대입 연산자를 알자

✅ 2. 비트 연산자를 알자.

✅ 3. 대입 연산자, 특히 축약 대입연산자를 알자.

⚠️ 실무에서 특별한 상황이 아니면, 증감연산자를 사용하지 않는다.

잘못 짜면 시스템마다 결과가 달라질 수 있고, 잘 짜도 다른 사람이 내 코드를 볼 때 헷갈리기 때문이다. (더 읽어보기 참고)

💣 과제, 309128842, 120931282의 덧셈, 뺄셈, 곱셈, 나눗셈, 나눗셈의 몫의 결과를 알아보자!(당연히 계산기가 아니라 코드로 짜야한다…) (난이도 下)

🔜 더 공부해보기,

더 읽어보기1(왜 C와 JAVA에서 결과가 다른가요?)

더 읽어보기2(Undefined Behavior)