스트링 리터럴
다음 코드는 hello란 스트링을 변수에 담지 않고 스트링 값을 곧바로 화면에 출력한다.
cout << "hello" << endl;
여기 나온 'hello'처럼 변수에 담지 않고 곧바로 값으로 표현한 스트링을 스트링 리터럴이라 부른다.
스트링 리터럴은 내부적으로 메모리의 읽기 전용 영역에 저장된다.
컴파일러는 같은 스트링 리터럴이 코드에 여러 번 나오면
그중 한 스트링에 대한 레퍼런스를 재사용하는 방식으로 메모리를 절약한다.
코드에서 'hello'란 스트링 리터럴을 500번 넘게 작성해도 컴파일러 hello에 대한 메모리 공간을 딱 하나만 할당한다.
이를 리터럴 폴링이라 한다.
스트링 리터럴을 변수에 대입할 수는 있지만,
메모리의 읽기 전용 영역에 있게 되거나 동일한 리터럴을 여러 곳에서 공유할 수 있기 때문에 변수에 저장하면 위험하다.
C++ 표준에서는 스트링 리터럴을 'const char가 n개인 배열' 타입으로 정의하고 있다
const 없이 char* 타입 변수에 스트링 리터럴을 대입하더라도
그 값을 변경하지 않는 한 프로그램에 실행에는 아무런 문제가 없다.
그러나 스트링 리터럴을 수정하는 동작에 대해서는 명확히 정의돼 있지 않기 때문에 프로그램이 갑자기 죽을 수도 있다.
예를 들면 다음과 같이 코드를 작성하면 결과를 예측할 수 없다.
char* ptr = "hello"; // 변수에 스트링 리터럴을 대입한다.
ptr[1] 'a'; // 결과를 예측할 수 없다.
스트링 리터럴을 참조할 때는 const 문자에 대한 포인터를 사용하는 것이 훨씬 안전하다.
다음 코드도 위와 똑같은 버그를 담고 있지만 스트링 리터럴을 const char* 타입 변수에 대입했기 때문에
컴파일러는 읽기 전용 메모리에 쓰기 작업을 실행하는 것을 막을 수 있다.
const char* ptr = "hello"; // 변수에 스트링 리터럴을 대입한다.
ptr[1] = 'a'; // 읽기 전용 메모리에 값을 쓰기 때문에 에러가 발생한다.
문자 배열(char[])의 초깃값을 설정할 때도 스트링 리터럴을 사용한다.
이때 컴파일러는 주어진 스트링을 충분히 담을 정도로 큰 배열을 생성한 뒤 여기에 실제 스트링 값을 복사한다.
컴파일러는 이렇게 만든 스트링 리터럴을 읽기 전용 메모리에 넣지 않으면 재사용하지도 않는다.
char arr[] = "hello"; // 컴파일러는 적절한 크기의 문자 배열 arr을 생성한다.
arr[1] = 'a'; // 이제 스트링을 수정할 수 있다.
1. 로 스트링 리터럴
로 스트링 리터럴이란 여러 줄에 걸쳐 작성한 스트링 리터럴로서,
그 안에 담긴 인용 부호를 이스케이프 시퀀스로 표현할 필요가 없고, \t나 \n 같은 제어 문자를 일반 텍스트로 취급한다.
const char* str = "Hello "World"!"; // 에러가 발생한다.
일반 스트링 리터럴을 위와 같이 작성하면 스트링 안에 있는 큰따옴표를
이스케이프 시퀀스로 표현하지 않았기 때문에 컴파일 에러가 발생한다.
이럴 때는 큰따옴표를 다음과 같이 이스케이프 시퀀스로 표현한다.
const char* str = "Hello \"World\"!";
하지만 로 스트링 리터럴을 사용하면 인용부호를 이스케이프 시퀀스로 표현하지 않아도 된다.
로 스트링 리터럴은 R"(로 시작해서 )"로 끝난다.
const char* str = R"(Hello "World"!)";
로 스트링 리터럴을 사용하지 않고
여러 줄에 걸친 스트링을 표현하려면 스트링 안에서 줄이 바뀌는 지점에 \n을 넣어야 한다.
const char* str = "Line 1\nLine 2";
이 스트링의 결과는 다음과 같다.
로 스트링 리터럴로 표현할 때는 다음과 같이 소스 코드에서 줄 바꿈을 할 지점에
\n 이스케이프 시퀀스를 입력하지 말고 그냥 엔터키를 누르면 된다.
const char* str = R"(Line 1
Line 2)";
그러면 앞에서 \n을 지정했을 때와 똑같이 출력된다.
로 스트링 리터럴에서는 이스케이프 시퀀스를 무시한다.
const char* str = R"(Is the following a tab character? \t)";
따라서 위 스트링을 출력하면 다음과 같이 나온다.
2. 확장 로 스트링 리터럴
로 스트링 리터럴은 )"로 끝나기 때문에 그 안에 )"를 넣을 수 없다.
const char* str = R"(Embedded )" characters)"; // 에러가 발생한다.
)" 문자를 추가하려면 다음과 같이 확장 로 스트링 리터럴 구문으로 표현해야 한다.
R"d-char-sequence(r-char-sequence)d-char-sequence"
여기서 r-char-sequence에 해당하는 부분이 실제 로 스트링이다.
d-char-sequence라고 표현한 부분은 구분자 시퀀스로서, 반드시 로 스트링 리터럴의 시작과 끝에 똑같이 나와야 한다.
이 구분자 시퀀스는 최대 16개의 문자를 가질 수 있다.
이때 구분자 시퀀스는 로 스트링 리터럴 안에 나오지 않는 값으로 지정해야 한다.
앞에 나온 스트링에서 고유한 구분자 시퀀스를 사용하도록 수정하면 다음과 같다.
const char* str = R"-(Embedded )" characters)-";
로 스트링 리터럴을 사용하면 데이터베이스 쿼리 스트링이나 정규표현식, 파일 경로 등을 쉽게 표현할 수 있다.
'💻프로그래밍 내용 정리 > C++17' 카테고리의 다른 글
[C++ 2.1.3-2] 하이레벨 숫자 변환(to_string, stoi), 로우레벨 숫자 변환(to_chars, from_chars) (0) | 2022.08.20 |
---|---|
[C++ 2.1.3-1] C++std::string 클래스, 표준 사용저 정의 리터럴's' (0) | 2022.08.20 |
[C++ 2.1.1] 동적 스트링(C 스타일 스트링), strlen, strcpy, strcat (0) | 2022.08.19 |
[C++ 1.6.4] 사용자 인터페이스 (0) | 2022.08.19 |
[C++ 1.6.3] Database 클래스 (0) | 2022.08.18 |