C++ 변수와 자료형 | int, double, string 완벽 정리 [초보자용]
이 글의 핵심
C++ 변수와 자료형에 대한 실전 가이드입니다. int, double, string 완벽 정리 [초보자용] 등을 예제와 함께 상세히 설명합니다.
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 | 문자 |
| bool | 1바이트 | true/false | 논리값 |
| string | 가변 | - | 문자열 (STL) |
자료형 심화
정수형 크기: 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은 플랫폼별 차이를 기억해 두세요.
부호 있는 타입과 없는 타입 (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로 변환되면 큰 양수)
실수형 정밀도: 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
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 등을 별도로 학습하면 됩니다.
변수 선언과 초기화
선언만 하기 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 에러 — 의도하지 않은 실수→정수 방지
{} 초기화는 벡터 등 컨테이너 초기화와도 문법이 통일되어 읽기 좋습니다.
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을 쓰는 편이 낫습니다.
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: 계산기 프로그램 (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로 버퍼를 비우지 않으면 다음 입력이 꼬일 수 있어, 반복 입력 루프를 만들 때 특히 중요합니다.
예제 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으로 나누어 실수 나눗셈이 되도록 했습니다.
흔한 실수
1. 초기화하지 않은 변수 사용
지역 변수는 자동으로 0이 되지 않습니다. 읽기 전에 반드시 값을 넣으세요.
// ❌ 위험
int x;
std::cout << x;
// ✅ 안전
int x = 0;
2. 정수 나눗셈 (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
3. 오버플로우
int 최댓값을 넘기면 정의된 범위에서 래핑되어 음수가 될 수 있습니다.
int max = 2147483647;
std::cout << max + 1 << "\n"; // -2147483648 (일반적인 2의 보수 환경)
long long big = 2147483647LL;
std::cout << big + 1 << "\n"; // 2147483648
큰 합·곱이 예상되면 처음부터 long long을 쓰거나 범위를 검사하세요.
4. 타입 불일치 (특히 unsigned와 혼합)
unsigned와 int를 비교하면 암시적 변환으로 논리 오류가 날 수 있습니다. 혼합 연산 전에 타입을 통일하거나 static_cast로 의도를 드러내세요.
5. char와 string 혼동
// ❌ 컴파일 에러
// char name = "Hong";
// ✅
char initial = 'H';
std::string name = "Hong";
자주 하는 실수 (요약 예제)
실수 1: 초기화하지 않은 변수 사용
// ❌ 위험한 코드
int x;
std::cout << x; // 쓰레기 값 출력
// ✅ 올바른 코드
int x = 0;
std::cout << x; // 0 출력
실수 2: 정수 나눗셈
// ❌ 예상과 다른 결과
int a = 5, b = 2;
double result = a / b; // 2.0 (정수 나눗셈)
// ✅ 올바른 코드
double result = static_cast<double>(a) / b; // 2.5
// 또는
double result = a / static_cast<double>(b); // 2.5
실수 3: char vs string
// ❌ 컴파일 에러
// char name = "Hong"; // char는 한 글자만
// ✅ 올바른 코드
char initial = 'H';
std::string name = "Hong";
형변환 (Type Casting) — 빠른 참고
암시적 형변환 (자동)
int i = 10;
double d = i; // int → double (안전)
double d2 = 3.14;
int i2 = d2; // double → int (소수점 버림, 주의!)
명시적 형변환 (수동)
// C 스타일 (가능하면 피하기)
double d = (double)5 / 2;
// C++ 스타일 (권장)
double d = static_cast<double>(5) / 2;
추가 자료형
unsigned (부호 없는 정수)
unsigned int age = 25; // 0 ~ 4,294,967,295
unsigned long long big = 18446744073709551615ULL; // 매우 큰 수
사용 시기: 음수가 절대 없는 경우 (나이, 개수 등). 단, unsigned와 signed를 섞지 않도록 주의하세요.
auto (자동 타입 추론) - C++11
auto x = 10; // int로 추론
auto y = 3.14; // double로 추론
auto str = std::string("Hong"); // std::string
장점: 타입이 복잡할 때 편리합니다.
단점: auto만 보고 타입이 안 보일 수 있으니, 팀 규칙에 맞게 사용하세요.
const (상수)
const int MAX_SCORE = 100; // 변경 불가
// MAX_SCORE = 200; // 컴파일 에러!
const double PI = 3.14159;
const std::string COMPANY = "ABC Corp";
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자리)- 성능 차이: 거의 없음 (현대 CPU)
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; // -2147483648 (오버플로우!)
long long big = 2147483647LL;
std::cout << big + 1 << std::endl; // 2147483648 (정상)
return 0;
}
주의: 코딩테스트에서 자주 나오는 함정입니다.
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ string | “문자열 처리” 완벽 가이드 [실전 함수 총정리]
- C++ 클래스와 객체 | “초보자를 위한” 완벽 가이드 [그림으로 이해]
- C++ 포인터 | “어렵다는 포인터” 5분 만에 이해하기 [그림으로 설명]
관련 글
- C++ 시리즈 전체 보기
- C++ Adapter Pattern 완벽 가이드 | 인터페이스 변환과 호환성
- C++ ADL
- C++ Aggregate Initialization