icon
2장 : 변수와 데이터 타입

상수와 리터럴

이번 장에서는 이렇게 변하지 않는 값들을 좀 더 심도 있게 다룰 것입니다. 바로 상수(Constants)리터럴(Literals) 입니다.

상수와 리터럴은 프로그램 내에서 고정된 값을 표현하는 방식입니다.

이들을 정확히 이해하는 것은 코드의 가독성과 안정성을 높이는 데 매우 중요합니다.


상수(Constants)의 이해

우리는 이미 const 키워드를 사용하여 상수를 선언하는 방법을 간략하게 살펴보았습니다.

상수는 이름이 있는 메모리 공간에 저장된 값이지만 변수와 달리 프로그램 실행 중 그 값을 변경할 수 없습니다.

왜 상수를 사용하는가?

상수를 사용하는 주된 이유는 다음과 같습니다.

  1. 의미 부여 및 가독성 향상: 3.141592라는 숫자만 보면 그것이 원주율인지, 다른 어떤 값인지 알기 어렵습니다. 하지만 const double PI = 3.141592;라고 선언하면 PI라는 이름 자체가 그 숫자의 의미를 명확하게 전달하여 코드의 가독성을 높입니다.
  2. 오류 방지: 실수로 값을 변경하려는 시도를 컴파일 시점에서 방지할 수 있습니다. 예를 들어, 원주율 PI의 값을 실수로 변경하려 한다면 컴파일러가 오류를 발생시켜 알려줄 것입니다. 이는 프로그램의 안정성을 높이는 데 기여합니다.
  3. 유지보수 용이성: 만약 프로그램의 여러 곳에서 동일한 고정 값을 사용하고 있다면, 이 값을 상수로 선언해두고 사용하는 것이 좋습니다. 나중에 이 값을 변경해야 할 때, 상수의 정의만 한 번 변경하면 모든 사용처에 자동으로 반영되므로 유지보수가 매우 편리해집니다.

상수를 선언하는 방법

  • 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)의 이해

리터럴은 소스 코드에 직접적으로 표현되는 고정된 값 자체를 의미합니다.

즉, 변수에 저장되지 않고 코드 내에 '문자 그대로' 쓰여진 값입니다.

리터럴은 특정 데이터 타입을 가집니다.

리터럴의 종류

  1. 정수 리터럴 (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)
  2. 부동 소수점 리터럴 (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
  3. 문자 리터럴 (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 = '\\'; // 백슬래시 문자
  4. 문자열 리터럴 (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-스타일 문자열보다 훨씬 유연하고 강력한 기능을 제공합니다. 이에 대해서는 나중에 별도의 장에서 자세히 다룰 것입니다.

  5. 논리 리터럴 (Boolean Literals) 논리 값을 직접 표현하는 리터럴은 두 가지뿐입니다.

    • true: 참
    • false: 거짓
    논리 리터럴 예시
    bool isComplete = true;
    bool isValid = false;
  6. 포인터 리터럴 (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;
}