시험공부

(2025-2) Introduction to programming(2) 2. Variables, types (pointer, references,string)

norepinephrine 2025. 12. 18. 14:43

수업일: 2025-09-08

학습목표: C++의 기본 타입과 초기화, 포인터·레퍼런스 차이, string 기본 인터페이스를 이해하고 전형적인 함정을 피한다.


🎯 학습 목표 (Exam Scope)

  • [ ] 객체/이름/주소 개념을 설명할 수 있다.
  • [ ] 레퍼런스와 포인터의 차이를 표와 코드로 설명할 수 있다.
  • [ ] 초기화 4종(=, (), {}, 디폴트) 차이를 말할 수 있다.
  • [ ] const/constexpr와 포인터-상수 조합을 읽고 쓸 수 있다.
  • [ ] auto/decltype의 추론 규칙(특히 decltype((x)))을 구분할 수 있다.
  • [ ] string의 생성/길이/비교/입력(>> vs getline)을 다룰 수 있다.
  • [ ] 부호 혼합 연산의 함정을 예로 설명할 수 있다.

🧠 큰 그림 요약

  • 객체(object)=상자, 이름(identifier)=이름표, 주소(address)=좌표
  • 레퍼런스=별명, 포인터=리모컨(주소 저장)
  • 초기화는 복사(=)/직접(())/리스트({})/디폴트 구분
  • const/constexpr: 읽기 전용 vs 컴파일타임 상수
  • *auto*는 편의 추론(보통 const 제거), **decltype*은 표현식 타입 그대로
  • string: 안전한 문자열, >>(단어) vs getline(한 줄)

1) 객체·이름·주소 (기초)

// 객체(object)=값을 담는 메모리 칸, 이름(identifier)=그 칸의 라벨, 주소(address)=칸의 좌표
int x = 10;        // x라는 이름표가 붙은 정수 상자에 10 저장
int* px = &x;      // &x: x의 주소(좌표)를 구함
std::cout << *px;  // *px: 주소를 따라가 x의 '값'을 읽음 (10)

  • [ ] &x는 주소, *px는 역참조(값 접근) 이라는 점을 혼동하지 않는다.

2) 레퍼런스 vs 포인터 (시험 단골)

✅ 한 장 요약 표

항목 레퍼런스(reference) 포인터(pointer)

정체 객체 아님(별도 저장공간 X), 기존 변수의 별명 객체(주소를 저장하는 변수)
초기화 즉시 바인딩 필수 (int& r=a;) 선언만 가능, 나중에 대입 가능
재바인딩 불가 (항상 같은 대상) 가능 (p=&b; 로 대상 변경)
없음 nullptr 가능
연산 특별한 연산 거의 없음 *(역참조), &(주소), 산술/비교 가능
용도 안전한 별칭(복사비용 없이 전달) 저수준 제어(재지정/널/배열과 호환)

예제

int a = 10;

// 레퍼런스: a의 '다른 이름'
int& r = a;      // 반드시 즉시 초기화
r = 20;          // a가 20으로 변함

// 포인터: a의 주소를 들고 다니는 '리모컨'
int* p = &a;     // p 안에 a의 주소 저장
*p = 30;         // a가 30으로 변함
p = nullptr;     // 이제 아무 것도 가리키지 않음(null)

  • [ ] int& r; 처럼 레퍼런스를 비워둘 수 없다(컴파일 에러).
  • [ ] p 사용 전 p가 무엇의 주소인지를 확인하자

3) 초기화 4종 ( =, (), {}, default )

유형 예시 특징

default int x; 지역변수는 미정의값(주의!), 전역/정적은 0
복사 int x = 3; 대입 형태
직접 int x(3); 생성자/형변환과 연계(클래스에서 중요)
리스트 int x{3}; 좁은 변환 금지로 안전, 컨테이너 초기화에 좋음
int a;        // (지역) 미정의 값 → 사용 금지!
int b = 0;    // 복사 초기화
int c(0);     // 직접 초기화
int d{0};     // 리스트 초기화(권장)

  • [ ] 지역변수 디폴트 초기화의 미정의값을 시험에서 주의할 것.

4) const / constexpr & 포인터-상수 조합

int x = 1, y = 2;

const int* p1 = &x; // '가리키는 값'이 상수(읽기만), 포인터는 변경 가능
// *p1 = 3;         // ❌ but, x = 3; 은 OK / x가 const int 가 되는 것은 아님
p1 = &y;            // OK (*p1 = 2) 

int* const p2 = &x; // '포인터 자체'가 상수(변경 불가), 값은 수정 가능
*p2 = 4;            // OK
// p2 = &y;         // ❌ 

const int* const p3 = &x; // 둘 다 상수

  • [ ] const가 별(*) 오른쪽이면 대상이 상수, 식별자 옆이면 포인터 자체 상수
  • [ ] constexpr int N = 10; 은 컴파일타임 상수로 배열 크기 등 정적 문맥에 사용.

5) auto / decltype (타입 추론 규칙)

const int ci = 42;

auto a = ci;        // a는 int (보통 const 제거)
decltype(ci) b = 0; // b는 const int

int v = 0;
decltype((v)) r = v; // r은 int&  ← 괄호로 '좌값 표현식'이 되어 참조 타입

  • [ ] auto는 주로 상수성 제거, decltype은 표현식 그대로 보존.
  • [ ] 함정: decltype((x))는 거의 항상 참조.

6) 부호/크기/형변환 (혼합 연산 주의)

unsigned u = 1;
int i = -2;
std::cout << (u > i) << "\\n";  // 보통 1(true): i가 unsigned로 승격되어 큰 값으로 비교됨

  • [ ] 부호 있는/없는 정수 혼합 연산 금지(명시 캐스트로 타입을 맞추자).
  • [ ] sizeof(T)로 타입 크기는 구현 의존임을 기억.

7) string 기본기 (>> vs getline)

#include <string>
#include <iostream>
using namespace std;

int main() {
    string s1;                 // ""
    string s2 = "hi";          // 복사 초기화
    string s3("hi");           // 직접 초기화
    string s4{5, 'x'};         // "xxxxx"

    cout << s2.size() << "\\n"; // 길이(부호 없는 정수)
    cout << boolalpha << s1.empty() << "\\n"; // true

    string token, line;
    cin >> token;              // 공백 전까지 읽음
    cin.ignore(numeric_limits<streamsize>::max(), '\\n'); // 남은 줄 비우기
    getline(cin, line);        // 공백 포함 한 줄 전체
}

  • [ ] "text"는 const char[] 리터럴, string과 구분 (필요 시 암시 변환).
  • [ ] >>는 토큰 단위, getline은 줄 단위.

8) 배열 ↔ 포인터 연결감 (기초)

int arr[3] = {10, 20, 30};
int* p = arr;               // &arr[0]로 decay
std::cout << *(p + 1);      // 20

  • [ ] nullptr 역참조는 미정의 동작(금지).

9) 미니 실습 스니펫

(A) 레퍼런스/포인터 동작 감각 익히기

#include <iostream>
using namespace std;

int main() {
    int a = 10;
    int& r = a;    // a의 별명
    int* p = &a;   // a의 주소

    r = 20;        // a=20
    *p = 30;       // a=30

    int b = 99;
    // r = b;     // r을 b에 다시 붙이는 게 아님! (a=b와 동일 효과)
    p = &b;        // 포인터는 재지정 가능
    cout << a << " " << *p << "\\n"; // 30 99
}

(B) 한 줄 입력 안전 패턴

#include <iostream>
#include <string>
#include <limits>
using namespace std;

int main() {
    string token, line;
    cout << "단어 하나: ";
    cin >> token;
    cin.ignore(numeric_limits<streamsize>::max(), '\\n'); // 버퍼 비우기
    cout << "한 줄: ";
    getline(cin, line);
    cout << "[" << token << "] / [" << line << "]\\n";
}


🔎 예상문제 (Self-Check)

Q1. const int* p; vs int* const p; 차이?

정답: 전자는 대상 상수, 후자는 포인터 자체 상수.

Q2. 아래 출력이 true(1)인 이유?

unsigned u=1; int i=-2; cout << (u > i);

정답: i가 unsigned로 승격되어 큰 값으로 비교됨.

Q3. 컴파일 에러는?

int a=10;
int& r;        // (A)
int* p; p=&a;  // (B)
int& r2=a;     // (C)

정답: (A) — 레퍼런스는 즉시 초기화 필요.

Q4. 공백 포함 한 줄 입력은?

정답: getline(cin, s).

Q5. 타입 추론 결과?

const int ci=0;
auto x=ci;          // x: int
decltype(ci) y=1;   // y: const int
int v=0; decltype((v)) z=v; // z: int&   (괄호 때문에 참조)


🧪 한 화면 요약 (암기 포인트)

  • 레퍼런스=별명(객체X), 포인터=리모컨(객체)
  • (역참조)/&(주소)/nullptr 구분
  • 초기화: 디폴트(미정의), 복사(=), 직접(()), 리스트({}=안전)
  • const 위치로 의미 판별(대상 vs 포인터 자체)
  • auto는 보통 const 제거, decltype은 그대로
  • string: size(), empty(), 비교, +=, >> vs getline
  • 부호 혼합 연산 주의(특히 unsigned ↔ int)

✅ 복습 체크리스트 (To-Do)

  • [ ] 레퍼런스/포인터 표를 빈칸 없이 암기한다.
  • [ ] {} 리스트 초기화 예시를 직접 타이핑해 본다.
  • [ ] const+포인터 조합 3종을 스스로 선언/설명해 본다.
  • [ ] decltype((x))가 참조가 되는 이유를 문장으로 설명해 본다.
  • [ ] >> 뒤 getline 사용 시 ignore 패턴을 적용해 본다.
  • [ ] unsigned/int 혼합 비교의 반례를 하나 만들고 설명한다.