소년코딩

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을 사용하자.


cpp 번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/66-c-style-strings/ 입니다.

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

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

최근에 게시된 이야기