소년코딩

02.09 - 리터럴 (literal)

C++에는 두 가지 상수(constant)가 있다: 리터럴(literal), 심볼릭(symbolic)

리터럴 상수(Literal constant)

리터럴(literal)은 코드에 직접 삽입된 값이다. 이 값은 변경할 수 없으므로 '상수'다.

bool myBlogIsBoy = true; // 부울 변수 'myBlogIsBoy'은 부울 리터럴 true가 할당된다.
int x = 5;               // 정수 변수 'x'는 정수 리터럴 5가 할당된다.
std::cout << 2 * 3;      // 2와 3은 정수 리터럴

정수와 부울 리터럴은 간단하지만, 부동 소수점 리터럴을 선언하는 방법은 두 가지가 있다.

double pi = 3.14159; // 3.14159 is a double literal
double avogadro = 6.02e23; // avogadro's number is 6.02 x 10^23

두 번째 형식에서 지수 뒤의 숫자가 음수일 수도 있다.

double electron = 1.6e-19; // charge on an electron is 1.6 x 10^-19

숫자 리터럴에서는 유형을 결정하는 접미사가 포함될 수 있다. 이 접미사는 선택사항으로 생략하면 컴파일러는 의도하는 것이 어떤 종류의 상수인지 유추한다.

Data Type Suffix Meaning
int u or U unsigned int
int l or L long
int ul, uL, Ul, UL, lu, lU, Lu, or LU unsigned long
int ll or LL long long
int ull, uLL, Ull, ULL, llu, llU, LLu, or LLU unsigned long long
double f or F float
double l or L long double
예제:
unsigned int nValue = 5u; // unsigned int
long nValue2 = 5L; // long
float fValue = 5.0f; // float
double d = 6.02e23; // double (by default)

C++은 문자와 문자열 리터럴도 제공한다.

char c = 'A'; // 'A' is a char literal
std::cout << "Hello, world!" // "Hello, world!" is a C-style string literal
std::cout << "Hello," " world!" // C++ will concatenate sequential string literals

8진수와 16진수 리터럴 (Octal and hexadecimal literals)

일상생활에서 우리는 보통 10진법decimal)으로 숫자를 센다. 각 숫자는 0, 1, 2, 3, 4, 5, 6, 7, 8, 9가 된다. 10진수는 가능한 10자리 숫자(0~9)가 있으므로 "base10"이라고도 불린다. 일반적으로 C++에서 숫자는 10진수로 간주한다.

int x = 12; // 12는 10진수로 간주된다.

2진법(binary)는 0과 1 두 수만 있으므로 "base2"라고 한다. (ex. 0, 1, 10, 100, 101, 110, 111)

8진법(octal)16진법(hexadecimal)도 있다.

8진법은 "base8"이다. 즉, 사용할 수 있는 순자는 0, 1, 2, 3, 4, 5, 6, 7이다. 10진법과 비교하면 다음과 같다.

10진법 (Decimal) 0 1 2 3 4 5 6 7 8 9 10 11
8진법(Octal) 0 1 2 3 4 5 6 7 10 11 12 13

8진수 리터럴을 사용하려면, 리터럴 앞에 0을 붙이면 된다.

#include <iostream>

int main()
{
    int x = 012; // 0 before the number means this is octal
    std::cout << x;
    return 0;
}

This outputs:
10

왜 12가 아닌 10이 출력되었을까? 왜냐하면, 숫자는 10진수로 출력되어 있고 8진수 12는 10진수 10과 같기 때문이다. 8진법은 어려우므로 사용을 피하는 게 좋다.

16진법(hexadecimal)"base16"이다. 숫자는 다음과 같이 센다: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12 ...

10진법(Decimal) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
16진법(Hexadecimal) 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11

8진수 리터럴을 사용하려면, 리터럴 앞에 0x를 붙이면 된다.

#include <iostream>

int main()
{
    int x = 0xF; // 0x before the number means this is hexadecimal
    std::cout << x;
    return 0;
}

This outputs:
15

16진수가 사용 가능한 숫자는 16개가 있으므로 하나의 16진수 숫자는 4-bit다. 그러므로 16진수를 사용해서 전체 byte를 나타낼 수 있다.

32비트 정수 값 '0011 1010 0111 1111 1001 1000 0010 0110'을 보자. 숫자의 길이 때문에 읽기 쉽지 않다. 16진수에서 동일 값은 '3A7F 9826'이다. 이처럼 16진수는 메모리의 값을 간결하게 표현할 때 유용하다. 그래서 16진수는 메모리 주소 또는 메모리의 값을 표현할 때 자주 사용한다.

C++ 14 이전에는 이진 리터럴(binary literal)을 할당할 수 없었으므로 16진수를 이용해 할당했다.

#include <iostream>

int main()
{
    int bin(0);
    bin = 0x01; // assign binary 0000 0001 to the variable
    bin = 0x02; // assign binary 0000 0010 to the variable
    bin = 0x04; // assign binary 0000 0100 to the variable
    bin = 0x08; // assign binary 0000 1000 to the variable
    bin = 0x10; // assign binary 0001 0000 to the variable
    bin = 0x20; // assign binary 0010 0000 to the variable
    bin = 0x40; // assign binary 0100 0000 to the variable
    bin = 0x80; // assign binary 1000 0000 to the variable
    bin = 0xFF; // assign binary 1111 1111 to the variable
    bin = 0xB3; // assign binary 1011 0011 to the variable
    bin = 0xF770; // assign binary 1111 0111 0111 0000 to the variable

    return 0;
}

C++ 14의 이진 리터럴과 숫자 구분 기호 (C++14 binary literals and digit separators)

C++ 14에서는 0b접두사를 사용해서 이진 리터럴을 할당할 수 있다.

#include <iostream>

int main()
{
    int bin(0);
    bin = 0b1;  // assign binary 0000 0001 to the variable
    bin = 0b11; // assign binary 0000 0011 to the variable
    bin = 0b1010; // assign binary 0000 1010 to the variable
    bin = 0b11110000; // assign binary 1111 0000 to the variable

    return 0;
}

긴 리터럴은 읽기 어려우므로 C++ 14는 숫자 구분 기호로 따옴표(')를 사용할 수 있다.

#include <iostream>

int main()
{
    int bin = 0b1011'0010;  // assign binary 1011 0010 to the variable
    long value = 2'132'673'462; // much easier to read than 2132673462

    return 0;
}

매직 넘버와 안좋은 이유 (Magic numbers, and why they are bad)

int maxStudents = numClassrooms * 30;

위 코드에서 30과 같은 숫자를 매직 넘버(magic number)라고 부른다. 매직 넘버란 컨텍스트(context)가 없는 코드 중간에서 하드 코딩된 리터럴(일반적으로 숫자)을 말한다. 여기서 30은 교실당 최대 학생 수를 의미한다는 것을 유추해야 한다. 30을 설명하기 위한 주석이 없으면 나중에 추론하는 게 매우 어려울 수 있다.

매직 넘버를 사용하는 것은 나쁜 습관인데, 매직 넘버가 무엇에 사용되는지에 대한 컨텍스트(context)가 없을뿐더러 나중에 값을 바꿔야 할 경우 문제가 발생하기 때문이다.

int maxStudents = numClassrooms * 30;
setMax(30);

교실의 크기가 커져서 교실당 학생 수가 35명이 되었다고 가정해보자. 상수 30을 35로 수정해야 한다. 그런데 setMax() 함수의 인자로 들어가는 30도 35로 수정해야 할까? 같은 의미라면 수정해야 하고 그렇지 않다면 내버려 두어야 한다. '전역 검색→바꾸기' 와 같은 에디터의 기능을 수행하면 변경하지 말아야 할 setMax() 함수의 인자를 수정할 수도 있다. 이와 같은 문제를 방지하려면 리터럴 30의 모든 인스턴스에 대한 코드를 살펴봐야 한다. 이는 심각한 시간 낭비다.

매직 넘버를 사용하지 말자.


cpp 번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/28-literals/ 입니다.

  • 김훈석 2021.01.17 08:02

    "8진수 리터럴을 사용하려면, 리터럴 앞에 0x를 붙이면 된다."
    라고 본문 중간에 되어있습니다. 16진수를 쓰시려다 실수하신 것 같습니다.

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기