C++ 변수와 자료형: int, double, string과 초보자가 자주 하는 실수
이 글의 핵심
C++ 기본 자료형의 크기는 표준이 최소 범위만 정하고 나머지는 플랫폼에 맡기기 때문에, 같은 코드가 Windows와 Linux에서 다르게 동작할 수 있습니다. 자료형별 범위와 용도, 초기화 방법, 형변환 규칙을 정리하고 정수 나눗셈·오버플로·unsigned 비교처럼 컴파일은 되는데 결과가 틀리는 실수를 예제로 짚습니다.
C++ 기본 자료형 한눈에 보기
| 자료형 | 크기 | 범위 | 용도 |
|---|---|---|---|
| int | 4바이트 | -2^31 ~ 2^31-1 | 정수 |
| long long | 8바이트 | -2^63 ~ 2^63-1 | 큰 정수 |
| float | 4바이트 | 약 ±3.4e38 | 실수 (정밀도 낮음) |
| double | 8바이트 | 약 ±1.7e308 | 실수 (정밀도 높음) |
| char | 1바이트 | -128 ~ 127 또는 0 ~ 255 (플랫폼마다 다름) | 문자 |
| bool | 1바이트 (대부분의 구현) | true/false | 논리값 |
| string | 가변 | - | 문자열 (STL) |
표의 크기는 오늘날 가장 흔한 64비트 데스크톱·서버 환경 기준입니다. C++ 표준은 각 타입의 최소 범위만 정하고(int는 최소 16비트, long long은 최소 64비트) 정확한 크기는 컴파일러와 플랫폼에 맡깁니다. 그래서 크기를 외우기보다 sizeof(int)나 std::numeric_limits<int>::max()(<limits> 헤더)로 직접 확인하는 습관이 더 유용합니다.
자료형 심화
정수형 크기: int, long, long long
C++에서 정수형의 정확한 바이트 수는 구현과 플랫폼에 따라 달라질 수 있습니다. 실무에서는 <cstdint>의 고정 크기 타입(int32_t, int64_t 등)을 쓰기도 하지만, 초보 단계에서는 표준 정수형의 관계만 이해하면 됩니다.
- int: 대부분의 환경에서 4바이트(32비트). 일상적인 정수 연산의 기본 선택입니다.
- long: Windows MSVC에서는
int와 같이 4바이트인 경우가 많고, Linux 64비트에서는long이 8바이트인 경우도 있습니다. 이식성이 필요하면long에 의존하지 말고 범위가 크면long long을 쓰는 편이 안전합니다. - long long: 최소 64비트 이상이 보장됩니다. 큰 수, 오버플로우를 피해야 하는 누적 합, 파일 크기 등에 적합합니다.
#include <iostream>
int main() {
int a = 2147483647; // int 최댓값 근처
long long b = 2147483648LL; // int를 넘는 값은 LL 접미사 권장
std::cout << a << ", " << b << "\n";
return 0;
}
요약: int로 충분한지 먼저 보며, 범위가 부족하면 long long으로 올리세요. long은 플랫폼별 차이를 기억해 두세요.
long의 크기가 운영체제마다 다른 이유는 데이터 모델이 다르기 때문입니다. 64비트 Linux와 macOS는 LP64 모델(long과 포인터가 64비트)을, 64비트 Windows는 LLP64 모델(long long과 포인터만 64비트, long은 32비트)을 씁니다. Linux에서 long에 파일 크기나 타임스탬프를 담던 코드를 Windows로 옮기면, 컴파일 경고 없이 4GB가 넘는 값에서 값이 잘립니다. 크기가 중요한 값(네트워크 패킷 필드, 파일 포맷, 해시)은 std::int32_t·std::int64_t처럼 비트 수가 이름에 드러나는 타입을 쓰면 이런 문제를 원천적으로 피할 수 있습니다.
2147483648LL에 접미사를 붙이는 이유도 짚어 둘 만합니다. 접미사 없는 정수 리터럴은 값이 들어가는 가장 작은 타입(int, 안 되면 long, 그다음 long long)으로 정해지므로 이 값은 사실 접미사가 없어도 담깁니다. 문제는 연산입니다. long long total = 100000 * 100000;은 오른쪽이 int끼리의 곱셈이라 대입 전에 이미 오버플로가 일어납니다. 결과를 담는 변수가 크다고 계산이 큰 타입으로 되는 것이 아니며, 100000LL * 100000처럼 피연산자 하나를 먼저 큰 타입으로 만들어야 합니다. 코딩 테스트에서 누적 합이 틀리는 가장 흔한 원인입니다.
부호 있는 타입과 없는 타입 (signed, unsigned)
- signed: 기본적으로
int,short등은 부호가 있습니다(음수·양수).signed int와int는 같은 의미입니다. - unsigned: 음수를 표현하지 않고 0 이상만 표현합니다. 같은 비트 폭이면 양의 범위가 약 두 배 넓어집니다.
unsigned int u = 4000000000U; // int로는 못 담을 수 있는 큰 양수
unsigned char flags = 255; // 0~255
주의: unsigned와 int를 섞어 연산하면 보통 unsigned 쪽으로 승격되어, 음수가 있던 변수가 거대한 양수로 해석되는 버그가 납니다. 비교·뺄셈 전에 타입을 맞추거나 static_cast로 의도를 명확히 하세요.
int x = -1;
unsigned int y = 10;
if (x < y) { } // 기대와 다르게 동작할 수 있음 (-1이 unsigned로 변환되면 큰 양수)
위 비교는 실제로 false입니다. -1이 unsigned int로 바뀌면 4294967295가 되기 때문입니다. 컴파일러는 -Wall -Wextra(GCC/Clang)를 켜면 comparison of integer expressions of different signedness: 'int' and 'unsigned int' 경고로 알려 주는데, 입문자가 가장 자주 만나는 형태는 for (int i = 0; i < v.size(); ++i)입니다. v.size()가 부호 없는 size_t라서 같은 경고가 나옵니다. 이 루프 자체는 i가 음수가 되지 않으므로 대체로 안전하지만, for (size_t i = v.size() - 1; i >= 0; --i)처럼 거꾸로 도는 루프는 i >= 0이 항상 참이라 끝나지 않고, 빈 벡터라면 v.size() - 1이 거대한 값이 되어 첫 접근부터 범위를 벗어납니다. “음수가 없는 값이니 unsigned”라는 직관이 오히려 버그를 만드는 전형적인 경우라서, 산술에 쓰이는 개수·인덱스도 부호 있는 타입을 쓰자는 의견(C++ Core Guidelines ES.102)이 많습니다.
실수형 정밀도: float vs double
| 구분 | float | double |
|---|---|---|
| 대략적인 유효 숫자 | 6~7자리 | 15~16자리 |
| 용도 | 메모리 절약, GPU 등 | 일반 계산의 기본 |
일반 애플리케이션에서는 double을 기본으로 쓰는 것이 좋습니다. 3.14 같은 리터럴도 C++에서는 기본적으로 double입니다. float 리터럴은 접미사 f를 붙입니다 (3.14f).
float f = 3.14f;
double d = 3.14; // double
auto x = 3.14; // 역시 double
“유효 숫자 15~16자리”는 정확히 표현되는 자릿수가 아니라 근사할 수 있는 자릿수입니다. double은 값을 2진 분수로 저장하므로 10진수 0.1은 정확히 표현되지 않고 가장 가까운 2진수 값으로 저장됩니다. 그래서 0.1 + 0.2 == 0.3은 false이고, std::cout << std::setprecision(17) << 0.1 + 0.2로 출력해 보면 0.30000000000000004가 나옵니다. 부동소수점 값을 ==로 비교하지 말고 std::abs(a - b) < 1e-9처럼 허용 오차를 두어 비교하며, 금액처럼 정확해야 하는 값은 원 단위 정수(long long)로 저장하는 편이 안전합니다.
char vs string
- char: 문자 하나를 담습니다(ASCII 한 글자 등). 작은따옴표
'A'를 사용합니다. - std::string: 문자열(여러 글자). 큰따옴표
"hello"를 사용합니다.
#include <string>
char letter = 'A'; // 한 글자
std::string name = "Hong"; // 문자열
// char wrong = "Hello"; // 오류: 문자열을 char에 넣을 수 없음
한글처럼 멀티바이트 문자는 한 char에 다 들어가지 않을 수 있습니다. 입문 단계에서는 ASCII와 std::string 위주로 이해하며, 유니코드가 필요하면 이후에 std::u8string 등을 별도로 학습하면 됩니다.
구체적으로 UTF-8에서 한글 한 글자는 3바이트입니다. 그래서 std::string s = "홍길동";의 s.size()는 3이 아니라 9이고, s[0]은 “홍”이 아니라 그 첫 바이트입니다. Windows 콘솔에서 한글이 깨져 나오는 문제도 대부분 여기서 비롯되는데, 소스 파일은 UTF-8로 저장했지만 콘솔은 CP949로 해석하기 때문입니다. MSVC라면 /utf-8 컴파일 옵션과 chcp 65001(콘솔 코드 페이지를 UTF-8로)을 함께 맞추면 해결되는 경우가 많습니다. 또 하나, 'A'와 "A"는 전혀 다른 타입입니다. 'A'는 char 값 65이고, "A"는 'A'와 끝을 표시하는 널 문자 '\0'을 담은 const char[2] 배열입니다.
변수 선언과 초기화
선언만 하기 vs 초기화하기
- 선언만: 변수의 이름과 타입만 정하며, 값은 아직 넣지 않습니다. 지역 변수는 읽기 전에 초기화하지 않으면 쓰레기 값이 들어갈 수 있습니다.
- 초기화: 선언과 동시에(또는 그 직후) 첫 값을 넣습니다. 가능하면 선언과 동시에 초기화하는 습관이 안전합니다.
int score = 100; // 선언 + 초기화 (권장)
double pi = 3.14159;
char grade = 'A';
bool passed = true;
std::string user = "홍길동";
기본 선언 예제
#include <iostream>
#include <string>
int main() {
// 선언만 (지역 변수는 초기화 전 사용 금지)
int age;
// 선언과 동시에 초기화 (권장)
int score = 100;
double pi = 3.14159;
char grade = 'A';
bool isPassed = true;
std::string name = "홍길동";
age = 20; // 나중에 대입
return 0;
}
중괄호 초기화 {} (Uniform initialization)
C++11 이후에는 중괄호 초기화를 많이 씁니다. 좁은 변환(narrowing)이 잡혀 실수를 줄여 줍니다.
int a{42};
double b{3.14};
int c = {7}; // 동일한 의미
// int x{3.14}; // 표준상 오류(narrowing) — 의도하지 않은 실수→정수 변환 방지
{} 초기화는 벡터 등 컨테이너 초기화와도 문법이 통일되어 읽기 좋습니다.
다만 컨테이너에서는 괄호와 중괄호의 의미가 달라지는 함정이 하나 있습니다. std::vector<int> v(3, 5);는 “5를 3개” 담은 {5, 5, 5}이지만, std::vector<int> v{3, 5};는 “3과 5 두 개”를 담은 {3, 5}입니다. 중괄호는 std::initializer_list를 받는 생성자를 우선하기 때문입니다. 기본 타입에서는 int a{};처럼 빈 중괄호로 0으로 초기화할 수 있다는 것도 알아 두면 좋습니다. int a;는 지역 변수라면 값이 정해지지 않지만 int a{};는 항상 0입니다.
auto 키워드
오른쪽 표현식의 타입을 컴파일러가 추론합니다. 타입이 길거나 복잡할 때 유용합니다.
auto x = 10; // int
auto y = 3.14; // double
auto name = std::string("Kim"); // std::string
auto z = "text";는 const char*로 잡힐 수 있어, std::string이 필요하면 명시적으로 std::string을 쓰는 편이 낫습니다. 이 차이가 드러나는 대표적인 예가 auto s = "Hello"; s += " World";로, const char*에는 +=가 없어 컴파일 에러가 납니다. C++14부터는 using namespace std::string_literals; 후 auto s = "Hello"s;처럼 s 접미사로 std::string 리터럴을 만들 수 있습니다.
const와 constexpr
- const: 실행 중에 바꿀 수 없는 읽기 전용 값입니다. 런타임에 결정되는 값으로 초기화할 수 있습니다.
const int MAX = 100;
const double PI = 3.14159;
// MAX = 200; // 오류
- constexpr: 컴파일 타임에 값이 확정되는 상수에 사용합니다. 배열 크기 등에 쓰면 성능·명확성에 유리합니다.
constexpr int buf_size = 1024;
int arr[buf_size]; // C++에서 constexpr 크기 허용
constexpr int square(int x) { return x * x; }
constexpr int nine = square(3);
입문 단계에서는 const로 “변하지 않는 변수”를 표현하며, 컴파일 상수가 필요할 때 constexpr을 추가로 학습하면 됩니다.
여러 변수 한 번에 선언
// 같은 타입 여러 개
int a = 1, b = 2, c = 3;
// 권장: 한 줄에 하나씩 (가독성·디버깅)
int a = 1;
int b = 2;
int c = 3;
타입 변환
암시적 변환 (자동)
연산자나 대입 시 컴파일러가 타입을 맞추기 위해 자동으로 변환합니다.
int i = 10;
double d = i; // int → double (대체로 안전)
double x = 9.99;
int j = x; // 소수 부분 버림 (9). 의도한 경우에만 사용
int a = 5, b = 2;
double r = a / b; // 정수 나눗셈 먼저 → 2.0 (암시적 변환의 함정)
작은 타입이 큰 타입으로 갈 때는 정보 손실이 적고, 큰 타입을 작은 타입으로 넣을 때는 잘림·오버플로우에 주의하세요.
명시적 캐스팅: static_cast
C 스타일 (double)a 대신 static_cast를 쓰면 검색이 쉽고 의도가 분명합니다.
double r1 = static_cast<double>(5) / 2; // 2.5
int rounded = static_cast<int>(3.99); // 3
포인터 간 변환 등은 다른 종류의 캐스트(reinterpret_cast 등)가 필요할 수 있지만, 변수와 기본 타입 사이에서는 static_cast가 기본입니다.
형 변환 주의사항
- 부호 있는 정수 ↔ unsigned: 부호 없는 쪽으로 맞추면 음수가 큰 양수로 바뀝니다.
- 큰 정수 → 작은 정수: 상위 비트가 잘려 값이 달라집니다.
- 실수 → 정수: 소수는 버려지며, 범위를 넘으면 정의되지 않은 동작이 될 수 있습니다.
- 정수 나눗셈: 피연산자가 모두 정수면 결과도 정수입니다. 실수 나눗셈이 필요하면 한쪽을
double로 바꾸세요.
이 중 1~3번은 컴파일러가 기본 설정에서는 조용히 넘어가는 경우가 많습니다. GCC/Clang에서 -Wconversion(부호 변환은 -Wsign-conversion)을 켜면 int j = x; 같은 암시적 좁힘 변환마다 경고가 나오므로, 처음 배울 때 한 번 켜 보면 어떤 대입이 값을 잃을 수 있는지 감을 잡는 데 큰 도움이 됩니다. 경고가 너무 많다면 앞에서 본 중괄호 초기화를 쓰는 것만으로도 좁힘 변환을 컴파일 에러로 바꿀 수 있습니다. 정수 나눗셈은 음수에서도 주의할 점이 있는데, C++11부터 나눗셈은 0 방향으로 잘리므로 -7 / 2는 -3, -7 % 2는 -1입니다. Python(-4와 1)과 결과가 다르다는 점을 알아 두면 다른 언어 코드를 옮길 때 헷갈리지 않습니다.
실전 예제
예제 1: 계산기 프로그램 (0으로 나누기 방지)
#include <iostream>
int main() {
double num1 = 0, num2 = 0;
char op = 0;
std::cout << "첫 번째 숫자: ";
if (!(std::cin >> num1)) {
std::cerr << "숫자가 아닙니다.\n";
return 1;
}
std::cout << "연산자 (+, -, *, /): ";
std::cin >> op;
std::cout << "두 번째 숫자: ";
if (!(std::cin >> num2)) {
std::cerr << "숫자가 아닙니다.\n";
return 1;
}
double result = 0;
bool ok = true;
if (op == '+') result = num1 + num2;
else if (op == '-') result = num1 - num2;
else if (op == '*') result = num1 * num2;
else if (op == '/') {
if (num2 == 0.0) {
std::cerr << "0으로 나눌 수 없습니다.\n";
return 1;
}
result = num1 / num2;
} else {
std::cerr << "지원하지 않는 연산자입니다.\n";
ok = false;
}
if (ok) std::cout << "결과: " << result << std::endl;
return 0;
}
double로 읽어 정수 나눗셈 실수를 피하며, 나눗셈 전에 0 여부를 검사했습니다.
예제 2: 온도 변환기 (섭씨 ↔ 화씨)
#include <iostream>
int main() {
double celsius = 0;
std::cout << "섭씨 온도: ";
std::cin >> celsius;
double fahrenheit = celsius * 9.0 / 5.0 + 32;
std::cout << celsius << "°C = " << fahrenheit << "°F\n";
// 화씨 → 섭씨: C = (F - 32) * 5/9
double back = (fahrenheit - 32.0) * 5.0 / 9.0;
std::cout << "역변환 확인: " << back << "°C\n";
return 0;
}
9.0 / 5.0처럼 실수 리터럴을 써서 정수 나눗셈(9/5 == 1)이 되지 않게 했습니다.
예제 3: 입력 검증 (범위·타입)
#include <iostream>
#include <limits>
int main() {
int age = 0;
std::cout << "나이 (1~150): ";
if (!(std::cin >> age)) {
std::cerr << "숫자를 입력하세요.\n";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return 1;
}
if (age < 1 || age > 150) {
std::cerr << "범위를 벗어났습니다.\n";
return 1;
}
std::cout << "입력한 나이: " << age << std::endl;
return 0;
}
std::cin 실패 시 clear와 ignore로 버퍼를 비우지 않으면 다음 입력이 꼬일 수 있어, 반복 입력 루프를 만들 때 특히 중요합니다.
구체적으로 무슨 일이 일어나는지 보면 이렇습니다. int를 기대하는 자리에 abc를 입력하면 std::cin은 실패 상태(failbit)가 되고, 이후의 모든 >> 연산은 아무것도 읽지 않고 즉시 실패합니다. 입력한 abc는 버퍼에 그대로 남아 있습니다. 이 상태로 while 루프를 돌리면 사용자에게 입력 기회도 주지 않고 에러 메시지가 무한히 출력됩니다. clear()는 실패 상태만 해제하고, ignore()가 남아 있는 abc\n을 버리는 역할을 하므로 둘 다 필요합니다. 비슷한 함정으로 std::cin >> age; 다음에 std::getline(std::cin, name);을 쓰면, 앞에서 남긴 줄바꿈 문자 때문에 getline이 빈 문자열을 읽고 바로 넘어갑니다. 이때도 getline 전에 ignore로 줄바꿈을 버려야 합니다.
예제 4: BMI 계산기
#include <iostream>
#include <iomanip> // setprecision
int main() {
double height = 0, weight = 0;
std::cout << "키(cm): ";
std::cin >> height;
std::cout << "몸무게(kg): ";
std::cin >> weight;
double heightM = height / 100.0;
double bmi = weight / (heightM * heightM);
std::cout << std::fixed << std::setprecision(1);
std::cout << "BMI: " << bmi << std::endl;
if (bmi < 18.5) std::cout << "저체중" << std::endl;
else if (bmi < 23) std::cout << "정상" << std::endl;
else if (bmi < 25) std::cout << "과체중" << std::endl;
else std::cout << "비만" << std::endl;
return 0;
}
키를 미터로 바꿀 때 100.0으로 나누어 실수 나눗셈이 되도록 했습니다.
초기화 누락·정수 나눗셈·오버플로: 컴파일은 되는데 틀리는 코드
초기화하지 않은 변수 사용
지역 변수는 자동으로 0이 되지 않습니다. 읽기 전에 반드시 값을 넣으세요.
// ❌ 위험
int x;
std::cout << x;
// ✅ 안전
int x = 0;
“쓰레기 값이 나온다”는 설명은 절반만 맞습니다. 초기화하지 않은 지역 변수를 읽는 것은 정의되지 않은 동작이라, 디버그 빌드에서는 0이나 특정 패턴(MSVC 디버그 빌드는 스택을 0xCCCCCCCC로 채워 -858993460이 출력되곤 합니다)이 나오다가 릴리스 빌드에서는 전혀 다른 값이 나올 수 있습니다. “내 컴퓨터에서는 되는데 제출하면 틀린다”는 코딩 테스트 질문의 상당수가 이 경우입니다. 전역 변수와 static 변수는 0으로 초기화된다는 점이 지역 변수와 다르다는 것도 혼동의 원인입니다. GCC/Clang의 -Wall은 'x' is used uninitialized 경고를, MSVC는 C4700 경고를 내 주므로 경고를 켜고 빌드하는 습관이 가장 확실한 방어입니다.
정수 나눗셈 (5 / 2 == 2)
int끼리 나누면 몫만 나옵니다. 평균·비율에는 double이나 static_cast를 사용하세요.
int a = 5, b = 2;
double wrong = a / b; // 2.0
double right = static_cast<double>(a) / b; // 2.5
오버플로우
int 최댓값을 넘기는 부호 있는 정수 오버플로는 정의되지 않은 동작(UB)입니다. 아래처럼 음수가 출력되는 경우가 많지만 보장된 결과가 아니고, 최적화가 켜지면 컴파일러가 “오버플로는 일어나지 않는다”고 가정해 조건문을 통째로 지우는 등 예상 밖의 코드가 만들어질 수 있습니다. 반면 unsigned 타입의 범위 초과는 표준이 모듈러 연산(0으로 되돌아감)으로 정의합니다.
int max = 2147483647;
std::cout << max + 1 << "\n"; // UB: 흔히 -2147483648이 나오지만 보장되지 않음
long long big = 2147483647LL;
std::cout << big + 1 << "\n"; // 2147483648
큰 합·곱이 예상되면 처음부터 long long을 쓰거나 범위를 검사하세요.
타입 불일치 (특히 unsigned와 혼합)
unsigned와 int를 비교하면 암시적 변환으로 논리 오류가 날 수 있습니다. 혼합 연산 전에 타입을 통일하거나 static_cast로 의도를 드러내세요.
char와 string 혼동
// ❌ 컴파일 에러
// char name = "Hong";
// ✅
char initial = 'H';
std::string name = "Hong";
FAQ
Q1: int와 long의 차이는 무엇인가요?
A: 크기가 다릅니다.
| 타입 | 크기 | 범위 |
|---|---|---|
| int | 4바이트 | -2,147,483,648 ~ 2,147,483,647 |
| long | 4바이트 (Windows) / 8바이트 (Linux) | 플랫폼 의존적 |
| long long | 8바이트 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
권장: 큰 수가 필요하면 long long 사용 (플랫폼 독립적)
Q2: float와 double 중 무엇을 써야 하나요?
A: 대부분 double을 사용하세요.
이유:
float: 정밀도 낮음 (소수점 6-7자리)double: 정밀도 높음 (소수점 15-16자리)- 성능 차이: 스칼라 연산은 거의 없음. 다만 SIMD 벡터화나 GPU에서는
float가 한 번에 두 배 많은 값을 처리하고 메모리 대역폭도 절반만 쓰므로 차이가 커질 수 있음 float 사용 시기: - 메모리가 매우 제한적인 경우
- GPU 프로그래밍
- 대량의 실수 배열
Q3: string은 왜 std::를 붙여야 하나요?
A: string은 C++ 표준 라이브러리(STL)의 클래스이기 때문입니다.
#include <string>
// 방법 1: std:: 붙이기
std::string name = "Hong";
// 방법 2: using 선언
using std::string;
string name = "Hong";
// 방법 3: using namespace (비추천)
using namespace std;
string name = "Hong";
Q4: 변수 이름 규칙은 무엇인가요?
A: 다음 규칙을 따르세요. 가능:
- 영문자, 숫자, 언더스코어(_)
- 영문자나 언더스코어로 시작
- 대소문자 구분
// ✅ 올바른 이름
int age;
int user_count;
int maxScore;
int _temp;
// ❌ 잘못된 이름
int 2age; // 숫자로 시작 불가
int user-count; // 하이픈 불가
int max score; // 공백 불가
네이밍 컨벤션 (권장):
// camelCase (일반 변수)
int userAge = 25;
int maxScore = 100;
// snake_case (일부 프로젝트)
int user_age = 25;
int max_score = 100;
// UPPER_CASE (상수)
const int MAX_USERS = 1000;
const double PI = 3.14159;
Q5: 왜 초기화를 해야 하나요?
A: 초기화하지 않으면 “쓰레기 값”이 들어갑니다.
#include <iostream>
int main() {
int x; // 초기화 안 함
std::cout << x; // 쓰레기 값 출력 (예: 32767, -1, 0 등 랜덤)
if (x > 100) { // 예상 못한 동작
}
return 0;
}
해결법: 항상 초기화하세요.
int x = 0; // 안전
Q6: 오버플로우는 무엇인가요?
A: 자료형의 범위를 넘어서는 경우입니다.
#include <iostream>
int main() {
int max = 2147483647; // int 최댓값
std::cout << max << std::endl; // 2147483647
std::cout << max + 1 << std::endl; // UB (흔히 -2147483648, 보장 안 됨)
long long big = 2147483647LL;
std::cout << big + 1 << std::endl; // 2147483648 (정상)
return 0;
}
주의: 코딩테스트에서 자주 나오는 함정입니다.
같이 보면 좋은 글
- C++ std::string 자주 쓰는 함수 정리
- C++ 클래스와 객체 입문
- C++ 포인터 처음 배우기: &와 * 연산자, swap·배열 순회, 초보자가 하는 첫 실수
- C++ 첫 프로그램: Hello World와 컴파일 과정
- C++ 시리즈 전체 보기