1학년 / 2학기 / C++ / 1강

자료형, 연산자, 흐름 제어

자료형, 연산자, 흐름 제어

학습 흐름

자료형과 리터럴 → 정수 나눗셈과 % → 형변환 → 정수 범위(오버플로, unsigned)
        ↓
연산자(증감, 비교, 논리, 삼항) → 우선순위와 결합성 → 단락 평가
        ↓
if / switch → while / do while / for → break / continue → 중첩 반복문

1. 프로그램의 기본 형태

#include <iostream>      // cin, cout을 쓰기 위한 헤더
using namespace std;     // std:: 생략
 
int main() {             // 프로그램의 시작점
    int age;
    cin >> age;                          // 입력
    cout << "age: " << age << endl;      // 출력 + 줄바꿈 + 버퍼 비우기
    return 0;                            // 0 = 정상 종료
}
요소기억할 점
#include <iostream>cin, cout 사용에 필요
using namespace std;없으면 std::cout, std::cin, std::string처럼 써야 함
main반드시 하나 있어야 함. 없으면 링크 오류
endl vs '\n'둘 다 줄바꿈. endl은 출력 버퍼도 비움

2. 자료형과 리터럴

자료형저장 대상리터럴 예주의
char문자 1개 (실제로는 작은 정수)'A'작은따옴표
int정수 (보통 32비트)42, 0x2A약 ±21억
long long큰 정수 (64비트)8000000000LL
float단정밀도 실수3.14ff가 없으면 double
double배정밀도 실수3.14실수 리터럴의 기본형
bool참/거짓true, false출력하면 1, 0
string문자열 (클래스)"Hello"#include <string>

문자는 정수 코드다 (아스키 코드)

char는 문자 하나를 정수 코드로 저장합니다. 산술 연산을 하면 int로 바뀌어 계산됩니다.

문자코드문자코드
'0'48'A'65
'9'57'Z'90
'\0' (널 문자)0'a'97
' ' (공백)32'z'122
char c = 'A';
cout << c;             // A        (char로 출력)
cout << c + 1;         // 66       (int로 바뀜)
cout << char(c + 1);   // B        (다시 char로 변환)
cout << '7' - '0';     // 7        (숫자 문자 → 정수 값)
cout << 'c' - 'a';     // 2        (알파벳 순번)

초기화하지 않은 지역 변수

함수 안에서 초기값 없이 선언한 기본 자료형 변수는 쓰레기 값을 가집니다. 읽으면 결과를 예측할 수 없습니다. 전역 변수와 static 변수는 자동으로 0이 됩니다.

int count;          // 함수 안: 값이 정해지지 않음
int total = 0;      // 항상 초기화하는 습관
int score{0};       // 중괄호 초기화도 가능

다음 출력을 쓰시오.

char c = 'A';
cout << c + 1 << ' ' << char(c + 1) << ' ' << c << '\n';
cout << '7' - '0' << ' ' << ('a' < 'B') << '\n';
정답 확인
66 B A
7 0

c + 1은 int 66, char(c + 1)은 'B'입니다. c 자체는 바뀌지 않습니다. 'a'(97)는 'B'(66)보다 크므로 'a' < 'B'는 0입니다.


3. 정수 나눗셈과 나머지 %

두 피연산자가 모두 정수이면 결과도 정수이고, 소수 부분은 0 쪽으로 잘립니다(버림이 아니라 절단).

식결과이유
7 / 233.5 → 3
-7 / 2-3−3.5 → −3 (0 쪽으로)
7 / 2.03.5하나라도 실수면 실수 나눗셈
7 % 317 = 3 × 2 + 1
-7 % 3-1나머지의 부호 = 왼쪽 피연산자의 부호
7 % -31왼쪽이 양수

규칙: a == (a / b) * b + a % b가 항상 성립합니다.

다음 출력을 쓰시오.

cout << 7 / 2 << ' ' << -7 / 2 << ' ' << 7 % 3 << ' '
     << -7 % 3 << ' ' << 7 % -3 << '\n';
정답 확인
3 -3 1 -1 1

나눗셈은 0 쪽으로 절단되고, 나머지의 부호는 왼쪽 피연산자를 따릅니다.


4. 자료형 변환

산술 변환: 더 넓은 쪽으로

서로 다른 자료형을 계산하면 더 넓은 자료형으로 맞춘 뒤 계산합니다.

bool, char, short → int (정수 승격)
int  op double    → double
int  op long long → long long

축소 변환: 정보 손실

넓은 자료형을 좁은 자료형에 넣으면 값이 잘립니다. 실수 → 정수는 소수 부분을 버립니다(0 쪽으로).

int n = 3.99;     // 3
int m = -3.99;    // -3  (내림이 아니라 0 쪽으로)
int k{3.99};      // 컴파일 오류: 중괄호 초기화는 축소 변환을 막음

명시적 변환

static_cast<double>(7) / 2   // 3.5  (C++ 방식)
(double)7 / 2                // 3.5  (C 방식)

다음 출력을 쓰시오.

int a = 7, b = 2;
double x = a / b;
double y = (double)a / b;
double z = (double)(a / b);
int n = 3.99;
int m = -3.99;
cout << x << ' ' << y << ' ' << z << ' ' << n << ' ' << m << '\n';
정답 확인
3 3.5 3 3 -3

x와 z는 정수 나눗셈 3을 변환한 것이고, y만 나누기 전에 a를 double로 바꿨습니다. double 3.0은 cout에서 3으로 출력됩니다.

부동소수점 오차

float, double은 많은 십진 소수를 정확히 저장하지 못합니다. 그래서 0.1 + 0.2 == 0.3은 거짓입니다. 실수 비교는 오차 범위를 둡니다.

#include <cmath>
bool same = fabs((0.1 + 0.2) - 0.3) < 1e-9;   // true

5. 정수의 범위: 오버플로와 unsigned

signed 오버플로 = 정의되지 않은 동작

32비트 int의 범위는 −2,147,483,648 ~ 2,147,483,647입니다. 이를 넘는 부호 있는 정수의 결과는 UB입니다. "최댓값 + 1 = 최솟값"이라고 가정하면 안 됩니다.

int big = 2147483647;
big = big + 1;          // UB
long long safe = 2147483647LL + 1;   // 2147483648

unsigned는 범위를 넘으면 돌아간다 (규칙이 정해져 있음)

unsigned int(32비트)는 0 ~ 4,294,967,295이고, 범위를 넘으면 2³²로 나눈 나머지로 돌아갑니다. 이 동작은 표준이 보장합니다.

0 - 1                  → 4294967295   (0 아래로 가면 최댓값)
4294967295 + 2         → 1
unsigned int u = -1;   → 4294967295   (−1을 넣어도 최댓값)

다음 출력을 쓰시오.

unsigned int u = 0;
u = u - 1;
cout << u << '\n';
int i = -1;
unsigned int k = 1;
cout << (i < k) << ' ' << (i < (int)k) << '\n';
unsigned int big = 4294967295u;
big = big + 2;
cout << big << '\n';
정답 확인
4294967295
0 1
1

i < k에서는 i가 unsigned로 바뀌어 4294967295 < 1이 되어 거짓입니다. k를 int로 바꾸면 −1 < 1로 참입니다. 4294967295 + 2는 한 바퀴 돌아 1입니다.


6. 연산자

대입과 복합 대입

=는 오른쪽 값을 계산한 뒤 왼쪽 변수에 저장합니다. 대입식 자체도 값(대입된 값)을 가집니다.

x = y = 10;     // 오른쪽부터: y = 10, 그 결과 10을 x에 대입
x += 3;         // x = x + 3
x %= 4;         // x = x % 4

증감 연산자: 전위와 후위

형식식의 값변수 변화
++x증가한 뒤 값+1
x++증가 전 값+1
--x감소한 뒤 값−1
x--감소 전 값−1

다음 출력을 쓰시오.

int x = 5;
int a = x++;
int b = ++x;
int c = x-- + 10;
cout << a << ' ' << b << ' ' << c << ' ' << x << '\n';
정답 확인
5 7 17 6
문장식의 값실행 후 x
a = x++56
b = ++x77
c = x-- + 107 + 10 = 176

비교 연산자와 = / ==

비교 결과는 bool이고 출력하면 1 또는 0입니다.

논리 연산자와 0/1 변환

  • 조건식에서 0은 거짓, 0이 아닌 모든 값(음수 포함)은 참입니다.
  • !는 결과를 bool로 뒤집습니다. !5는 0, !0은 1, !(-3)은 0.
  • bool을 정수로 바꾸면 true → 1, false → 0. bool b = -5;는 true.

단락 평가 (Short-circuit)

식왼쪽이 이것이면오른쪽은
A && B거짓실행하지 않음 (결과 거짓)
A || B참실행하지 않음 (결과 참)

오른쪽에 ++, 함수 호출, 나눗셈이 있으면 실행 여부에 따라 결과가 달라집니다. 0으로 나누는 것을 막는 가드로도 씁니다: d != 0 && 10 / d > 2.

다음 출력을 쓰시오.

int x = 0, y = 0;
bool r1 = (x++ > 0) && (y++ > 0);
bool r2 = (x++ > 0) || (y++ > 0);
bool r3 = (x > 5) || (++y);
cout << r1 << r2 << r3 << ' ' << x << ' ' << y << '\n';
정답 확인
011 2 1
  • r1: x++ > 0은 0 > 0으로 거짓(x는 1이 됨) → y++는 실행 안 됨.
  • r2: x++ > 0은 1 > 0으로 참(x는 2) → y++는 실행 안 됨.
  • r3: x > 5가 거짓이므로 ++y를 실행 → y = 1, 0이 아니므로 참.

삼항(조건) 연산자 ?:

조건 ? 참일 때 값 : 거짓일 때 값
int max = (a > b) ? a : b;
cout << (n % 2 == 0 ? "even" : "odd");

cout과 함께 쓸 때는 괄호로 감싸야 합니다. <<가 ?:보다 우선순위가 높기 때문입니다.


7. 연산자 우선순위와 결합성

우선순위 표 (위가 먼저 묶임)

순위연산자결합 방향
1( ), a[i], f(), 후위 x++ x--→
2전위 ++x --x, 단항 ! - +, (type), sizeof←
3* / %→
4+ -→
5<< >> (cout 출력도 이것)→
6< <= > >=→
7== !=→
8&&→
9||→
10? :←
11= += -= *= /= %=←

외우는 순서: 단항 → 산술(곱·나눗셈·나머지 → 덧셈·뺄셈) → 비교(크기) → 같음 → AND → OR → 삼항 → 대입

계산하는 방법

  1. 단항 연산자(!, -, ++)를 먼저 피연산자에 붙입니다.
  2. 우선순위가 높은 연산자부터 괄호로 묶습니다.
  3. 같은 순위가 이어지면 왼쪽부터 묶습니다. (단항, 삼항, 대입만 오른쪽부터)
  4. 묶인 괄호를 안쪽부터 계산합니다. 단, &&, ||는 단락 평가로 오른쪽을 건너뛸 수 있습니다.
1 + 2 < 4 && 0 || 3 > 2
 
① 산술:   (1 + 2) < 4 && 0 || 3 > 2
② 비교:   ((1 + 2) < 4) && 0 || (3 > 2)
③ AND:    (((1 + 2) < 4) && 0) || (3 > 2)
④ OR:     전체
계산:     (1 && 0) || 1  →  0 || 1  →  1
!x == 0          →  (!x) == 0        ! 가 == 보다 먼저
-x % 3           →  (-x) % 3         단항 - 가 % 보다 먼저
a % b * 2        →  (a % b) * 2      같은 순위는 왼쪽부터
x < y < z        →  (x < y) < z      0 또는 1과 z를 비교 (수학의 범위 비교가 아님!)
x = (7 * 3 / 4 - 2) * 5 == y   →  x = ((((7*3)/4)-2)*5 == y)   == 가 = 보다 먼저

int a = 10, b = 3;일 때 각 식의 값을 쓰시오.

식
(1)2 + 3 * 4 % 5
(2)10 - 4 - 3
(3)!0 + 1
(4)5 > 3 == 1
(5)1 + 2 < 4 && 0 || 3 > 2
(6)a % b * 2 + a / b
(7)!a == 0
(8)-2 * -3
정답 확인
묶는 순서값
(1)2 + ((3 * 4) % 5) = 2 + (12 % 5)4
(2)(10 - 4) - 33
(3)(!0) + 1 = 1 + 12
(4)(5 > 3) == 1 = 1 == 11
(5)((3 < 4) && 0) || (3 > 2) = 0 || 11
(6)((10 % 3) * 2) + (10 / 3) = 2 + 35
(7)(!10) == 0 = 0 == 01
(8)(-2) * (-3)6

8. 조건문 if

if (score >= 90)      grade = 'A';
else if (score >= 80) grade = 'B';   // 위 조건이 거짓일 때만 검사
else                  grade = 'F';
  • else if 사슬은 처음 참이 된 분기 하나만 실행합니다. 그래서 범위가 좁은(높은) 조건을 먼저 씁니다.
  • if를 여러 개 따로 쓰면 각각 독립적으로 검사되어 여러 개가 실행될 수 있습니다.
  • 중괄호가 없으면 if는 바로 다음 한 문장만 지배합니다.

각 코드의 출력을 쓰시오.

// (1)
int x = 7;
if (x = 0) cout << "A"; else cout << "B";
cout << x << '\n';
 
// (2) a = 5일 때와 a = 1일 때
int b = 0;
if (a > 3)
    if (b > 3)
        cout << "X";
else
    cout << "Y";
cout << "Z\n";
 
// (3)
int s = 95;
if (s >= 90) cout << "A";
if (s >= 80) cout << "B";
else cout << "C";
cout << '\n';
정답 확인

(1) B0. x = 0은 대입이므로 x는 0이 되고 조건은 거짓입니다.

(2) a = 5 → YZ, a = 1 → Z. else는 안쪽 if (b > 3)의 짝입니다. a = 1이면 바깥 if가 거짓이라 안쪽 if-else 전체를 건너뜁니다.

(3) AB. 두 if는 독립적입니다. 두 번째 if의 else는 실행되지 않습니다.


9. switch

switch (정수식) {          // int, char, bool, enum 가능 (double, string 불가)
case 상수1:
    문장;
    break;                 // 없으면 아래 case로 계속 진행 (fall-through)
case 상수2:
case 상수3:                // 여러 값을 같은 처리로 묶기
    문장;
    break;
default:                   // 일치하는 case가 없을 때
    문장;
}

아래 sw(n)에 대해 sw(2), sw(4), sw(7)의 출력을 쓰시오. 그리고 sw2(5), sw2(2), sw2(1)의 출력을 쓰시오.

void sw(int n) {
    switch (n) {
    case 1: cout << "one ";
    case 2: cout << "two ";
    case 3: cout << "three "; break;
    case 4: cout << "four ";
    default: cout << "other ";
    }
}
 
void sw2(int n) {
    switch (n) {
    default: cout << "D";
    case 1: cout << "1"; break;
    case 2: cout << "2";
    }
}
정답 확인
호출출력이유
sw(2)two three case 2부터 실행, case 3의 break에서 멈춤
sw(4)four other break가 없어 default까지 실행
sw(7)other 일치하는 case가 없어 default로
sw2(5)D1default로 점프한 뒤 아래 case 1까지 진행
sw2(2)2case 2가 마지막이므로 여기서 끝
sw2(1)1case 1 실행 후 break

10. 반복문

세 반복문 비교

반복문조건 검사 시점최소 실행 횟수주로 쓰는 경우
while (조건) { }본문 전0횟수를 모르고 조건으로 반복
do { } while (조건);본문 후1입력을 먼저 받고 검사
for (초기식; 조건; 증감식) { }본문 전0횟수가 정해진 반복

for의 실행 순서: 초기식(한 번) → 조건 → 본문 → 증감식 → 조건 → 본문 → …

while ↔ for 변환

초기식;                          for (초기식; 조건; 증감식) {
while (조건) {          ↔            본문;
    본문;                        }
    증감식;
}

break와 continue

키워드동작
break가장 안쪽 반복문(또는 switch) 하나를 즉시 빠져나감
continue이번 회차의 나머지를 건너뛰고 다음 회차로

다음 출력을 쓰시오.

// (1)
int y = 1;
for (int i = 5; i > 0; i--) {
    if (i % 2 == 1) y = y + i;
    else y++;
    cout << y << ' ';
}
 
// (2)
int i = 10;
do { cout << i; i++; } while (i < 5);
cout << " i=" << i;
 
// (3)
int k;
for (k = 0; k < 10; k += 3);
cout << k;
 
// (4)
int n = 1, total = 0;
while (n < 20) {
    if (n % 3 == 0) { n++; continue; }
    if (n > 10) break;
    total += n;
    n++;
}
cout << total << ' ' << n;
정답 확인

(1) 6 7 10 11 12 — i = 5, 3, 1(홀수)에서는 i를 더하고, 4, 2에서는 1을 더합니다.

(2) 10 i=11 — do while은 조건(10 < 5)이 거짓이어도 한 번 실행합니다.

(3) 12 — for 뒤의 ; 때문에 본문이 비어 있습니다. k = 0, 3, 6, 9, 12에서 조건이 거짓이 되어 멈춥니다.

(4) 37 11 — 3의 배수(3, 6, 9)는 건너뛰고, 1 + 2 + 4 + 5 + 7 + 8 + 10 = 37. n = 11에서 break.

다음 while문을 같은 동작의 for문으로 바꾸고, 반복이 끝난 뒤 sum과 j의 값을 쓰시오. 두 변수는 반복문 밖에서도 사용해야 한다.

// (1)
int sum = 0;
int j = 0;
while (j <= 10) {
    sum = sum + j;
    j++;
}
 
// (2)
int sum = 0;
int j = 5;
while (j > 0)
    sum = sum + j--;
정답 확인
// (1)
int sum = 0;
int j = 0;
for (; j <= 10; j++)
    sum = sum + j;
 
// (2)
int sum = 0;
int j = 5;
for (; j > 0; j--)
    sum = sum + j;

(1) sum = 55, j = 11. (2) sum = 15, j = 0. (2)의 j--는 후위이므로 현재 값을 더한 뒤 감소합니다. for로 옮길 때는 감소를 증감식 자리로 빼야 합니다. 초기식을 for 안에서 int j = 0으로 선언하면 반복 후 j를 쓸 수 없습니다.


11. 중첩 반복문

바깥 반복이 한 번 돌 때마다 안쪽 반복이 처음부터 끝까지 돕니다. 안쪽 반복의 범위가 바깥 변수에 따라 바뀌면 표를 그려 횟수를 셉니다.

for (int i = 1; i <= 4; i++) {        // 줄
    for (int j = 1; j <= i; j++)      // i번 반복
        cout << '*';
    cout << '\n';
}
*
**
***
****

다음 출력을 쓰시오.

// (1)
int cnt = 0;
for (int i = 0; i < 4; i++)
    for (int j = i; j < 4; j++)
        cnt++;
cout << cnt << '\n';
 
// (2)
for (int i = 1; i <= 3; i++)
    for (int j = 1; j <= 3; j++) {
        if (j == 2) break;
        cout << i << j << ' ';
    }
정답 확인

(1) 10 — i = 0, 1, 2, 3일 때 안쪽이 4, 3, 2, 1번 돌아 4 + 3 + 2 + 1 = 10.

(2) 11 21 31 — break는 안쪽 반복만 끝냅니다. 바깥 반복은 계속되므로 i마다 j = 1만 출력됩니다.


12. 범위 기반 for

vector<int> v = {1, 2, 3};
for (int x : v) cout << x;        // 복사본으로 읽기
for (int& x : v) x *= 2;          // 참조로 원본 수정
for (const int& x : v) cout << x; // 복사 없이 읽기만

13. 컴파일 오류와 정의되지 않은 동작 구분

시험에서는 "컴파일 오류 / 실행 시 문제(UB) / 정상"을 구분하라고 자주 묻습니다.

코드판정이유
char g = "A";컴파일 오류문자열을 char에 저장할 수 없음
int k{3.5};컴파일 오류중괄호 초기화의 축소 변환 금지
do { … } while (x) (세미콜론 없음)컴파일 오류문법 오류
5.5 % 2컴파일 오류%는 정수 전용
int x; cout << x;UB초기화하지 않은 값 읽기
int r = 10 / 0;UB정수를 0으로 나눔
int big = 2147483647; big++;UBsigned 오버플로
i++ + ++iUB한 식에서 같은 변수를 두 번 변경
unsigned int u = -1;정상4294967295로 정해져 있음
if (x = 5)정상(논리 오류)대입식이 항상 참
int i = 0; while (i < 5) { }정상(무한 반복)i가 바뀌지 않음

핵심 정리

  1. char는 정수 코드다. 'A'=65, 'a'=97, '0'=48. 산술하면 int가 된다.
  2. 정수끼리 나누면 0 쪽으로 절단되고, %의 부호는 왼쪽 피연산자를 따른다.
  3. 형변환은 나누기 전에 해야 실수 결과가 나온다. 실수 → 정수는 0 쪽으로 버린다.
  4. signed 오버플로는 UB, unsigned는 돌아간다(0 − 1 = 4294967295). signed/unsigned 비교는 위험하다.
  5. 우선순위: 단항 → 산술 → 비교 → 같음 → && → || → ?: → 대입. 같은 순위는 왼쪽부터(단항·삼항·대입은 오른쪽부터).
  6. &&는 왼쪽이 거짓, ||는 왼쪽이 참이면 오른쪽을 실행하지 않는다.
  7. else는 가장 가까운 짝 없는 if와 짝이다. if (x = 0)은 대입이다.
  8. switch는 break가 없으면 아래 case로 계속 실행된다.
  9. do while은 최소 한 번 실행된다. continue는 for에서는 증감식으로, while에서는 조건으로 간다.
  10. break는 가장 안쪽 반복문 하나만 빠져나간다.
오늘도 한 걸음 앞으로.

내용을 이해했다면 학습 완료를 체크해 주세요.

학습 기록은 현재 브라우저에 저장됩니다.
HJS / STUDY한지석