산술 연산자
나머지 연산자는 계산기에 없는데 프로그램에서는 자주 씁니다.
printf("%d\n", 7 + 3); // 10
printf("%d\n", 7 - 3); // 4
printf("%d\n", 7 * 3); // 21
printf("%d\n", 7 / 3); // 2 정수끼리는 몫만 남는다
printf("%d\n", 7 % 3); // 1 나머지
+ - *는 계산기와 같고, /는 정수끼리 나누면 소수점 아래를 버립니다(7 / 3은 2). 처음 보는 것은 % 하나인데,
나누고 남은 값인 나머지를 구합니다. 퍼센트가 아닙니다.
나머지 연산자의 쓰임
- 짝수인지 홀수인지 —
n % 2가 0이면 짝수다. - 단위를 바꿀 때 — 135초는
135 / 60분135 % 60초. - 몇 개마다 한 번 —
i % 10이 0일 때만 줄을 바꾼다.
#include <stdio.h>
int main(void) {
int sec;
printf("초를 입력하세요: ");
scanf("%d", &sec);
printf("%d분 %d초\n", sec / 60, sec % 60);
return 0;
}%는 정수끼리만 쓸 수 있습니다
7.5 % 2는 컴파일 오류가 납니다. 실수에는 쓸 수 없습니다.
/가 정수끼리면 몫만 남긴다는 것도 함께 기억해 두세요.
/와 %는 몫과 나머지를 함께 구할 때 나란히 자주 쓰입니다.
음수가 있으면 파이썬과 결과가 달라질 수 있습니다. C는 나눗셈 결과의 소수점 아래를 0 쪽으로 버려 몫을 정하므로
-7 / 2는 -3, -7 % 2는 -1입니다. 파이썬에서는 각각 -4와 1이 나옵니다.
그래서 홀수인지는 n % 2 == 1이 아니라 n % 2 != 0으로 검사하는 편이 안전합니다.
스스로 확인
오늘이 무슨 요일인지 계산하는 프로그램을 만든다면 %를 어디에 쓸 수 있을까요?
계산하면서 대입하기
짧게 쓰는 방법이지만, 짧아진 만큼 읽을 때 헷갈립니다.
total = total + 5처럼 변수의 값을 꺼내 계산한 뒤 다시 그 변수에 대입하는 일이 워낙 잦아서
줄여 쓰는 방법이 따로 있습니다. 바로 복합 대입 연산자(compound assignment operator)입니다.
int total = 10;
total += 5; // total = total + 5 와 같다
printf("%d\n", total); // 15
total *= 2;
printf("%d\n", total); // 30
+= -= *= /= %=가 모두 같은 방식입니다.
대입 규칙이 그대로 적용됩니다. 오른쪽을 먼저 계산하고, 그 값과 왼쪽 변수를 연산한 결과를 왼쪽 변수에 대입합니다.
1씩 늘리고 줄일 때는 증감 연산자
int a = 5;
printf("%d\n", a++); // 5 먼저 쓰고 나서 1 늘린다
printf("%d\n", a); // 6
int b = 5;
printf("%d\n", ++b); // 6 먼저 1 늘리고 나서 쓴다
printf("%d\n", b); // 6
++와 --를 증감 연산자(increment·decrement operator)라고 하며, 변수 값을 1 늘리거나 줄입니다.
변수 값이 1 늘어난다는 결과는 앞에 붙이든 뒤에 붙이든 같습니다. 다른 것은
그 식에서 쓰이는 값입니다. 뒤에 붙이면 「먼저 쓰고 나서 늘리고」,
앞에 붙이면 「먼저 늘리고 나서 쓴다」입니다.
그래서 a++;처럼 한 줄에 혼자 쓰일 때는 둘이 완전히 같습니다.
차이는 다른 식 안에 들어 있을 때만 드러납니다.
짧게 쓰는 것이 늘 좋은 것은 아닙니다
printf("%d %d", a++, ++a);처럼 한 줄에 몰아 쓰면
읽는 사람마다 다르게 이해하고, C 표준도 결과를 정해 두지 않았습니다.
이렇게 한 식에서 같은 변수를 두 번 바꾸면 컴파일러마다, 때로는 같은 컴파일러에서도 설정에 따라 다른 값이 나올 수 있습니다.
줄여 쓰기보다 한 줄에 한 가지 일만 하는 편이 낫습니다.
스스로 확인
int n = 3; int m = n++ + 2; 다음에 n과 m은 각각 얼마일까요?
비교 연산과 논리 연산
C에서는 참과 거짓이 1과 0으로 나옵니다.
printf("%d\n", 7 > 3); // 1 참
printf("%d\n", 7 < 3); // 0 거짓
printf("%d\n", 7 == 7); // 1 같은지 비교할 때는 등호 두 개
printf("%d\n", 7 != 7); // 0참은 1, 거짓은 0
C에서는 비교 결과가 참·거짓 전용 값이 아니라 정수(int)로 나옵니다.
그래서 printf("%d", 7 > 3)은 1을 출력합니다.
거꾸로 조건식에 아무 수나 쓸 수도 있습니다. 0이면 거짓, 0이 아니면 모두 참으로 봅니다. 1만 참인 것이 아닙니다.
같은지 비교하는 것은 ==, 대입하는 것은 =
if (x = 5)라고 쓰면 x에 5를 대입하고, 그 결과인 5가 0이 아니므로 언제나 참이 됩니다.
오류로 멈추지 않기 때문에 프로그램이 늘 같은 쪽으로만 실행됩니다.
조건식에 등호가 하나만 있으면 의심하세요.
여러 조건을 하나로 묶을 때는 논리 연산자(logical operator)를 씁니다.
int score = 85;
printf("%d\n", score >= 80 && score < 90); // 1 둘 다 참이어야 참
printf("%d\n", score < 60 || score > 90); // 0 하나라도 참이면 참인데, 둘 다 거짓이다
printf("%d\n", !(score >= 80)); // 0 참과 거짓을 뒤집는다| 연산자 | 읽는 법 | 참이 되는 경우 |
|---|---|---|
&& |
그리고 | 양쪽이 모두 참일 때 |
|| |
또는 | 하나라도 참일 때 |
! |
아니다 | 뒤에 오는 조건이 거짓일 때 |
답이 정해지면 오른쪽은 아예 계산하지 않습니다
int sum = 100;
int count = 0; // 아직 아무도 응답하지 않았다
if (count != 0 && sum / count >= 60) {
printf("평균이 60점 이상입니다\n");
} else {
printf("아직 평균을 낼 수 없습니다\n"); // 이쪽이 실행된다
}
// count가 0이라 왼쪽이 거짓 → 오른쪽 sum / count는 「계산되지 않는다」
// 만약 계산했다면 0으로 나누게 된다 — C가 결과를 정해 두지 않은 동작이라 대개 프로그램이 비정상 종료된다
&&는 왼쪽이 거짓이면 오른쪽을 계산할 필요도 없이 거짓입니다.
그래서 오른쪽을 건너뜁니다. 위 코드에서 count가 0이므로
sum / count는 아예 계산되지 않습니다.
이처럼 결과가 정해지면 나머지 계산을 건너뛰는 것을 단축 평가(short-circuit evaluation)라고 합니다.
단축 평가는 꼭 필요한 성질입니다. 만약 오른쪽까지 계산했다면 정수를 0으로 나누게 되는데, C는 그 결과를 정해 두지 않았고 대개 프로그램이 그 줄에서 비정상 종료됩니다. 「먼저 안전한지 확인하고, 안전할 때만 계산한다」를 한 줄로 쓸 수 있는 것도 단축 평가 덕분입니다. 확인하는 조건을 반드시 왼쪽에 두어야 합니다. 순서를 바꾸면 0으로 나누는 것을 막지 못합니다.
||는 반대로 왼쪽이 참이면 오른쪽을 건너뜁니다.
왼쪽 값에 따라 오른쪽이 실행되기도 하고 건너뛰기도 한다는 뜻이므로,
조건식 안에 값을 바꾸는 식을 넣지 않는 편이 안전합니다.
수학처럼 80 <= score < 90이라고 쓰면 의미가 완전히 달라집니다.
왼쪽부터 차례로 계산해 (80 <= score)가 1이나 0이 되고, 그 값을 다시 90과 비교하기 때문에
언제나 참이 됩니다. 반드시 score >= 80 && score < 90처럼 두 조건으로 나누어 씁니다.
스스로 확인
점수가 0점 미만이거나 100점을 넘으면 「잘못된 값」이라고 판단하려면 조건식을 어떻게 쓰면 될까요?
연산자 우선순위
헷갈리면 외우지 말고 괄호로 순서를 정해 줍니다.
한 식에 연산자가 여럿이면 먼저 계산되는 순서가 있습니다. 이 순서를 우선순위(precedence)라고 하며, 대체로 수학과 같습니다.
*와 %처럼 우선순위가 같은 산술 연산자끼리 만나면 왼쪽부터 계산합니다.
| 순서 | 연산자 | 설명 |
|---|---|---|
| 1 | () |
괄호가 가장 먼저다. 헷갈리면 괄호를 친다 |
| 2 | ! ++ -- (자료형) |
값 하나에 붙는 연산자 |
| 3 | * / % |
곱셈 · 나눗셈 · 나머지 |
| 4 | + - |
덧셈 · 뺄셈 |
| 5 | < <= > >= |
크기 비교 |
| 6 | == != |
크기 비교보다 나중이다 |
| 7 | && → || |
「그리고」가 「또는」보다 먼저다 |
| 8 | = += … |
대입이 가장 나중이다 |
표를 외우는 것이 목표가 아닙니다
실제 개발에서도 이 표를 다 외워서 쓰지는 않습니다. 헷갈리는 곳에 괄호를 치는 것이 더 빠르고, 나중에 읽는 사람도 이해하기 쉽습니다. 표는 다른 사람이 쓴 코드를 읽을 때 「왜 이렇게 계산됐지?」를 확인하는 데 씁니다.
다만 &&가 ||보다 먼저라는 것 하나는 알아 두면 좋습니다.
a || b && c는 a || (b && c)로 계산됩니다.
왼쪽부터 차례로 묶이지 않습니다.
스스로 확인
2 + 3 * 4 % 5는 얼마일까요? 표의 순서대로 하나씩 계산해 보세요.
핵심 정리
나머지 %
나누고 남은 값. 정수끼리만 쓸 수 있다. 짝수 판별, 단위 바꾸기, 몇 개마다 한 번 하는 일에 쓴다.
증감 연산자
변수가 1 늘어나는 결과는 같다. 다른 것은 그 식에서 쓰이는 값이다.
a++는 먼저 쓰고 나서 늘리고, ++a는 먼저 늘리고 나서 쓴다.
참과 거짓
비교 결과는 1과 0으로 나온다. 조건식에서는
0이면 거짓, 0이 아니면 모두 참이다.
같은지 비교하는 것은 ==, 대입하는 것은 =.
우선순위
대체로 수학과 같고 &&가 ||보다 먼저다.
외우기보다 괄호를 친다.
80 <= score < 90은 의미가 달라지므로 두 조건으로 나누어 쓴다.
확인 퀴즈
배운 내용을 확인해 봅시다.
1. int a = 5; printf("%d", a++);가 출력하는 값과, 출력한 뒤 a의 값은?
2. 점수가 80점 이상 90점 미만인지 검사하려면?
3. 다음 중 틀린 설명은?