상수와 리터럴
이번 장에서는 이렇게 변하지 않는 값들을 좀 더 심도 있게 다룰 것입니다. 바로 상수(Constants) 와 리터럴(Literals) 입니다.
상수와 리터럴은 프로그램 내에서 고정된 값을 표현하는 방식입니다.
이들을 정확히 이해하는 것은 코드의 가독성과 안정성을 높이는 데 매우 중요합니다.
상수(Constants)의 이해
우리는 이미 const
키워드를 사용하여 상수를 선언하는 방법을 간략하게 살펴보았습니다.
상수는 이름이 있는 메모리 공간에 저장된 값이지만 변수와 달리 프로그램 실행 중 그 값을 변경할 수 없습니다.
왜 상수를 사용하는가?
상수를 사용하는 주된 이유는 다음과 같습니다.
- 의미 부여 및 가독성 향상:
3.141592
라는 숫자만 보면 그것이 원주율인지, 다른 어떤 값인지 알기 어렵습니다. 하지만const double PI = 3.141592;
라고 선언하면PI
라는 이름 자체가 그 숫자의 의미를 명확하게 전달하여 코드의 가독성을 높입니다. - 오류 방지: 실수로 값을 변경하려는 시도를 컴파일 시점에서 방지할 수 있습니다. 예를 들어, 원주율
PI
의 값을 실수로 변경하려 한다면 컴파일러가 오류를 발생시켜 알려줄 것입니다. 이는 프로그램의 안정성을 높이는 데 기여합니다. - 유지보수 용이성: 만약 프로그램의 여러 곳에서 동일한 고정 값을 사용하고 있다면, 이 값을 상수로 선언해두고 사용하는 것이 좋습니다. 나중에 이 값을 변경해야 할 때, 상수의 정의만 한 번 변경하면 모든 사용처에 자동으로 반영되므로 유지보수가 매우 편리해집니다.
상수를 선언하는 방법
-
const
키워드: 가장 일반적이고 권장되는 방법입니다. 변수 선언 시 타입 앞에const
를 붙여 상수로 만듭니다. 상수는 반드시 선언과 동시에 초기화되어야 합니다.const 상수 선언 예시 const double PI = 3.1415926535; // 원주율 const int MAX_USERS = 100; // 최대 사용자 수 const char NEWLINE = '\n'; // 개행 문자
const
를 사용하면 컴파일러가 해당 값이 변경되지 않음을 알기 때문에 최적화를 수행하는 데도 도움이 될 수 있습니다. -
#define
전처리기 지시자 (레거시 방식):#define
은 C++보다는 C 언어에서 상수를 정의할 때 많이 사용되던 방식입니다. 이는 컴파일 이전에 단순한 텍스트 치환(macro substitution)을 수행합니다.#define 매크로 예시 #define PI_MACRO 3.1415926535 // 세미콜론을 붙이지 않습니다! #define MAX_BUFFER_SIZE 1024
const
와#define
의 차이점- 타입 안전성:
const
는 타입 정보를 가지므로 컴파일러가 타입 체크를 수행하여 오류를 방지할 수 있습니다.#define
은 단순 텍스트 치환이라 타입 체크가 이루어지지 않습니다. - 디버깅:
const
상수는 디버거에서 심볼 정보로 확인 가능하지만,#define
매크로는 컴파일 전에 사라지므로 디버깅이 어렵습니다. - 스코프:
const
상수는 변수와 동일하게 스코프(유효 범위)를 가질 수 있어 특정 블록이나 함수 내에서만 유효하게 만들 수 있습니다.#define
매크로는 정의된 이후부터 파일의 끝까지 전역적으로 유효합니다. - 메모리 사용:
const
상수는 실제 메모리 공간을 차지할 수 있지만, 컴파일러 최적화에 따라 레지스터에 저장되거나 직접 값이 삽입되어 메모리를 차지하지 않을 수도 있습니다.#define
은 텍스트 치환이므로 메모리를 차지하지 않습니다. 이러한 이유로 현대 C++에서는#define
을 사용하여 상수를 정의하는 대신const
키워드를 사용하는 것을 강력히 권장합니다.
- 타입 안전성:
-
enum
(열거형):enum
은 주로 관련된 정수 값들을 의미 있는 이름으로 묶을 때 사용됩니다. 상수의 집합을 정의하는 데 유용합니다.enum 예시 enum Color { RED, GREEN, BLUE }; // RED는 0, GREEN은 1, BLUE는 2의 값을 가짐 (기본적으로 0부터 시작) enum State { IDLE = 0, RUNNING = 1, PAUSED = 2, STOPPED = 3 }; // 특정 값 지정 가능 // 사용 예시 Color myColor = RED; if (myColor == RED) { // ... }
enum
에 대해서는 나중에 더 자세히 다룰 기회가 있을 것입니다.
리터럴(Literals)의 이해
리터럴은 소스 코드에 직접적으로 표현되는 고정된 값 자체를 의미합니다.
즉, 변수에 저장되지 않고 코드 내에 '문자 그대로' 쓰여진 값입니다.
리터럴은 특정 데이터 타입을 가집니다.
리터럴의 종류
-
정수 리터럴 (Integer Literals)
- 십진수: 우리가 가장 흔히 사용하는 형태입니다. (예:
10
,123
,-5
) - 이진수 (Binary): C++14부터
0b
또는0B
접두사를 사용하여 이진수를 표현할 수 있습니다. (예:0b1010
은 십진수 10) - 팔진수 (Octal):
0
접두사를 사용하여 팔진수를 표현합니다. (예:012
은 십진수 10) - 십육진수 (Hexadecimal):
0x
또는0X
접두사를 사용하여 십육진수를 표현합니다. (예:0xA
은 십진수 10)
타입 접미사: 정수 리터럴 뒤에 특정 접미사를 붙여 타입을 명시할 수 있습니다.
U
또는u
:unsigned
(부호 없는) 정수. (예:100U
)L
또는l
:long
정수. (예:100L
)LL
또는ll
:long long
정수. (예:100LL
)UL
,ull
등 조합도 가능합니다.
정수 리터럴 예시 int dec = 10; int bin = 0b1010; // C++14 int oct = 012; int hex = 0xA; long long veryBig = 123456789012345LL; unsigned int flags = 0xFFU; // 십육진수 255 (unsigned int)
- 십진수: 우리가 가장 흔히 사용하는 형태입니다. (예:
-
부동 소수점 리터럴 (Floating-Point Literals): 소수점이나 지수 표기(
e
또는E
)를 포함한 숫자입니다.기본적으로
double
타입으로 간주됩니다.타입 접미사
F
또는f
:float
타입. (예:3.14F
)L
또는l
:long double
타입. (예:3.14L
)
부동 소수점 리터럴 예시 double dval = 3.14; float fval = 3.14F; long double ldval = 3.14L; double sci_notation = 6.02e23; // 6.02 * 10^23
-
문자 리터럴 (Character Literals) 작은따옴표(
''
)로 묶인 단일 문자입니다. 기본적으로char
타입입니다.접미사/접두사
L
:wchar_t
(와이드 문자). (예:L'가'
)u
:char16_t
(UTF-16 문자). (예:u'글'
)U
:char32_t
(UTF-32 문자). (예:U'자'
)
이스케이프 시퀀스 (Escape Sequences): 특정 특수 문자는 백슬래시(
\
)와 함께 조합하여 표현합니다.\n
: 줄 바꿈 (개행)\t
: 탭\\
: 백슬래시 자체\'
: 작은따옴표\"
: 큰따옴표\0
: 널 문자 (문자열의 끝을 나타내는 데 사용)
문자 리터럴 예시 char ch = 'A'; char newline = '\n'; // 줄 바꿈 문자 char backslash = '\\'; // 백슬래시 문자
-
문자열 리터럴 (String Literals) 큰따옴표(
""
)로 묶인 일련의 문자 시퀀스입니다.C-스타일 문자열(character array)로 취급되며, 항상 마지막에 널 문자(
\0
)가 자동으로 추가됩니다.접두사
L
:wchar_t
문자열. (예:L"안녕하세요"
)u
:char16_t
문자열. (예:u"Hi"
)U
:char32_t
문자열. (예:U"Hello"
)R
: 원시 문자열 (Raw String Literal). 이스케이프 시퀀스를 해석하지 않고 문자 그대로를 사용합니다. 파일 경로 등을 표현할 때 유용합니다. (예:R"(C:\new\folder)"
는C:\new\folder
그대로 해석)
문자열 리터럴 예시 const char* message = "Hello, C++!"; // C-스타일 문자열 std::string name = "나 혼자 C++"; // C++ 표준 라이브러리의 string 타입 // 원시 문자열 리터럴 const char* path = R"(C:\Users\Admin\Documents\my_file.txt)"; std::cout << path << std::endl; // 출력: C:\Users\Admin\Documents\my_file.txt
std::string
은 C++ 표준 라이브러리에서 제공하는 문자열 클래스로, C-스타일 문자열보다 훨씬 유연하고 강력한 기능을 제공합니다. 이에 대해서는 나중에 별도의 장에서 자세히 다룰 것입니다. -
논리 리터럴 (Boolean Literals) 논리 값을 직접 표현하는 리터럴은 두 가지뿐입니다.
true
: 참false
: 거짓
논리 리터럴 예시 bool isComplete = true; bool isValid = false;
-
포인터 리터럴 (Pointer Literal)
nullptr
: C++11부터 도입된 포인터 리터럴입니다. 아무것도 가리키지 않는 널 포인터를 명시적으로 나타냅니다. C에서는NULL
이나0
을 사용하기도 했지만,nullptr
은 타입 안전성을 제공하여 더 바람직합니다.
포인터 리터럴 예시 int* ptr = nullptr; // 아무것도 가리키지 않는 정수형 포인터
리터럴 상수 (Literal Constants)
리터럴은 그 자체로 상수입니다.
10
, 3.14
, 'A'
, "Hello"
와 같은 값들은 프로그램 내에서 고정된 값을 가지며 변경할 수 없습니다.
이들은 이름이 없는 상수라고 볼 수 있습니다.
우리가 변수를 선언하고 const
키워드로 상수를 만드는 것은 이렇게 이름 없는 리터럴에 의미 있는 이름을 부여하여 코드의 가독성과 유지보수성을 높이기 위함입니다.
#include <iostream>
#include <string> // std::string을 사용하기 위해 포함
int main() {
// 상수 선언 (이름 있는 상수)
const int PROGRAM_VERSION = 2;
const double DISCOUNT_RATE = 0.15; // 15% 할인
const std::string WELCOME_MESSAGE = "환영합니다!";
// 리터럴 직접 사용 (이름 없는 상수)
int score = 95; // 95는 정수 리터럴
double tax = score * 0.05; // 0.05는 부동 소수점 리터럴
std::cout << WELCOME_MESSAGE << std::endl;
std::cout << "프로그램 버전: " << PROGRAM_VERSION << std::endl;
std::cout << "할인율: " << DISCOUNT_RATE * 100 << "%" << std::endl;
std::cout << "원래 점수: " << score << std::endl;
std::cout << "세금: " << tax << std::endl;
// 문자 리터럴과 이스케이프 시퀀스
char tabChar = '\t';
std::cout << "이것은" << tabChar << "탭 문자입니다." << std::endl;
std::cout << "큰따옴표(\")와 백슬래시(\\)를 사용할 수 있습니다." << std::endl;
return 0;
}