변수란 무엇인가
변수는 값을 담는, 이름이 붙은 메모리 조각입니다. C++에서는 모든 변수가 고정된 타입을 가지며 - 선언할 때 정해집니다 - 그 타입은 절대 바뀌지 않습니다. 이것이 바로 C++을 정적 타입으로 만드는 점입니다. 컴파일러는 프로그램이 실행되기 전에 모든 변수의 타입을 알고 있으며, 잘못된 종류의 값을 저장하는 코드는 컴파일하기를 거부합니다.
선언은 세 부분으로 이루어집니다: 타입, 이름, 그리고 (거의 항상) 초깃값입니다.
int age = 30;은 "age라는 이름의 int를 만들고 그 안에 30을 넣어라"로 읽으세요. 세미콜론은 문장을 끝맺으며, 이는 주석 페이지가 모든 문장에 대해 보여 준 것과 같습니다. isActive가 1로 출력된 점에 주목하세요. bool은 기본적으로 1/0으로 표시되며, 이는 데이터 타입 페이지가 토대로 삼는 내용입니다.
초기화 vs. 대입
이 둘은 비슷해 보이지만 서로 다른 연산이며, 그 차이는 이 페이지에서 가장 중요한 개념 중 하나입니다.
초기화는 선언의 일부로 변수에 첫 값을 줍니다. 대입은 이미 존재하는 변수의 값을 바꿉니다.
초기화 없이 선언한 뒤 나중에 대입할 수도 있지만, 조심하세요:
이것이 동작하는 이유는 score를 읽기 전에 대입했기 때문입니다. 위험한 버전은 먼저 읽는 것이며, 바로 다음에서 다룹니다.
초기화되지 않은 변수의 함정
이것은 다른 많은 언어에는 없는, C++의 전형적인 함정입니다. 선언되었지만 한 번도 값을 받은 적 없는 지역 변수를 읽는 것은 정의되지 않은 동작입니다. 변수는 그 메모리에 우연히 남아 있던 바이트를 담고 있습니다.
int score; // 초기화되지 않음
std::cout << score; // 정의되지 않은 동작 - 쓰레기 값을 출력하거나, "동작"하거나, 충돌함
컴파일러는 이것을 군말 없이 빌드합니다. 0을 출력할 수도, 32766을 출력할 수도, 실행할 때마다 또는 머신마다 다르게 동작할 수도 있는데 - 이 때문에 이런 버그는 추적하기가 끔찍합니다. 두 가지 방어책이 있습니다.
- 항상 선언에서 초기화하세요.
int score = 0;은 비용이 들지 않으면서 문제 전체를 없애 줍니다. - 경고를 켜세요.
-Wall -Wextra로 컴파일하면 컴파일러가 많은 초기화되지 않은 읽기를, 당신을 물기 전에 표시해 줍니다.
모든 지역 변수에 대해 첫 번째 선택지를 택하세요. 변수를 만드는 바로 그 순간에 합리적인 시작 값을 주세요.
초기화 스타일: =, (), {}
C++은 초깃값을 쓰는 몇 가지 방법을 제공합니다. 대부분 같은 일을 하지만, 중괄호 초기화에는 알아 둘 만한 안전 기능이 하나 더 있습니다.
{}를 쓰는 이유는 축소 변환(narrowing)을 거부하기 때문입니다. 이는 조용히 데이터를 잃게 만드는 대입을 말합니다. =로는 소수 값이 소리 없이 잘려 나가지만, {}로는 컴파일러가 당신을 멈춰 세웁니다.
int x = 3.9; // 컴파일됨 - x는 조용히 3이 됨 (.9는 버려짐)
int y{3.9}; // 컴파일 오류 - double에서 int로의 축소는 허용되지 않음
이를 컴파일 타임에 잡아내는 것이 바로 당신이 원하는 바입니다. 흔한 현대적 습관은 이 추가 검사를 위해 기본적으로 {}를 쓰고, 복사 초기화가 더 자연스럽게 읽힐 때만 =로 돌아가는 것입니다.
이름 짓기 규칙과 관례
C++은 몇 가지 엄격한 규칙을 강제하고, 그 위에 모두가 관례를 얹습니다. 규칙은 이렇습니다. 이름은 글자, 숫자, _를 포함할 수 있습니다. 숫자로 시작할 수 없습니다. 예약어(int나 return 같은)일 수 없습니다. 그리고 대소문자를 구분합니다(age와 Age는 서로 다른 두 변수입니다). 밑줄 뒤에 대문자가 오는 이름이나 밑줄이 연속으로 두 개 들어간 이름은 피하세요. 그것들은 구현(implementation)을 위해 예약되어 있습니다.
대부분의 C++ 코드가 따르는 관례는 다음과 같습니다.
- 변수는
snake_case나camelCase를 사용합니다 - 하나를 골라 일관되게 유지하세요:item_count또는itemCount. - 이름은 값을 설명해야 합니다:
c가 아니라count,x가 아니라user_email.
명확한 이름은 장식이 아닙니다. 그것은 미래의 당신이 코드를 읽는 방식입니다. total_price = item_count * price_per_item은 t = c * p가 결코 해낼 수 없는 방식으로 그 자체로 설명이 됩니다.
변수 스코프
변수는 그것이 선언된 블록 - { ... } - 안에서만 존재하며, 닫는 중괄호에서 파괴됩니다. 이것이 그 변수의 **스코프(scope)**입니다. 반복문이나 if 블록 안에서 선언된 변수는 그 바깥에서는 보이지 않습니다.
i와 square 둘 다 반복문에 속하며 반복문이 끝나면 사라집니다. total은 바깥 블록에서 선언되었으므로 살아남습니다. 안쪽 블록은 바깥 블록의 이름을 가릴(shadow) 수도 있습니다. 같은 이름의 새 변수가 안쪽 블록이 닫힐 때까지 바깥쪽 변수를 일시적으로 숨기는데, 이는 흔한 혼란의 원인이므로 중첩된 스코프에 걸쳐 이름을 재사용하는 것은 피하세요.
실용적인 교훈은 이렇습니다. 각 변수를 그것이 필요한 가장 작은 블록에서 선언하고, 그곳에서 초기화하세요. 좁은 스코프는 당신의 주의를 두고 다투는 이름이 더 적고, 값이 설정된 곳에서 멀리 떨어진 데서 그것을 읽을 가능성이 더 적다는 뜻입니다.
다음: 데이터 타입
이 페이지의 모든 변수는 타입으로 시작했습니다 - int, double, string, bool. 다음 페이지에서는 C++의 데이터 타입을 자세히 분석합니다: 기본 타입과 그 크기, 부호 있는 정수와 부호 없는 정수, float vs. double, char, 그리고 작업에 맞는 올바른 타입을 고르는 방법.
자주 묻는 질문
C++에서 변수는 어떻게 선언하나요?
타입을 쓰고, 그다음 이름을 쓰고, 선택적으로 값을 줍니다: int age = 30;. 타입(int)은 변수의 일생 동안 고정되며, 이름(age)은 그 변수를 가리키는 방법입니다. 값 없이 선언할 수도 있지만 - int age; - 지역 변수의 경우 값을 대입하기 전까지 쓰레기 값을 담고 있게 되므로, 항상 선언하는 지점에서 초기화하세요.
C++에서 초기화와 대입의 차이는 무엇인가요?
초기화는 선언의 일부로 변수에 첫 값을 줍니다: int x = 5; 또는 int x{5};. 대입은 이미 존재하는 변수의 값을 바꿉니다: x = 7;. 이 구분이 중요한 이유는, 선언되었지만 한 번도 초기화되지 않은 지역 변수를 읽는 것은 정의되지 않은 동작이기 때문입니다.
C++에서 초기화되지 않은 변수를 사용하면 어떻게 되나요?
초기화되지 않은 지역 변수의 값을 읽는 것은 **정의되지 않은 동작(undefined behavior)**입니다. 변수는 그 메모리에 우연히 남아 있던 바이트를 담고 있으므로, 프로그램이 무작위 숫자를 출력하거나, 운 좋게 동작하거나, 충돌할 수 있습니다. 컴파일러는 막아 주지 않으므로(다만 다수가 -Wall로 경고합니다), 해결책은 지역 변수를 선언할 때 항상 값을 주는 것입니다.