프로그램은 CPU가 처리하는 명령어의 묶음입니다. 일반적으로 연산자는 컴파일되면 명령어로 바뀌므로 연산자를 배우는 것은 결국 명령어를 익히는 것과 같습니다. 연산 명령에는 연산의 대상이 되는 데이터가 필요한데 이를 피연산자라고 합니다. 1+a 또는 a+b라는 문장이 있다면 +는 연산자이고, 1, a, b는 피연산자인 거죠.
그런데 피연산자가 이렇게 상수와 변수만 있는 건 아닙니다. 때로는 수식을 피연산자로 직접 사용합니다. 연산자는 보통 기능에 따라 분류하지만, 피연산자의 개수에 따라 나눌 수도 있습니다. 피연산자가 하나면 단항 연산자, 2개면 이항 연산자, 3개면 삼항 연산자가 됩니다.
✅산술 연산자와 대입 연산자
✔️산술 연산자
산술 연산자에는 더하기(+), 빼기(-), 곱하기(* ), 나누기(/), 나머지(%)가 있습니다. 모두 2개의 피연산자를 사용하며 이 중 빼기 연산자(-)는 피연산자를 하나만 사용할 때 피연산자의 부호를 바꾸는 역할도 합니다.
#include <stdio.h>
int main(void)
{
int a, b;
int sum, sub, mul, inv;
a = 10; // 대입 연산(=)
b = 20; // 대입 연산(=)
sum = a + b; // 더하기 연산(+) 후 대입 연산(=)
sub = a - b; // 빼기 연산(-) 후 대입 연산(=)
mul = a * b; // 곱하기 연산(*) 후 대입 연산(=)
inv = -a; // 음수 연산(-) 후 대입 연산(=)
printf(“a의 값 :%d, b의 값 :%d\n”, a, b);
printf(“덧셈 : %d\n”, sum);
printf(“뺄셈 : %d\n”, sub);
printf(“곱셈 : %d\n”, mul);
printf(“a의 음수 연산 : %d\n”, inv);
return 0;
}
/* 실행결과
a의 값 :10, b의 값 :20
덧셈 : 30
뺄셈 : -10
곱셈 : 200
a의 음수 연산 : -10
*/
✔️대입 연산자
=를 대입 연산자라 하며 오른쪽 수식의 결과를 왼쪽 변수에 저장합니다. 여기서 수식은 상수나 변수 또는 연산자를 사용한 식을 모두 포함합니다.
a = 10; // 8행. 대입 연산
b = 20; // 9행. 대입 연산
대입 연산은 변수의 값을 바꾸므로 이후에 a와 b의 값은 각각 10과 20이 됩니다. 대입 연산자를 다른 연산자와 함께 사용할 때는 우선순위에 따라 다른 연산을 먼저 수행하고 그 결과를 왼쪽 변수에 저장합니다.
✔️나누기 연산자와 나머지 연산자
나누기는 나누기 연산자인 /을 사용합니다. 나누기 연산자를 사용할 때는 피연산자의 형태에 따라 결과가 달라지니 주의해야 합니다. 정수로 연산할 때는 몫을 구하고 실수로 연산할 때는 소수점까지 구합니다. 다음 예제는 5를 2로 나누는 간단한 계산입니다. 얼마가 나올까요?
#include <stdio.h>
int main(void)
{
double apple; // 실수
int banana; // 정수
int orange; // 정수
apple = 5.0 / 2.0; // 실수와 실수의 나누기 연산(/)
banana = 5 / 2; // 정수와 정수의 나누기 연산(/)
orange = 5 % 2; // 정수와 정수의 나머지 연산(%)
printf(“apple : %.1lf\n”, apple);
printf(“banana : %d\n”, banana);
printf(“orange : %d\n”, orange);
return 0;
}
/* 실행결과
apple : 2.5
banana : 2
orange : 1
*/
apple의 값은 예상한 대로 2.5가 나왔습니다. 9행에서 나누기 연산의 피연산자가 모두 실수이므로 소수점까지 계산되어 결과는 2.5가 됩니다.
apple = 5.0 / 2.0; // 9행. 실수와 실수 나누기 연산(/)
그런데 10행에서 계산한 banana의 출력값은 2입니다. 피연산자가 모두 정수이므로 몫만 계산되었습니다.
banana = 5 / 2; // 10행. 정수와 정수의 나누기 연산(/)
이와 같이 나눗셈 연산은 결괏값이 정수 또는 실수가 될 수 있으므로 그 값을 저장하는 변수도 그에 맞게 사용해야 합니다. 따라서 9행의 연산 결과는 실수이므로 double형 변수에, 10행의 연산 결과는 정수이므로 int형 변수에 저장합니다. 만약 정수/실수와 같이 피연산자의 형태가 다르거나 실수를 정수형 변수에 저장하면 얘기는 복잡해집니다.
마지막으로 몫을 뺀 나머지만 구할 때는 나머지 연산자인 %를 사용합니다.
orange = 5 % 2; // 11행. 정수와 정수의 나머지 연산(%)
실수 연산에는 나머지의 개념이 없으므로 나머지 연산자의 피연산자로는 반드시 정수만을 사용해야 한다는 점을 잊지 마세요.
✅증감 연산자
a라는 int형 변수에 1을 더하려면 a = a + 1;이라고 쓰면 됩니다. 이렇게 쓰지 않고 ++a;로 간단히 표현할 수도 있습니다. 이는 빼기 연산에서도 마찬가지입니다.
이런 표현은 for문, while문, do while문 같은 반복문에서 루프마다 하나씩 더하거나 뺄 때 자주 사용됩니다. 증감 연산자(++, –)는 하나의 연산자로 대입 연산까지 수행하므로 변수의 값을 1씩 증가시키거나 감소시킬 때 쉽게 사용할 수 있습니다.
#include <stdio.h>
int main(void)
{
int a = 10, b = 10;
++a; // 변수의 값을 1만큼 증가
--b; // 변수의 값을 1만큼 감소
printf(“a : %d\n”, a);
printf(“b : %d\n”, b);
return 0;
}
/* 실행결과
a : 11
b : 9
*/
증감 연산자를 사용해서 a = a + 1을 ++a로 표현한다는 말은 증감 연산자가 대입 연산을 포함하므로 피연산자의 값이 바뀐다는 의미입니다. 당연하겠지만, ++10과 같이 상수에 직접 증감 연산자를 사용할 수는 없습니다. 10에 1을 더할 수는 있지만, 상수 10에 11을 저장할 수 없고 그 값을 바꿔서도 안 되기 때문입니다.
✔️전위 표기와 후위 표기
증감 연산자의 위치를 a++ 혹은 a–와 같이 피연산자 뒤로 옮길 수도 있습니다. 증감 연산자가 피연산자 앞에 놓이면 전위 표기prefix, 뒤에 놓이면 후위 표기postfix라고 합니다. 그렇다면 두 방식은 어떻게 다를까요? 전위 표기는 값이 증감하고 나서 연산에 사용하고, 후위 표기는 연산에 사용하고 나서 값이 증감합니다.
어떤 방법이 더 좋으냐의 문제가 아닙니다. 때에 따라 같은 결과를 내거나 다른 결과를 낼 수도 있습니다. 필요에 따라 사용하면 됩니다.
#include <stdio.h>
int main(void)
{
int a = 5, b = 5;
int pre, post;
pre = (++a) * 3; // 전위형 증감 연산자
post = (b++) * 3; // 후위형 증감 연산자
printf(“증감 연산 후 초깃값 a = %d, b = %d\n”, a, b);
printf(“전위형: (++a) * 3 = %d, 후위형: (b++) * 3 = %d\n”, pre, post);
return 0;
}
/* 실행결과
증감 연산 후 초깃값 a = 6, b = 6
전위형: (++a) * 3 = 18, 후위형: (b++) * 3 = 15
*/
증감 연산자를 단독으로 사용하면 전위 표기와 후위 표기의 결과는 같습니다. 즉, ++a와 a++는 모두 a의 값을 1씩 증가시킵니다. 그러나 다른 연산자와 함께 쓰이면 연산의 결과에 영향을 미칩니다.
8행의 (++a) * 3은 a의 값을 6으로 증가시킨 후에 3을 곱하므로 결괏값이 18이 됩니다. 하지만 9행의 (b++) * 3은 b가 증가되기 전의 값을 3과 곱하므로 15가 됩니다. 물론 연산이 끝난 후에 a와 b의 값은 모두 1씩 증가됩니다. 연산 과정이 어찌되었든 결과만놓고 본다면 증감 연산자의 후위 표기는 다른 연산자와 함께 사용될 때 가장 나중에 연산된다고 생각해도 좋습니다.
✅관계 연산자
놀이공원에 가면 나이에 따라 입장료가 다릅니다. 이를 프로그래밍으로 구현하려면 나이를 기준으로 값을 출력하면 됩니다. 이처럼 프로그래밍을 하다 보면 조건에 따라 명령을 실행해야 하는 경우도 있는데 이때 특정한 기준, 조건에 관해 명령을 실행하려면 관계 연산자가 필요합니다.
관계 연산자에는 대소 관계 연산자와 동등 관계 연산자가 있습니다. 대소 관계는 < 또는 > 등의 기호를 사용하고, 동등 관계는 = =(같다)나 !=(같지 않다) 기호를 사용합니다. 이들 연산자는 모두 피연산자를 2개 사용하며, 연산의 결괏값은 1 또는 0입니다. 컴파일러는 참과 거짓을 1과 0으로 판단하므로 관계식을 실행 조건 검사에 사용할 수 있습니다.
#include <stdio.h>
int main(void)
{
int a = 10, b = 20, c = 10; // a, b, c 값을 대입
int res; // 결괏값을 저장할 변수
res = (a > b); // 10 > 20은 거짓이므로 결괏값은 0
printf(“a > b : %d\n”, res);
res = (a >= b); // 10 >= 20은 거짓이므로 결괏값은 0
printf(“a >= b : %d\n”, res);
res = (a < b); // 10 < 20은 참이므로 결괏값은 1
printf(“a < b : %d\n”, res);
res = (a <= b); // 10 <= 20은 참이므로 결괏값은 1
printf(“a <= b : %d\n”, res);
res = (a <= c); // 10 <= 10은 참이므로 결괏값은 1
printf(“a <= c : %d\n”, res);
res = (a == b); // 10 == 20은 거짓이므로 결괏값은 0
printf(“a == b : %d\n”, res);
res = (a != c); // 10 != 10은 거짓이므로 결괏값은 0
printf(“a != c : %d\n”, res);
return 0;
}
/* 실행결과
a > b : 0
a >= b : 0
a < b : 1
a <= b : 1
a <= c : 1
a == b : 0
a != c : 0
*/
10행의 a >= b의 결과는 a가 b보다 커도 참이고 a와 b가 같아도 참이 됩니다. 즉, 둘 중 하나만 만족해도 참이 되는데 둘 다 만족하지 못하므로 거짓(false)이 되어 0을 출력합니다.
res = (a >= b); // 10행. 10 >= 20은 거짓이므로 결괏값은 0
20행에서 사용한 동등 관계 연산자 !=는 피연산자가 같지 않다는 의미입니다. !
res = (a != c); // 20행. 10 != 10은 거짓이므로 결괏값은 0
예제에서는 연산자의 기능을 확인하고자 결괏값을 출력하고 끝냈지만, 대부분 프로그래밍에서는 주로 판단의 근거로 관계 연산의 결과를 사용합니다. 예를 들어 a와 b의 값이 무엇인지 모르지만 둘 중에 큰 값을 택해야 하는 상황이라면 a > b 연산을 수행해 결괏값을 확인하면 됩니다. 결과가 1이면 a가 b보다 크므로 a를 택하고 결과가 0이면 b가 크거나 최소한 같으므로 b를 택하면 됩니다. 관계 연산자는 제어문(if 문, switch ~ case 문, for 문, while 문 등)에서 주로 사용됩니다.
✅논리 연산자
논리 연산자는 논리 관계를 판단하는 데 사용하며 &&(AND), ¦¦(OR), !(NOT) 3가지뿐입니다.
&&는 논리곱(AND) 연산자로 2개의 피연산자가 모두 참일 때만 연산 결과가 참이 됩니다. ¦¦는 논리합(OR) 연산자로 둘 중에 하나라도 참이면 참이 됩니다. !는 논리부정(NOT) 연산자이며 피연산자를 하나 사용해 그 참과 거짓을 바꿀 때 사용합니다.
#include <stdio.h>
int main(void)
{
int a = 30;
int res;
res = (a > 10) && (a < 20); // 좌항과 우항이 모두 참이면 참
printf(“(a > 10) && (a < 20) : %d\n”, res);
res = (a < 10) || (a > 20); // 좌항과 우항 중 하나라도 참이면 참
printf(“(a < 10) || (a > 20) : %d\n”, res);
res = !(a >= 30); // 거짓이면 참으로, 참이면 거짓으로
printf(“! (a >= 30) : %d\n”, res);
return 0;
}
/* 실행결과
(a > 10) && (a < 20) : 0
(a < 10) || (a > 20) : 1
! (a >= 30) : 0
*/
8행을 살펴보겠습니다.
res = (a > 10) && (a < 20); // 8행. 좌항과 우항이 모두 참이면 참
이 연산은 10 < a < 20처럼 관계 연산자만 사용해서 표현할 수 있을 것 같습니다. 과연 그럴까요? 컴퓨터는 다음과 같이 왼쪽부터 오른쪽으로 차례대로 계산을 실행합니다.
- ❶단계
- ① 10 < a 에서 a 값 30을 대입한다
- ② 10 < 30은 참이므로 ❶자리에 1을 대입한다
⠀
- ❷단계
- ① ❶< 20에서 ❶의 결괏값인 1을 대입한다.
- ② 1 < 20은 참이므로 해당 식의 값은 1이다.
- ❶단계
- ① 10 < a 에서 a 값 30을 대입한다.
- ② 10 < 30은 참이므로 ❶자리에 1을 대입한다.
⠀
- ❷단계
- ① a < 20에서 a 값 30을 대입한다.
- 30 < 20은 거짓이므로 ❷자리에 0을 대입한다.
⠀
- ❸단계
- ① ❶&&❷에 각 식에서 나온 값을 대입한다.
- ② 1&&0은 거짓이므로 해당 식의 값은 0이다.
위 내용은 『혼자 공부하는 C언어(개정판)』의 일부분을 재구성하여 작성하였습니다.
개정판에서는 혼자 공부하다 보면 생기는 ‘왜?’라는 궁금증을 바로 해결할 수 있도록 보충 설명 영상을 QR 코드로 연결하였고, 비주얼 스튜디오 2022 버전을 반영하여 최신 프로그램에서도 무리없이 실습할 수 있도록 만들었습니다. 161개의 예제로 코딩 감각을 익힌 후 새롭게 추가된 도전 실전 예제로 실력을 향상시켜 보세요!
👀 도서 자세히 보기
✍️ 유튜브 강의
📑 네이버 카페(Q&A) 바로가기
Leave A Comment