문자열(string)
char buffeer[256];
cin.getline(buffer, 256);
// 더 나은 방법
#define BUFFER_SIZE 256;
char buffeer[BUFFER_SIZE];
cin.getline(buffer, BUFFER_SIZE);
// 또 다른 방법
const int BUFFER_SIZE = 256;
char buffeer[BUFFER_SIZE];
cin.getline(buffer, BUFFER_SIZE);
- 위 코드의 문제점
- 아무것도 읽지 못했을 때
- 한 줄의 문자가 256자 이상일 때(버퍼의 크기가 충분히 크지 않을 때)
std::string 클래스
- std::string 클래스를 이용한 문자열은 길이가 증가할 수 있다.
#include <string>
std::string name;
std::cin >> name;
- 대입(Assignment)과 덧붙이기(Appending).
- 문자열 합치기(Concatenation)
- 문자열 비교(Relational) 연산자
size(), length(), c_str()
- size(), length(): 문자열 길이를 반환하는 함수.
→ length()는 잘 사용하지 않는다. (컨테이너를 배우면서 그 이유가 밝혀진다.) - c_str()
→const char *
를 반환해줌.
→ 해당 string이 가지고 있는 문자 배열의 시작 주소를 가리키는 포인터를 반환한다.
std::string line;
cin >> line;
const char * cLine = line.c_str();
→ C로 작성된 코드와의 호환성 및 시스템 콜 함수 사용등 문자열 자체를 요구하는 경우가 있으므로, string 클래스 자체가 아닌 C스타일의 캐릭터 배열을 가져올 수 있어야 한다.
string의 한 문자 접근
std::string name = "HELLO"
char letter = name[1];
name[2] = 'H'; // []또한 함수이다.
name[2] = ‘H’
함수를 호출한 것이므로, 반환값이 존재할 것이다. 반환 값은 임시 값인데, 어떻게 값에 직접 접근하여 변경을 해주느냐? → 참조로 반환했기 때문에 대입이 이루어질 수 있는 것이다.
#include <iostream>
#include <string>
int main()
{
std::string name = "12345";
char letter = name[3];
std::cout << "name is "<< name << std::endl;
std::cout << "letter is "<< letter << std::endl;
name[3] = '5';
std::cout << "letter is "<< letter << std::endl;
std::cout << "name is "<< name << std::endl;
return (0);
}
output
at()
- 해당 문자열에서 주어진 위치의 문자를 참조로 반환.
char letter = name.at(1);
name.at(1) = '0';
→ 배열로 접근하여 사용하면 되기 때문에 잘 쓰이진 않는다.
- 한 줄 읽기
string mailHeader;
getline(cin, mailHeader); // \\n을 만날 때까지 cin에서 문자를 꺼내어 mailHeader에 저장한다.
getline(cin, mailHeader, '@'); // '@' 문자를 만날 때까지 cin에서 문자를 꺼내어 저장한다.
- getline()함수는 eof를 만나거나, 구분 문자(delimiter)를 만날 때까지 string에 저장해준다.<sstream>
std::istringstream
- cin과 비슷: 키보드 대신 string으로부터 읽어옴
- sscanf()와 비슷하다.
std::ostringtream
- cout과 비슷: 콘솔 대신 string에 출력한다.
- sprintf()와 비슷하다.
- cout, cin도 스트림이다.
C 헤더를 사용
→ 성능 상의 이유로 많은 C 함수들을 사용한다.
- string.h → cstring
- stdio.h → cstdio
- cypte.h → cctype
string 클래스가 좋은 이유
- 문자 배열 길이에 관해 고민할 필요가 없다.
string이 어떻게 작동하는가?
- 1단계
std::string line;
string 클래스가 처음 만들어질 때 character 들을 클래스 내부에 저장한다.
→ 처음에 16바이트(널문자 포함)를 잡아주어 15개의 문자를 담을 수 있도록 한다.(플랫폼 마다, 컴파일러 마다 다름)
→ 포인터는 heap 메모리에 저장되고, size와 capacity는 스택에 저장된다. (포인터는 동적으로 늘어날 수 있어야 하기 때문에)
- 2단계
→ heap 영역에 메모리 복사가 일어나고 size는 문자열의 길이만큼 초기화된다. 용량은 그대로 15.
- 3단계
→ line 뒤에 문자열을 이어붙이려고 한다.
- 4단계
→ 메모리가 추가로 필요하므로 32비트를 새로 잡아준다.
- 5단계
→ 기존에 존재하던 문자열 “POPE”를 복사하여 가져온다.
- 6단계
→ 기존 메모리를 free 해주고, 새로운 문자열로 주소를 연결시켜준다.
- 7단계
“POPE” 뒤부터 메모리를 복사해준다.
정리
- heap 메모리 할당은 느리다.
- 메모리 단편화(memory fragmentation, 메모리 파편화)문제
- RAM에서 메모리의 공간이 작은 공간으로 나누어져 있어서 사용가능한 메모리가 충분히 존재하지만, 연속적이지 않아 할당 불가능한 상태를 들어 메모리 단편화 문제가 일어났다고 한다.
- 내부 버퍼의 증가는 멀티 쓰레드 환경에서 안전하지 않을 수 있다.
- 락을 제대로 걸어주어야 한다.
- 한 스레드에서 문자열을 이어 붙이다가 이미 할당 받은 공간이 넘어서 새로운 문자열을 만들어 복사를 하고 string 클래스의 문자열 주소를 변경했는데, 다른 스레드는 기존 문자열 주소에 작업을 할 수 있음.
- 성능을 중요시하는 업계에서만 사용한다는 것을 염두에 두어야 한다.
- sprintf()와 char[]를 많이 사용하고 있다.
c_str()은 어떻게 작동하는가?
string line = "POPE"
const char *cLine = line.c_str();
- → c_str()로 반환 받은 배열로 접근해서 변경을 한다면 string 클래스 내부에서 사용하는 멤버 변수들과 충돌이 일어날 수 있다. 그래서 const로 반환된다.
'Language > C++' 카테고리의 다른 글
[C++] 7.Unmanaged Programming: Class (0) | 2024.01.23 |
---|---|
[C++] 6.Unmanaged Programming: 파일 입출력 (1) | 2024.01.23 |
[C++] 4.Unmanaged Programming: 참조(Reference) (0) | 2024.01.21 |
[C++] 3.Unmanaged Programming: 입력 스트림(Input Stream) (1) | 2024.01.21 |
[C++] 2.Unmanaged Programming: 출력형식 지정(output formatting) (0) | 2024.01.21 |