2 minute read

‘전문가를 위한 C++ - Marc Gregoire 지음, 남기혁 옮김’ 책을 참고하여 작성한 포스트입니다.


동적 스트링

C 스타일 스트링

  • C언어에서 스트링은 널 문자(\0, NUL)로 끝나는 문자배열로 표현됐다.
  • 이는 버퍼 오버플로(buffer overflow)를 비롯한 다양한 문제로 보안 취약점이 드러날 수 있다.
  • 실제 메모리에 저장될 때 문자의 개수에 널 문자를 위한 자리 하나를 더 포함한 공간이 필요하다.
  • strlen()은 문자의 개수를 반환하므로, strcpy()를 하기 위해 배열을 새로 할당할 때 strlen()에 1을 더한 크기의 공간을 할당하여야 한다.
  • sizeof()의 경우는 스트링의 크기를 구할 때 절대로 사용하면 안된다.
    • 리턴 값이 C스트링이 저장된 방식에 따라 다르다.
      1. char[]로 저장한 경우는 널문자를 포함하여 반환하므로, 실제 할당된 메모리 크기이다.
      2. char*로 저장한 경우는 포인터의 크기를 반환한다. 32비트 모드면 4,64비트 모드에서 컴파일하면 8을 반환한다.
  • C 스트링 관련 함수들은 <cstring> 헤더 파일에 정의되어 있다.

스트링 리터럴(string literal)

  • 변수에 담지 않고 곧바로 값으로 표현한 스트링.
  • 메모리의 읽기 전용 영역에 저장된다.
  • 따라서, 같은 스트링 리터럴이 코드에 여러 번 나오면 이에 대한 레퍼런스를 재활용하는 방식으로 메모리를 절약한다. 이를 리터럴 풀링(literal pooling)이라 부른다.
  • 스트링 리터럴을 변수에 대입할 수 있지만, 동일한 리터럴을 여러 곳에서 공유할 수 있기 때문에 변수에 저장하면 위험하다.
  • c++ 표준에서는 스트링 리터럴을 ‘const char가 n개인 배열’ 이라 정의하고 있지만, 다수의 컴파일러는 const char* 타입 변수에 저장하지 않는다.
  • 해당 리터럴을 변경하지 않는다면 상관 없겠지만, 그러지 않다면 예측할 수 없는 문제들이 발생할 수 있다.
  • 그래서 스트링 리터럴을 참조할 때는 const 문자에 대한 포인터를 사용하는 것이 훨씬 안전하다.
    const char* ptr { "Hello" };
    ptr[0] = 'a'; // 에러 발생
    
  • 문자 배열(char[])의 초깃값 설정 시에 스트링 리터럴을 사용한다. 이때 컴파일러는 주어진 스트링을 담을 수 있게 충분히 큰 배열을 생성한 뒤 여기에 실제 스트링 값을 복사한다.
  • 이렇게 만들어진 스트링 리터럴은 읽기 전용 메모리에 넣어지지 않으며, 따라서 재활용되지 않는다.

로 스트링 리터럴(raw string literal)

  • 여러 줄에 걸쳐 작성하는 스트링 리터럴
  • R"( 스트링 )" 의 형식이다.
  • 이스케이프 시퀀스가 무시된다. 스페이스나 엔터 그냥 쓰면 된다.

C++ std::string 클래스

  • std 네임스페이스에 속하며, <string> 헤더에 속해 있다.

    string 클래스 사용법

  • 클래스지만 마치 기본 타입인 것처럼 사용한다.
  • 메서드 중 compare()는 값이 같은 경우에 0을 반환하고, 다른 경우에는 0이 아닌 값을 리턴한다.
  • 그래서 두 스트링 비교는 == 사용하는 것이 좋다.
  • 메모리 관련 작업은 string 클래스가 알아서 처리해 준다.
  • 그리고 string 객체는 스택 변수로 생성되므로, 메모리 누수가 발생하지 않는다.
  • 다음과 같은 연산들이 있다.
    • substr(pos, len)
      • 인수로 지정한 시작 위치와 길이에 맞는 서브스트링 리턴
    • find(str)
      • 인수로 지정한 서브스트링이 있는 지점을 리턴. 없으면 string::npos 반환하고
    • replace(pos, len, str)
      • 스트링에서 인수로 지정한 위치와 길이에 해당하는 부분을 str로 지정한 값으로 교체

숫자 변환

하이레벨 숫자 변환

숫자를 string으로 변환

  • string 객체를 새로 생성해서 리턴하며, 메모리 할당 작업도 처리해준다.
string to_string(T val);

long double d { 3.14L };
string s { to_string(d) };

string을 숫자로 변환

  • 다음 함수 매개변수의 idx는 변환되지 않은 부분의 맨 앞에 있는 문자의 인덱스를 가리키는 포인터,
  • base는 변환할 수의 밑(base, 기수, 기저)이다.
  • idx 포인터를 널 포인터로 지정하면 이 값을 무시한다.
  • 변환함수들은 제일 앞에 나온 공백 문자를 무시하고, 변환에 실패하면 invalid_argument를, 리턴 타입의 범위를 벗어나면 out_of_range 예외를 던진다.
int stoi(const string& str, size_t *idx=0, int base=10);
long long stol(...);
float stof(...);
double stod(...);

const string MyString {"  123A"};
size_t Idx { 0 };
int Value { stoi(MyString, &Idx) };
// Value : 123
// MyString[Idx] : 'A'
  • base 값을 0으로 주면 0x, 0X는 16진수, 0으로 시작하는 숫자는 8진수, 나머지는 10진수로 처리한다.

로우 레벨 숫자 변환 (추가하기)

std::string_view 클래스 (추가하기)

  • 읽기 전용 스트링에 효과적이다.

스트링 포맷 지정

  • c++20

Leave a comment