프로그래밍

연산자

연산자가 무슨 일을 하는지는
양쪽 값의 자료형에 따라 달라지기도 합니다.

학습 목표

  1. 산술 연산자로 계산할 수 있고, 나머지 연산자가 어디에 쓰이는지 설명할 수 있다.
  2. 복합 대입 연산자와 증감 연산자를 읽고, 증감 연산자를 앞에 붙일 때와 뒤에 붙일 때의 차이를 설명할 수 있다.
  3. 비교 연산과 논리 연산의 결과가 1과 0으로 나온다는 것을 알고 조건식을 만들 수 있다.
  4. 우선순위 때문에 의미가 달라지는 식을 알아보고 괄호로 계산 순서를 분명히 할 수 있다.

산술 연산자

나머지 연산자는 계산기에 없는데 프로그램에서는 자주 씁니다.

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. 다음 중 틀린 설명은?