[C언어] 03. 수식과 연산자
프로그램이 하는 일의 대부분은 값을 계산하고 비교하고 조합하는 것이다. 이 모든 연산을 담당하는 것이 연산자다. 오늘은 산술·증감·관계·논리 연산자부터, 내가 자주 헷갈린 형변환과 연산 우선순위까지 정리했다.
1. 산술 연산자
기본 사칙연산과 나머지 연산이 있다.
| 연산자 | 의미 | 예 (a=7, b=2) |
|---|---|---|
| + | 덧셈 | a + b → 9 |
| - | 뺄셈 | a - b → 5 |
| * | 곱셈 | a * b → 14 |
| / | 나눗셈 | a / b → 3 (정수 나눗셈) |
| % | 나머지 | a % b → 1 |
7 / 2 는 3.5가 아니라 3이다. 정수끼리 계산하면 결과도 정수가 되어 소수 이하가 잘리기 때문이다. 실수 결과가 필요하면 한쪽을 실수로 만들어야 한다(7.0 / 2 → 3.5). 이 함정은 뒤의 형변환에서 다시 다룬다.
2. 증감 연산자
++, --는 변수 값을 1 증가/감소시킨다. 붙는 위치에 따라 동작 시점이 달라지는 게 핵심이다.

- 전위(prefix)
++x: 먼저 증가시킨 뒤 그 값을 사용한다. - 후위(postfix)
x++: 먼저 현재 값을 사용한 뒤 증가시킨다.
int x = 5, y;
y = ++x; // x를 6으로 만든 뒤 y에 대입 → x=6, y=6
x = 5;
y = x++; // 현재 5를 y에 대입한 뒤 x 증가 → x=6, y=5
"증가가 먼저냐, 사용이 먼저냐"의 차이가 결과를 가른다. 처음엔 둘이 같은 줄 알았는데, y에 들어간 값이 6과 5로 갈리는 걸 직접 찍어보고서야 위치의 의미가 잡혔다.
3. 복합 대입 연산자
대입(=)과 다른 연산자를 결합한 것으로, x op= y는 x = x op y와 같다.

x += 3; // x = x + 3
x *= y + 1; // x = x * (y + 1) ← 오른쪽 전체가 먼저 계산됨
x *= y + 1이 x = x*y + 1이 아니라 x = x*(y+1)이라는 점을 주의한다. 오른쪽 수식 전체가 먼저 계산된 뒤 대입된다.
4. 관계 연산자와 논리 연산자
관계 연산자는 두 값을 비교해 참(1) / 거짓(0)을 돌려준다.

C에는 원래 별도의 논리형이 없어서, 0은 거짓, 0이 아닌 값은 참으로 다룬다.
x == y 는 "같은가?"를 묻는 관계 연산이고, x = y 는 "y를 x에 대입"하는 대입 연산이다. 조건문에 if (x = y)라고 쓰면 비교가 아니라 대입이 일어나고, 그 대입 결과값으로 참거짓이 판정되어 의도와 전혀 다르게 동작한다. 가장 흔한 실수 중 하나다.
논리 연산자는 여러 조건을 조합한다. AND(&&), OR(||), NOT(!)이다.

(x >= 60) && (x < 100) // x가 60 이상 100 미만
(x == 1) || (x == 2) // x가 1 또는 2
(x != 0) && (x != 1) // x가 0도 1도 아님
5. 조건 연산자와 콤마 연산자
조건 연산자(삼항 연산자)는 조건 ? 참일때값 : 거짓일때값 형태로, if문을 한 줄로 줄여준다.
max_value = (x > y) ? x : y; // x가 크면 x, 아니면 y
콤마 연산자는 여러 수식을 왼쪽부터 차례로 계산한다(x++, y++).
6. 형변환 (Type Casting)
서로 다른 자료형이 섞이면 형이 변환된다.
자동 형변환 (묵시적)

- 올림 변환:
double f = 10;→ f에 10.0 저장. - 내림 변환:
int i = 3.14;→ i에 3 저장(소수 이하 유실). - 수식 안에서: 서로 다른 타입이 섞이면 표현 범위가 큰 쪽으로 승급(promotion)된다.
int + double은 int가 double로 올라가 결과도 double이 된다.
명시적 형변환 (캐스트 연산자)
(자료형) 수식 형태로 프로그래머가 직접 형을 지정한다.

(int)1.23456 // 1 로 변환
(double)x // x를 double로
double f;
f = 3 / 2; // 정수끼리 → 1, f는 1.000000
f = (double)3 / 2; // 3을 double로 → 1.5, f는 1.500000
같은 3/2인데, 캐스트 하나로 결과가 1에서 1.5로 바뀐다. "언제 정수로 계산되고 언제 실수로 계산되는가"를 의식하는 게 중요하다.
7. 연산 우선순위
한 수식에 연산자가 여럿이면 우선순위에 따라 계산 순서가 정해지고, 같은 순위면 결합성(좌→우 또는 우→좌)을 따른다.

대략 "산술 → 관계 → 논리 → 대입" 순으로 우선순위가 낮아진다고 기억하면 큰 틀이 잡힌다. 헷갈릴 땐 괄호로 의도를 명확히 하는 게 가장 안전하다.
실습: 화씨를 섭씨로 변환

변환 공식은 섭씨 = 5/9 × (화씨 - 32)다.
5 / 9 를 정수로 계산하면 0이 되어버려, 결과가 항상 0으로 나온다. 반드시 실수로 계산해야 한다.
double celsius = 5.0 / 9.0 * (fahrenheit - 32);
8. 수업 코드로 보는 초 → 시·분·초 변환
나눗셈과 나머지 연산을 함께 쓰는 좋은 예제다. 전체 초를 입력받아 시, 분, 초로 나눈다.
int iNo;
int iTemp;
int iSec;
int iHour;
int iMin;
printf("초 입력: ");
scanf("%d", &iNo);
iSec = iNo % 60;
iTemp = iNo / 60;
iHour = iTemp / 60;
iMin = iTemp - (iHour * 60);
printf("%d초는 %d시간, %d분, %d초입니다.\n", iNo, iHour, iMin, iSec);
| 입력 초 | iSec | iTemp(전체 분) | iHour | iMin |
|---|---|---|---|---|
| 3661 | 1 | 61 | 1 | 1 |
| 85490 | 50 | 1424 | 23 | 44 |
이 문제는 %가 "남은 조각"을 구하고, /가 "큰 단위로 묶은 개수"를 구한다는 점을 보여준다. 단위 변환 문제는 대부분 이 두 연산을 번갈아 쓰면 풀린다.
오늘 느낀 점
- 연산자가 종류만 많아 보였는데, 결국 "값을 계산·비교·조합한다"는 한 일의 갈래였다. 우선순위도 산술→관계→논리→대입의 큰 흐름으로 잡으니 외울 게 줄었다.
- 정수 나눗셈 함정을 산술·형변환·실습에서 세 번이나 만났다. 같은 함정을 반복해 겪으니 이제는 나눗셈만 보면 타입을 의심하는 습관이 생겼다.
- 우선순위를 다 외우려다 지쳤는데, "애매하면 괄호"라는 원칙이 외우기보다 훨씬 실용적이라는 걸 받아들였다.
한 걸음 더
- 논리 연산자
&&와||는 단축 평가(short-circuit)를 한다.A && B에서 A가 거짓이면 B는 아예 계산하지 않는다(어차피 결과가 거짓이니까). 이 성질을 이용해(p != NULL) && (p->x > 0)처럼 "앞 조건이 참일 때만 뒤를 검사"하는 안전한 코드를 짤 수 있다. - 후위 증감(
x++)이 "값을 쓴 뒤 증가"한다고 했는데,a[i++] = x;처럼 한 문장에서 같은 변수를 여러 번 증감하며 쓰면 동작이 정의되지 않는(undefined) 위험한 코드가 된다. 한 줄에 증감을 욕심내기보다 또박또박 나눠 쓰는 편이 안전하다. - 우선순위를 외우는 것보다 중요한 건 "애매하면 괄호"라는 원칙이다. 숙련된 개발자도 우선순위를 전부 외우진 않으며, 괄호로 의도를 드러낸 코드가 읽기에도 디버깅에도 유리하다.