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

시험 직전 정리: 헷갈리는 개념

시험 직전 정리: 헷갈리는 개념

시험장 들어가기 전에 이 페이지만 훑으세요. 각 항목은 규칙 → 헷갈리는 코드 → 답 순서이고, 모든 출력은 실제로 컴파일해 확인한 값입니다. 자세한 설명은 항목 끝의 강의 링크에 있습니다.

범위강의족보 단골 유형
중간01 자료형·연산자·흐름 제어 · 02 함수·재귀·스코프 · 03 참조·배열·vector반복문 출력 추적, while↔for 변환, static 추적, 재귀 값 + 반복문 변환, 참조 오류 찾기, 단락 평가, 컴파일 오류 vs UB
기말04 포인터 · 05 클래스 · 06 클래스 고급·예외 · 07 템플릿접근 O/X 표, virtual 출력, 클래스 작성, 예외 출력(throw 자료형 바꾸기), 템플릿 작성, Factorial<0>

중간고사 범위

1. 문자는 정수다

  • 'A' = 65, 'a' = 97, '0' = 48, '\0' = 0. 대문자가 소문자보다 작다.
  • char끼리 산술하면 int가 된다. 다시 문자로 보려면 char(…).
char c = 'A';
cout << c + 1 << ' ' << char(c + 1) << ' ' << '7' - '0' << ' ' << ('a' < 'B');

→ 66 B 7 0

2. 정수 나눗셈과 %

  • 정수 ÷ 정수는 0 쪽으로 절단: 7 / 2 = 3, -7 / 2 = −3.
  • %의 부호는 왼쪽 피연산자: -7 % 3 = −1, 7 % -3 = 1.
  • 홀수 판별은 n % 2 != 0 (n % 2 == 1은 음수에서 틀림).
  • double x = 7 / 2; → 3 (나눈 뒤 변환). (double)7 / 2 → 3.5 (나누기 전 변환). (double)(7 / 2) → 3.
  • int n = -3.99; → −3 (내림이 아니라 0 쪽으로).

자세히: 01강 3–4절

3. unsigned에서 −1이 되면

4. 연산자 우선순위 계산하는 법

1 + 2 < 4 && 0 || 3 > 2
→ (((1 + 2) < 4) && 0) || (3 > 2)
→ (1 && 0) || 1  →  0 || 1  →  1
식묶음값 (a = 10, b = 3)
2 + 3 * 4 % 52 + ((3 * 4) % 5)4
!0 + 1(!0) + 12
!a == 0(!a) == 01
5 > 3 == 1(5 > 3) == 11
a % b * 2 + a / b((a % b) * 2) + (a / b)5
x < y < z(x < y) < z0/1과 z 비교 (범위 비교 아님!)
x = y = 10x = (y = 10)둘 다 10

5. 증감과 단락 평가

  • x++: 식의 값은 증가 전. ++x: 증가 후. x = 5; a = x++; b = ++x; c = x-- + 10; → a 5, b 7, c 17, x 6.
  • A && B: A가 거짓이면 B 실행 안 함. A || B: A가 참이면 B 실행 안 함.
int x = 0;
false && (++x);   // x = 0
true  || (++x);   // x = 0
true  && (++x);   // x = 1
false || (++x);   // x = 2
  • 같은 식에서 같은 변수를 두 번 바꾸면 UB: i++ + ++i.

6. = / ==, 매달린 else

  • if (x = 0) → 대입. 조건은 항상 거짓, x는 0이 됨. if (x = 5) → 항상 참. 둘 다 컴파일은 됨.
  • else는 들여쓰기와 상관없이 가장 가까운 짝 없는 if의 짝.
  • if를 따로따로 쓰면 각각 검사 → 여러 개 실행 가능. else if는 처음 참인 것 하나만.

7. switch: break가 없으면 계속 내려간다

switch (n) {
case 1: cout << "one ";
case 2: cout << "two ";
case 3: cout << "three "; break;
case 4: cout << "four ";
default: cout << "other ";
}
n출력
2two three
4four other
7other
  • default가 맨 위에 있어도 "일치하는 case가 없을 때 들어가는 위치"일 뿐, 그 아래로 계속 실행된다.
  • case에는 정수형 상수만 (double, string, 변수 불가).

8. 반복문: 변환과 끝난 뒤의 값

while ↔ for 변환 공식

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

족보형: y = 1; for (i = 5; i > 0; i--) { if (i % 2 == 1) y = y + i; else y++; cout << y; } → 6 7 10 11 12

9. 값 전달 vs 참조 전달

매개변수원본넘길 수 있는 것
int x안 바뀜 (복사)변수, 리터럴, 식
int& x바뀜수정 가능한 변수만 (f(5), f(a + 1), const 변수 → 컴파일 오류)
const int& x못 바꿈모두
int a[] (배열)바뀜배열
vector<int> v안 바뀜 (복사)
  • next(i);만 쓰고 반환값을 버리면 i는 그대로 → while (i < 5) next(i);는 무한 반복(논리 오류). i = next(i);로.

10. 참조 초기화 규칙

11. static 지역 변수

int next_value() {
    static int saved = 1;
    int local = 1;
    saved += 2;
    local += 2;
    return saved + local;
}
cout << next_value() << ' ' << next_value();     // 6 8
int r(int n) { static int c = 0; c++; if (n == 0) return c; return r(n - 1); }
cout << r(3) << ' '; cout << r(2);               // 4 7  (재귀끼리도 공유)
  • static과 전역 변수는 초기값이 없으면 0, 일반 지역 변수는 쓰레기 값.
  • 추적표에 시작 m / 종료 m 열을 꼭 만들 것 (족보 1번: 같은 코드에서 static 유무로 결과가 3, 5, 6, 8, 9 vs 3, 4, 4, 5, 5).

12. 스코프와 가리기

  • 안쪽에 같은 이름을 선언하면 바깥 이름이 가려짐. 가려진 전역은 ::x.
  • 함수는 자기가 정의된 곳에서 보이는 이름을 씀 (호출한 쪽의 지역 변수와 무관).
  • for (int i …)의 i는 반복문 밖에서 쓸 수 없음.

13. 재귀 값 구하기와 for / while 변환

함수기저점화식값
if (x <= 0) return 5; return f(x-1) + f(x-1);f(0) = 52·f(x−1)f(4) = 80, f(n) = 5·2ⁿ
int i = x - 1; if (i <= 0) return 5; …같은 식x ≤ 1에서 52·f(x−1)f(1)=5, f(4) = 40 (한 칸 밀림)
if (x <= 0) return 1; …같은 식f(0) = 12·f(x−1)f(n) = 2ⁿ, f(4) = 16
if (x <= 0) return x + 1; return g(x-1)*3번 합;g(0) = 13·g(x−1)g(3) = 27, g(−2) = −1
위 g에서 기저를 x < 0으로g(−1) = 03·g(x−1)모두 0
int fFor(int x) {                 // f(x) = 2 f(x-1), f(0) = 5
    int result = 5;
    for (int i = 1; i <= x; i++) result *= 2;
    return result;                // x <= 0이면 반복 0번 → 5
}
int fWhile(int x) {
    int result = 5, i = 1;
    while (i <= x) { result *= 2; i++; }
    return result;
}
  • 재귀 호출 앞의 출력은 내려가며, 뒤의 출력은 올라오며(역순) 실행: p(3)이 cout << n; p(n-1); cout << n;이면 321123.
  • 기저 조건이 n == 1이면 f(0)은 끝나지 않음 → n <= 1로.

14. 함수 오버로딩·기본 인수·매크로

  • 반환형만 다르면 오버로딩 불가. 모호한 호출(f(3L)에 f(int), f(double), f(char))은 컴파일 오류.
  • 기본 인수는 오른쪽부터: int bad(int a = 1, int b); 오류.
  • #define SQ(x) x * x → SQ(a + 1) = a + 1 * a + 1 (a = 3이면 7), 100 / SQ(5) = 100 / 5 * 5 = 100. 괄호를 넣은 ((x) * (x))면 16, 4.

15. 배열

  • 배열을 함수에 넘기면 원본이 바뀜 + 크기는 따로 넘김. 원소 하나(a[0])를 값으로 넘기면 복사본.
  • 최댓값은 max = a[0]으로 시작 (0으로 시작하면 음수만 있을 때 틀림).

16. 문자 배열과 '\0'

  • char s[] = "Hi C++"; → sizeof(s) = 7 (문자 6 + '\0'), 길이 6.
  • char w[5] = "Hello"; → 컴파일 오류 (6칸 필요).
  • 끝 검사는 s[i] != '\0' (숫자 '0'이 아님!). 빈도는 count[s[i] - 'a']++, 배열은 {0}으로 초기화.

17. 2차원 배열

  • m[i][j]: i = 행, j = 열. 행 합은 바깥 i / 안쪽 j, 열 합은 바깥 j / 안쪽 i.
  • 메모리는 행 우선(한 줄로): m[0][0] m[0][1] m[0][2] m[1][0] …
  • 대각선 m[i][i], 반대 대각선 m[i][N-1-i], 아래 삼각형 i >= j.
  • 3×4 {{1,2,3,4},{5,6,7,8},{9,10,11,12}} → 행 합 10 26 42, 열 합 15 18 21 24.

18. 정렬

  • 버블: 이웃 비교, 패스마다 최댓값이 맨 뒤 확정. 교환이 없으면 종료. {5,1,4,2,8} → pass1 1 4 2 5 8, pass2 1 2 4 5 8, pass3 교환 없음.
  • 선택: 남은 것 중 최솟값을 앞으로, 패스당 교환 1번. {64,25,12,22,11} → 11 25 12 22 64 → 11 12 25 22 64 → 11 12 22 25 64.

19. 컴파일 오류 / UB / 정상 판정표

코드판정
선언 안 된 함수 호출, 이름 오타컴파일 오류
int& r;, int& r = 5;컴파일 오류
const 변수·참조 수정컴파일 오류
int a[3] = {1,2,3,4};, 배열 대입컴파일 오류
char g = "A";, int k{3.5};, 5.5 % 2컴파일 오류
초기화 안 한 변수 읽기, 배열 범위 초과UB
정수 ÷ 0, signed 오버플로, i++ + ++iUB
unsigned u = -1;정상 (4294967295)
if (x = 5), 갱신 없는 while정상 컴파일, 논리 오류

기말고사 범위

20. 포인터 기본: 화살표를 그린다

  • p = q → 주소 복사(화살표 이동). *p = *q → 값 복사.
  • int a = 10, b = 20; int* p = &a, * q = &b; *p = *q + 5; p = q; *p = 7; → a 25, b 7.
  • int n = 3; int& k = n; int* p = &n; → p[0] == n 참, p = &k; 가능(같은 주소), arr[i] == *(arr + i).
  • int* p, q; → q는 포인터가 아님.

21. const 포인터

선언*p = 값p = 다른주소
const int* pXO
int* const pOX
const int* const pXX
  • 규칙: const가 *의 왼쪽 → 값, 오른쪽 → 포인터 고정.
  • const int c; int* p = &c; → 컴파일 오류.

22. 포인터로 swap / *p++

void s3(int* a, int* b) { int* t = a; a = b; b = t; }   // 지역 포인터만 교환 → 원본 그대로!
  • *p++ = *(p++) → 포인터 이동. (*p)++ → 값 증가.
  • {10, 20, 30, 40}에서 *p++, (*p)++, *++p, ++*p 순서 → 출력 10 20 30 31, 배열 10 21 31 40.
  • 배열 이름은 대입·증가 불가(a = p, a++ 오류). sizeof(a) = 20, sizeof(p) = 8.

23. C 문자열 함수

  • 비교는 strcmp(a, b) == 0 (==는 주소 비교). strcmp("Zoo", "apple") < 0 (대문자가 앞).
  • 대입은 strcpy(name, "Kim") (name = "Kim" 오류). 빈 문자열은 strcpy(name, "") 또는 name[0] = '\0'.
  • strlen = 문자 수, sizeof = 배열 크기. char a[20] = "Hello"; → 5 vs 20.

24. new / delete

  • new ↔ delete, new[] ↔ delete[]. 틀리면 UB.
  • 누수: 해제 없이 주소를 잃음. 댕글링: 해제 후 사용. 해제 후 p = nullptr;.

25. 접근 O/X 표 푸는 법

class A { public: int x; A* pa; protected: int y; private: int z; }; class B : public A { public: A objA; }; class C : public B { public: C* pc; B objB; };

표현in Ain Bin C
yOOO
zOXX
objA.yXXX
pa->zOXX
objB.yXXX
pc->yXXO

26. 상속 종류별 접근

기반 멤버public 상속protected 상속private 상속
publicpublicprotectedprivate
protectedprotectedprotectedprivate
private접근 불가접근 불가접근 불가
  • class D : A (지정자 생략) → private 상속. struct는 public.
  • private 상속하면 손자 클래스에서도 x, y 접근 불가.

27. 생성자·소멸자

  • 생성자를 하나라도 만들면 기본 생성자는 사라진다 → P a;, P arr[3]; 오류.
  • 순서: 기반 → 멤버(선언 순서) → 자신, 소멸은 반대. 초기화 목록에 쓴 순서는 무시.
  • Derived() : p2("2"), p1("1") → B P(1) P(2) D | ~D ~P(2) ~P(1) ~B
  • 지역 객체는 생성 역순 소멸, 전역은 main 전 생성·main 후 소멸, new는 delete할 때만 소멸.
  • const 멤버·참조 멤버는 초기화 목록에서만 초기화.

28. 복사 생성자 vs 대입

코드호출
T b = a;, T c(a);복사 생성자
c = b;대입 연산자
f(T t)에 f(a), catch (T e)복사 생성자
f(T& t)없음
  • 포인터 멤버의 기본 복사 = 얕은 복사(같은 메모리 공유, 이중 해제) → 깊은 복사 생성자 작성.

29. virtual 출력 규칙

호출 (C2 객체)일반 funAvirtual funB
C1* o2 = new C2; o2->C1C2
C2* o3 = new C2; o3->C2C2
C1* o1 = new C1; o1->C1C1
  • 기반 포인터로 delete할 거면 virtual 소멸자. 없으면 ~Derived가 안 불림.
  • 파생 클래스에 같은 이름을 선언하면 기반의 같은 이름 함수가 모두 가려짐(d.f() 오류, d.H::f()는 OK).
  • 순수 가상 함수(= 0)가 있으면 추상 클래스 → 객체 생성 불가.

30. static 멤버, this, 연산자 오버로딩

  • static 데이터 멤버는 클래스 밖에서 int Player::count = 0;으로 정의. static 함수엔 this 없음 → 일반 멤버 사용 불가.
  • Counter& add()는 같은 객체에 연결, Counter addCopy()는 복사본 반환 → c.addCopy(1).addCopy(1)은 c에 한 번만 반영.
  • 2 * p, cout << p는 왼쪽이 내 클래스가 아니므로 비멤버(friend). operator<<는 ostream& 반환.
  • 전위 operator++() → 증가 후 *this 참조, 후위 operator++(int) → 증가 전 복사본.

31. 예외: throw 자료형에 따른 출력

fun: catch (int), catch (char) { …; throw; } / main: catch (int), catch (double), catch (...)

throw출력
(double)5.0A FA D 5 F
3A FA FCA 3 BC B F
'c'A FA FCB c E F
trueA FA E F
"K"A FA E F
  • throw 아래 문장(BA)은 실행 안 됨. catch가 처리하면 try-catch 다음 문장으로.
  • throw;는 같은 예외를 호출한 쪽으로 다시 던짐 → fun(); throw (double)2.0;에서 두 번째 throw는 실행 안 됨 → in catch CC.
  • try와 catch 사이 문장 불가(참), catch (...)는 마지막.
  • 전파될 때 지역 객체는 역순 소멸, 그 뒤에 catch 실행: main+ out+ in+ in- out- main- catch1.

32. 템플릿

  • 원본을 바꾸는 change는 T&:
template <typename T>
void change(T& a, T& b) { T t = a; a = b; b = t; }
  • 추론은 변환 없음: maximum(3, 4.5) 오류, maximum<double>(3, 4.5) = 4.5, maximum<int>(3, 4.9) = 4.
  • 정확히 맞는 일반 함수가 우선: show(1) → 일반 함수, show<int>(1) → 템플릿.
  • 자료형마다 다른 함수 → static도 따로: counter(1) counter(2) counter(1.0) counter('a') counter(3) → 12113.
  • Factorial<5> → 120. Factorial<0>은 기저 조건(특수화). 없으면 Factorial<-1>, <-2> …를 끝없이 만들어 컴파일 오류.

마지막 1분 체크리스트

  • 7 / 2 = 3, -7 % 3 = -1, (double) 위치
  • unsigned 0 − 1 = 4294967295, signed/unsigned 비교
  • 단항 → 산술 → 비교 → 같음 → && → || → ?: → =
  • && 왼쪽 거짓 / || 왼쪽 참이면 오른쪽 안 함
  • switch는 break까지 내려감
  • 반복 끝난 뒤 변수 값, for(…);, continue 위치
  • static은 한 번만 초기화, 재귀끼리도 공유
  • 재귀 기저 위치 확인 → 표 → 반복문 4단계
  • int& r;, int& r = 5; 오류 / const int&는 OK
  • 배열 초기화·범위·함수 전달, '\0', 2차원 열 크기 필수
  • 포인터는 화살표, const 위치, *p++
  • protected는 자기 클래스 객체를 통해서만
  • 생성 기반→멤버→자신, 소멸 반대
  • virtual은 기반에 있어야, 포인터 자료형 vs 실제 객체
  • catch 자료형 정확히 일치, throw; 경로
  • 템플릿 T&, Factorial<0> = 기저
오늘도 한 걸음 앞으로.

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

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