C ++에서 문자열과 char [] 유형의 차이점


126

나는 약간의 C를 알고 있으며 이제 C ++을 살펴보고 있습니다. C 문자열을 처리하기 위해 char 배열에 익숙하지만 C ++ 코드를 살펴보면 문자열 유형과 char 배열을 모두 사용하는 예제가 있습니다.

#include <iostream>
#include <string>
using namespace std;

int main () {
  string mystr;
  cout << "What's your name? ";
  getline (cin, mystr);
  cout << "Hello " << mystr << ".\n";
  cout << "What is your favorite team? ";
  getline (cin, mystr);
  cout << "I like " << mystr << " too!\n";
  return 0;
}

#include <iostream>
using namespace std;

int main () {
  char name[256], title[256];

  cout << "Enter your name: ";
  cin.getline (name,256);

  cout << "Enter your favourite movie: ";
  cin.getline (title,256);

  cout << name << "'s favourite movie is " << title;

  return 0;
}

( http://www.cplusplus.com의 두 가지 예 )

나는 이것이 널리 질문되고 대답 된 (분명히?) 질문이라고 생각하지만, 누군가 C ++에서 문자열을 처리하는 두 가지 방법 (성능, API 통합, 각 방식이 어떻게 다른지)을 말해 줄 수 있다면 좋을 것입니다 더 나은 ...).

감사합니다.


답변:


187

char 배열은 문자 배열입니다.

  • 스택에 할당되면 (예와 같이) 항상 예를 ​​들어 점유합니다. 텍스트 길이에 관계없이 256 바이트
  • 힙에 할당 된 경우 (malloc () 또는 new char [] 사용) 이후에 메모리를 해제해야하며 항상 힙 할당 오버 헤드가 발생합니다.
  • 256 자 이상의 텍스트를 배열에 복사하면 충돌이 발생하고 어설 션 메시지가 생성되거나 프로그램의 다른 곳에서 설명 할 수없는 (미스) 동작이 발생할 수 있습니다.
  • 텍스트의 길이를 결정하려면 문자를 기준으로 \ 0 문자의 배열을 스캔해야합니다.

문자열은 char 배열을 포함하는 클래스이지만 자동으로 관리합니다. 대부분의 문자열 구현에는 16 문자의 내장 배열이 있으므로 짧은 문자열은 힙을 조각화하지 않으며 더 긴 문자열에는 힙을 사용합니다.

다음과 같이 문자열의 char 배열에 액세스 할 수 있습니다.

std::string myString = "Hello World";
const char *myStringChars = myString.c_str();

C ++ 문자열에는 \ 0 문자가 포함될 수 있으며, 계산없이 길이를 알 수 있고, 짧은 텍스트의 경우 힙 할당 문자 배열보다 빠르며 버퍼 오버런으로부터 보호 할 수 있습니다. 또한 읽기 쉽고 사용하기 쉽습니다.


그러나 C ++ 문자열은 DLL 경계를 넘어서서 사용하기에 (매우) 적합하지 않습니다. 왜냐하면 문자열 함수가 다르게 동작 할 위험이 없도록, DLL 함수를 사용하는 사용자는 정확히 동일한 컴파일러 및 C ++ 런타임 구현을 사용해야합니다.

일반적으로 문자열 클래스는 호출 힙에서 힙 메모리를 해제하므로 공유 (.dll 또는 .so) 버전의 런타임을 사용하는 경우에만 메모리를 다시 확보 할 수 있습니다.

간단히 말해서 : 모든 내부 함수와 메소드에서 C ++ 문자열을 사용하십시오. .dll 또는 .so를 작성하는 경우 공용 (dll / so-exposed) 함수에서 C 문자열을 사용하십시오.


4
또한 문자열에는 실제로 깔끔한 도우미 함수가 많이 있습니다.
Håkon

1
DLL 경계에 대해서는 조금 믿지 않습니다. 매우 특수한 조건에서 잠재적으로 중단 될 수 있습니다 (하나의 DLL은 다른 DLL에서 사용하는 것과 다른 버전의 런타임에 정적으로 연결되어 있으며 이러한 상황에서는 아마도 더 나쁜 일이 발생할 수 있습니다). 모든 사람이 기본값을 사용하는 일반적인 경우 표준 런타임의 공유 버전 (기본값)은 발생하지 않습니다.
Martin York

2
예 : 공용 API에 std :: string &이있는 libfoo라는 공용 라이브러리의 VC2008SP1 컴파일 된 바이너리를 배포합니다. 이제 누군가가 libfoo.dll을 다운로드하고 디버그 빌드를 수행합니다. 그의 std :: string에는 추가 디버그 필드가 포함될 수 있으므로 동적 문자열의 포인터 오프셋이 이동합니다.
Cygon

2
예 2 : 2010 년에 누군가 누군가 libfoo.dll을 다운로드하여 VC2010 빌드 응용 프로그램에서 사용합니다. 그의 코드는 MSVCP100.dll을로드하고 libfoo.dll은 여전히 ​​MSVCP90.dll을로드합니다-> 두 개의 힙을 얻습니다-> 메모리가 해제 될 수 없습니다. 다시 포인터.
Cygon

1
"간단히 말하면 : 모든 내부 함수와 메서드에 C ++ 문자열을 사용하십시오." 당신의 예를 이해하려고 노력하는 것은 나의 두뇌 팝을 하녀
Stephen

12

Arkaitz는 string관리 유형입니다. 이것이 당신에게 의미 하는 것은 문자열의 길이에 대해 걱정할 필요가 없으며 문자열의 메모리를 비우거나 다시 할당하는 것에 대해 걱정할 필요가 없다는 것입니다.

반면에 char[]위 의 표기법은 문자 버퍼를 정확히 256 자로 제한했습니다. 해당 버퍼에 256 자 이상을 쓰려고하면 프로그램이 소유 한 다른 메모리를 덮어 씁니다. 최악의 경우, 소유하지 않은 메모리를 덮어 쓰려고 시도하고 OS가 그 자리에서 프로그램을 종료시킵니다.

결론? 문자열은 프로그래머에게 훨씬 친숙하고, char []는 컴퓨터에 훨씬 효율적입니다.


4
최악의 경우 다른 사람들이 컴퓨터에서 메모리를 덮어 쓰고 악성 코드를 실행합니다. buffer overflow 도 참조하십시오 .
David Johnstone

6

문자열 유형은 문자열에 대해 완전히 관리되는 클래스 인 반면, char []는 여전히 C에있는 문자열입니다 (문자열을 나타내는 바이트 배열).

API 및 표준 라이브러리 측면에서 모든 것은 char []가 아닌 문자열 측면에서 구현되지만 char []를받는 libc에는 여전히 많은 함수가 있으므로 char []를 제외하고는 함수를 사용해야 할 수도 있습니다. 항상 std :: string을 사용하십시오.

물론 효율성 측면에서 관리되지 않는 메모리의 원시 버퍼는 대부분 많은 경우에 거의 항상 빠를 것이지만 문자열 비교를 고려하십시오. 문자별로 비교해야합니다.


5

나는 개인적으로 오래된 코드와의 호환성을 제외하고 char * 또는 char []를 사용하려는 이유를 알지 못합니다. std :: string은 c-string을 사용하는 것보다 느리지 않습니다. 단, 재배치가 처리됩니다. 만들 때 크기를 설정할 수 있으므로 원하는 경우 다시 할당하지 않아도됩니다. 인덱싱 연산자 ([])는 일정한 시간 액세스를 제공합니다 (그리고 모든 의미에서 c-string 인덱서를 사용하는 것과 똑같은 의미입니다). at 메소드를 사용하면 검사하지 않은 한 안전 검사를받을 수 있으며 c 문자열로 얻을 수없는 것입니다. 컴파일러는 릴리스 모드에서 인덱서 사용을 대부분 최적화합니다. c- 문자열로 엉망이되기 쉽습니다. delete vs delete [], 예외 안전, c- 문자열 재 할당 방법 등.

그리고 COW 문자열, MT를위한 비 COW와 같은 고급 개념을 다루어야 할 때는 std :: string이 필요합니다.

참조를 사용하고 가능한 한 참조를 const 참조하는 한 복사가 걱정된다면 복사로 인한 오버 헤드가 없으며 c-string으로 수행하는 것과 동일합니다.


+1 DLL 호환성과 같은 구현 문제는 고려하지 않았지만 COW가 있습니다.

12 바이트의 char 배열을 알고 있습니까? 문자열을 인스턴스화하면 실제로 효율적이지 않을 수 있습니까?
David 天宇 Wong

@David : 성능이 매우 민감한 코드가 있다면 예. std :: string 멤버의 초기화 외에도 std :: string ctor 호출을 오버 헤드로 간주 할 수 있습니다. 그러나 조기 최적화는 많은 코드 기반을 불필요하게 C 스타일로 만들었으므로주의하십시오.
Abhay

1

문자열에는 도우미 기능이 있으며 문자 배열을 자동으로 관리합니다. 문자열을 연결할 수 있으며, char 배열의 경우 새 배열에 복사해야하며 런타임에 문자열의 길이를 변경할 수 있습니다. char 배열은 문자열보다 관리하기 어렵고 특정 함수는 문자열을 입력으로 만 받아 들일 수 있으므로 배열을 문자열로 변환해야합니다. 배열을 사용하지 않아도되도록 문자열을 사용하는 것이 좋습니다. 배열이 객관적으로 좋으면 문자열이 없을 것입니다.


0

(char *)를 string.begin ()으로 생각하십시오. 근본적인 차이점은 (char *)는 반복자이고 std :: string은 컨테이너라는 것입니다. 기본 문자열을 고수하면 (char *)가 std :: string :: iterator의 기능을 제공합니다. 반복자의 이점과 C와의 호환성을 원할 때 (char *)를 사용할 수 있지만 예외는 예외입니다. 항상 그렇듯이 반복자 무효화에주의하십시오. 사람들이 (char *) 안전하지 않다고 말할 때 이것이 의미하는 바입니다. 다른 C ++ 반복자만큼 안전합니다.


0

차이점 중 하나는 Null 종료 (\ 0)입니다.

C 및 C ++에서 char * 또는 char []는 매개 변수로 단일 char에 대한 포인터를 가져 와서 0 메모리 값에 도달 할 때까지 메모리를 따라 추적합니다 (종종 널 종료 자라고 함).

C ++ 문자열에는 \ 0 문자가 포함될 수 있으며, 길이를 세지 않고 알 수 있습니다.

#include<stdio.h>
#include<string.h>
#include<iostream>

using namespace std;

void NullTerminatedString(string str){
   int NUll_term = 3;
   str[NUll_term] = '\0';       // specific character is kept as NULL in string
   cout << str << endl <<endl <<endl;
}

void NullTerminatedChar(char *str){
   int NUll_term = 3;
   str[NUll_term] = 0;     // from specific, all the character are removed 
   cout << str << endl;
}

int main(){
  string str = "Feels Happy";
  printf("string = %s\n", str.c_str());
  printf("strlen = %d\n", strlen(str.c_str()));  
  printf("size = %d\n", str.size());  
  printf("sizeof = %d\n", sizeof(str)); // sizeof std::string class  and compiler dependent
  NullTerminatedString(str);


  char str1[12] = "Feels Happy";
  printf("char[] = %s\n", str1);
  printf("strlen = %d\n", strlen(str1));
  printf("sizeof = %d\n", sizeof(str1));    // sizeof char array
  NullTerminatedChar(str1);
  return 0;
}

산출:

strlen = 11
size = 11
sizeof = 32  
Fee s Happy


strlen = 11
sizeof = 12
Fee

"특정에서 모든 문자가 제거됩니다"아니오, "제거"되지 않으며, char 포인터를 인쇄하면 null 종결 자까지만 인쇄됩니다. (문자 *가 끝을 아는 유일한 방법이므로) 문자열 클래스는 전체 크기 자체를 알고 있으므로 그냥 사용합니다. 문자 *의 크기를 알고 있다면 모든 문자를 직접 인쇄 / 사용할 수 있습니다.
Puddle
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.