07.06 - C-style strings
포스트 '05-03 - 문자열, string'에서 문자열(string)은 문자들의 연속적인 모음이라고 정의했다. 문자열은 C++에서 텍스트로 작업하는 기본 방법이며 std::string
은 C++에서 문자열로 작업하는 것을 쉽게 만든다.
Modern C++은 'std::string'과 'C-style 문자열' 두 가지를 지원한다. 이 포스트에서는 C-style 문자열을 자세히 살펴본다.
C-style string
C 스타일 문자열은 단순히 null 종료자를 가지는 문자 배열이다. null 종료자(null terminator)는 문자열의 끝을 나타내는 데 사용하는 특수 문자\0
(ascii code 0)이다.
즉, C 스타일 문자열은 null로 끝나는 문자열이다.
C 스타일 문자열을 정의하려면 char 배열을 선언하고 문자열 리터럴로 초기화하면 된다.
char myString[] = "string";
"string"은 6글자밖에 없지만 C++은 자동으로 null 종결자를 문자열 끝에 추가한다. (직접 포함하지 않아도 된다) 따라서 myString
은 실제로 길이가 7인 배열이다.
다음 프로그램에서 문자열 길이를 출력하고 모든 문자의 ASCII 값을 출력하는 증거를 볼 수 있다.
#include <iostream>
int main()
{
char myString[] = "string";
int length = sizeof(myString) / sizeof(myString[0]);
std::cout << myString<< " has " << length << " characters.\n";
for (int index = 0; index < length; ++index)
std::cout << static_cast<int>(myString[index]) << " ";
return 0;
}
// string has 7 characters.
// 115 116 114 105 110 103 0
0은 문자열 끝에 추가된 null 종료자의 ASCII 코드다.
위 같은 방식으로 문자열을 선언할 때 []를 사용하여 컴파일러가 배열의 길이를 계산하도록 하는 것이 좋다. 이렇게 하면 나중에 문자열을 변경할 때 배열 길이를 수동으로 조정할 필요가 없다.
주목해야 할 한 가지 중요한 점은 C 스타일 문자열이 배열과 같은 규칙을 따른다는 것이다. 즉, 문자열을 생성할 때 초기화할 수 있지만 이후 할당 연산자를 사용해서 해당 문자열에 값을 할당할 수 없다.
char myString[] = "string"; // ok
myString = "rope"; // not ok!
C 스타일 문자열은 배열이므로 [] 연산자를 사용해서 문자열의 개별 문자를 변경할 수 있다.
#include <iostream>
int main()
{
char myString[] = "string";
myString[1] = 'p';
std::cout << myString;
return 0;
}
// spring
C 스타일 문자열을 출력할 때 std::cout
은 null 종결자를 만날 때까지 문자를 출력한다. 실수로 myString[6]
의 null 종결자를 덮어쓰면 std:cout
은 메모리 슬롯이 0을 만날 때까지 모든 것을 출력한다.
NOTE: 문자열 길이보다 배열의 길이가 더 크다면 문제가 되지 않는다.
#include <iostream>
int main()
{
char name[20] = "Alex"; // only use 5 characters (4 letters + null terminator)
std::cout << "My name is: " << name << '\n';
return 0;
}
위의 경우 "Alex" 문자열이 출력되고 std::cout
은 null 종결자에서 중지된다. 배열의 나머지 문자는 무시된다.
C-style strings and std::cin
문자열 길이가 얼마나 길지 모르는 경우가 있다. 예를 들어, 사용자에게 이름을 입력하도록 요청할 때 이름의 길이가 얼마가 되는지는 입력할 때까지 모른다.
이 경우, 필요한 배열보다 더 큰 배열을 선언할 수 있다.
#include <iostream>
int main()
{
char name[255]; // declare array large enough to hold 255 characters
std::cout << "Enter your name: ";
std::cin >> name;
std::cout << "You entered: " << name << '\n';
return 0;
}
위 프로그램에서는 255자의 배열을 할당하여, 사용자가 이보다 더 긴 문자를 입력하지 않는다고 추측한다. 이것은 C++ 프로그래밍에서 흔히 볼 수 있지만, 사용자가 255자를 초과하여 입력할 수 있으므로 좋은 방식이 아니다.
cin을 사용하여 문자열을 읽는 권장 방법은 다음과 같다:
#include <iostream>
int main()
{
char name[255]; // declare array large enough to hold 255 characters
std::cout << "Enter your name: ";
std::cin.getline(name, 255);
std::cout << "You entered: " << name << '\n';
return 0;
}
cin.getline()
을 호출하면 최대 254자를 name
으로 읽을 수 있다. 초과한 문자는 버려진다. 이러한 방법으로 오버플로를 하지 않도록 보장한다!
Manipulating C-style strings
C++은 <cstring>
라이브러리의 일부로 C 스타일 문자열을 조작하는 많은 함수를 제공한다.
strcpy()
를 사용하면 문자열을 다른 문자열로 복사할 수 있다. 일반적으로 문자열에 값을 할당하는 데 사용한다.
#include <cstring>
int main()
{
char source[] = "Copy this!";
char dest[50];
strcpy(dest, source);
std::cout << dest; // prints "Copy this!"
return 0;
}
하지만 주의하지 않으면 strcpy()
로 인해 배열에 오버플로가 쉽게 발생할 수 있다. 다음 예제에서는 dest
가 문자열 길이보다 작으므로 오버플로가 발생한다.
#include <cstring>
int main()
{
char source[] = "Copy this!";
char dest[5]; // note that the length of dest is only 5 chars!
strcpy(dest, source); // overflow!
std::cout << dest;
return 0;
}
C++ 11에서는 strcpy()
대신 strcpy_s
를 사용할 수 있으며, 새로운 매개 변수가 추가되어 대상의 크기를 정의할 수 있습니다. 그러나 모든 컴파일러가 이 함수를 지원하는 것은 아니므로 이를 사용하려면 정수값 1로 __STDC_WANT_LIB_EXT1__
를 정의해야 한다.
#define __STDC_WANT_LIB_EXT1__ 1
#include <cstring> // for strcpy_s
int main()
{
char source[] = "Copy this!";
char dest[5]; // note that the length of dest is only 5 chars!
strcpy_s(dest, 5, source); // An runtime error will occur in debug mode
std::cout << dest;
return 0;
}
또 다른 유용한 함수는 C 스타일 문자열의 길이를 반환하는 strlen()
함수다. (null 종결자는 제외한 길이다.)
#include <iostream>
#include <cstring>
int main()
{
char name[20] = "Alex"; // only use 5 characters (4 letters + null terminator)
std::cout << "My name is: " << name << '\n';
std::cout << name << " has " << strlen(name) << " letters.\n";
std::cout << name << " has " << sizeof(name) / sizeof(name[0]) << " characters in the array.\n";
return 0;
}
/*
My name is: Alex
Alex has 4 letters.
Alex has 20 characters in the array.
*/
기타 유용한 기능:
strcat()
: 한 문자열을 다른 문자열에 추가한다.
strncat()
: 버퍼 길이 검사를 통해 하나의 문자열을 다른 문자열에 추가한다.
strcmp()
: 두 문자열을 비교한다. (같은 경우 0 반환)
strncmp()
: 두 문자열을 지정된 문자 수만큼 비교한다. (같은 경우 0 반환)
다음은 위 몇 가지 개념을 사용하는 예제 프로그램이다.
#include <iostream>
#include <cstring>
int main()
{
// Ask the user to enter a string
char buffer[255];
std::cout << "Enter a string: ";
std::cin.getline(buffer, 255);
int spacesFound = 0;
// Loop through all of the characters the user entered
for (int index = 0; index < strlen(buffer); ++index)
{
// If the current character is a space, count it
if (buffer[index] == ' ')
spacesFound++;
}
std::cout << "You typed " << spacesFound << " spaces!\n";
return 0;
}
Don’t use C-style strings
C 스타일 문자열은 많은 코드에서 사용되기 때문에 C 스타일 문자열에 대해 알아야 한다. 그러나 C 스타일 문자열은 가능하면 사용하지 않는 것이 좋다. C 스타일 문자열을 사용해야 하는 특별한 이유가 없다면 std::string
을 사용하는 것이 좋다. std::string
은 더 쉽고 안전하며 유연하다.
C 스타일 문자열 대신 std::string을 사용하자.
번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/66-c-style-strings/ 입니다.