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 ++で文字列を処理するための2つの方法(パフォーマンス、API統合、それぞれの方法)の正確な違いは誰かに教えてもらえれば幸いですより良い、...)。

ありがとうございました。


これは役立つかもしれません:C ++ char *とstd :: string
Wael Dalloul 2009

回答:


187

char配列はそれだけです-文字の配列:

  • スタックに割り当てられている場合(例のように)、常に占有します。含まれるテキストがどのくらい長くても256バイト
  • ヒープに割り当てられた場合(malloc()またはnew char []を使用)、後でメモリを解放する必要があり、常にヒープ割り当てのオーバーヘッドが発生します。
  • 256文字を超えるテキストを配列にコピーすると、クラッシュしたり、醜いアサーションメッセージを生成したり、プログラムの他の場所で説明できない(誤)動作を引き起こしたりする可能性があります。
  • テキストの長さを決定するには、配列を文字ごとにスキャンして\ 0文字を探す必要があります。

文字列は、char配列を含むクラスですが、自動的に管理されます。ほとんどの文字列実装には16文字の組み込み配列があり(短い文字列はヒープを断片化しません)、長い文字列にはヒープを使用します。

次のようにして、文字列のchar配列にアクセスできます。

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

C ++文字列には、埋め込まれた\ 0文字を含めることができ、カウントせずにその長さがわかります。短いテキストの場合、ヒープに割り当てられたchar配列よりも高速で、バッファーオーバーランから保護されます。さらに、それらはより読みやすく、使いやすくなっています。


ただし、C ++文字列は、DLLの境界を越えた使用には(非常に)適していません。これは、そのようなDLL関数のユーザーがまったく同じコンパイラーとC ++ランタイム実装を使用していることを確認する必要があるためです。

通常、文字列クラスは呼び出し側のヒープでもヒープメモリを解放するため、ランタイムの共有(.dllまたは.so)バージョンを使用している場合にのみ、メモリを再度解放できます。

つまり、すべての内部関数とメソッドでC ++文字列を使用します。.dllまたは.soを作成する場合は、公開(dll / so-exposed)関数でC文字列を使用します。


4
さらに、文字列には、本当にきちんとしたヘルパー関数がたくさんあります。
–Håkon、

1
DLLの境界については少し信じられません。非常に特殊な状況下では、破損する可能性があります((1つのDLLが他のDLLで使用されているものとは異なるバージョンのランタイムに対して静的にリンクしていて、これらの状況ではおそらく最悪の事態が最初に発生します)が、全員がデフォルトを使用している一般的なケースでは標準ランタイムの共有バージョン(デフォルト)これは起こりません。
マーティンヨーク

2
例:パブリックAPIにstd :: string&があるlibfooというパブリックライブラリのVC2008SP1でコンパイルされたバイナリを配布します。ここで誰かがlibfoo.dllをダウンロードし、デバッグビルドを行います。彼のstd :: stringには、いくつかの追加のデバッグフィールドが含まれている可能性があり、動的文字列のポインタのオフセットが移動します。
サイゴン2009

2
例2:2010年に、誰かがlibfoo.dllをダウンロードして、VC2010で構築されたアプリケーションで使用します。彼のコードはMSVCP100.dllをロードし、libfoo.dllは引き続きMSVCP90.dllをロードします-> 2つのヒープを取得します->メモリを解放できません。libfooが文字列参照を変更して新しいstd :: stringを渡すと、デバッグモードでアサーションエラーが発生します戻るポインター。
サイゴン2009

1
「要するに、すべての内部関数とメソッドでC ++文字列を使用してください。あなたの例を理解しようとすると、私の頭がポップになります。
スティーブン

12

stringマネージドタイプであるArkaitzは正解です。これが意味することは、文字列の長さについて心配する必要がなく、文字列のメモリの解放や再割り当てについて心配する必要がないということです。

一方、char[]上記の場合の表記では、文字バッファーは正確に256文字に制限されています。そのバッファに256文字を超えて書き込もうとした場合、せいぜいプログラムが「所有」している他のメモリを上書きするだけです。最悪の場合、所有していないメモリを上書きしようとすると、OSがその場でプログラムを強制終了します。

ボトムライン?文字列はよりプログラマーフレンドリーで、char []はコンピュータにとってはるかに効率的です。


4
最悪の場合、他の人がメモリを上書きし、コンピュータ上で悪意のあるコードを実行します。バッファオーバーフローも参照してください。
David Johnstone、

6

まあ、文字列型は文字列の完全に管理されたクラスですが、char []は文字列を表すCのバイト配列です。

APIと標準ライブラリに関しては、すべてがchar []ではなく文字列で実装されていますが、libcにはchar []を受け取る関数がまだたくさんあるので、それらを使用する必要があるかもしれません。常にstd :: stringを使用します。

もちろん、効率の点では、アンマネージメモリのrawバッファは多くの場合ほとんどの場合高速ですが、たとえば、std :: stringは、最初にチェックするサイズを常に持っていますが、char []では文字ごとに比較する必要があります。


5

私は個人的に、古いコードとの互換性を除いて、char *またはchar []を使用したい理由を知りません。std :: stringは、再割り当てを処理することを除いて、c-stringを使用するよりも遅くありません。作成時にサイズを設定できるため、必要に応じて再割り当てを回避できます。それはインデックス演算子([])で一定時間のアクセスを提供します(そして、あらゆる意味でc-stringインデクサーを使用するのとまったく同じです)。atメソッドを使用すると、境界チェック済みの安全性も得られます。これを記述しない限り、c-stringでは得られないものになります。コンパイラは、ほとんどの場合、リリースモードでのインデクサーの使用を最適化します。c-stringをいじるのは簡単です。削除vs delete []、例外の安全性、c文字列の再割り当て方法なども含まれます。

また、COW文字列やMTの非COWなどの高度な概念を扱う必要がある場合は、std :: stringが必要になります。

コピーを心配している場合、可能な限り参照とconst参照を使用している限り、コピーによるオーバーヘッドは発生しません。これは、c-stringを使用する場合と同じです。


+1 DLLの互換性などの実装の問題を考慮していませんが、COWを入手しました。

私のchar配列が12バイトであることを知っていますか?そのために文字列をインスタンス化した場合、それは本当に効率的ではないかもしれませんね?
David天宇Wong

@David:非常にパフォーマンスが重要なコードがある場合は、はい。std :: stringメンバーの初期化に加えて、std :: string ctor呼び出しをオーバーヘッドと見なす場合があります。ただし、時期尚早な最適化により、多くのコードベースが不必要にCスタイルになったことを覚えておいてください。注意してください。
Abhay

1

文字列にはヘルパー関数があり、char配列を自動的に管理します。文字列を連結できます。char配列の場合、新しい配列にコピーする必要があります。文字列は実行時に長さを変更できます。char配列は文字列より管理が難しく、特定の関数は文字列のみを入力として受け入れる場合があるため、配列を文字列に変換する必要があります。文字列を使用することをお勧めします。これらは、配列を使用する必要がないように作成されています。配列が客観的に優れていれば、文字列はありません。


0

(char *)をstring.begin()と考えてください。本質的な違いは、(char *)がイテレーターであり、std :: stringがコンテナーであることです。基本的な文字列に固執する場合、(char *)はstd :: string :: iteratorの機能を提供します。イテレータの利点とCとの互換性も必要な場合は(char *)を使用できますが、これは例外であり、規則ではありません。いつものように、イテレータの無効化に注意してください。(char *)が安全でないと人々が言うとき、これは彼らが意味することです。他のC ++イテレータと同じくらい安全です。


0

違いの1つは、ヌル終了(\ 0)です。

CおよびC ++では、char *またはchar []はパラメーターとして単一のcharへのポインターを取り、0のメモリー値に到達するまでメモリーに沿って追跡します(しばしばnullターミネーターと呼ばれます)。

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ターミネータまでしか印刷されません。(それがchar *が最後を知る唯一の方法であるため)文字列クラスはフルサイズ自体を知っているので、それを使用します。char *のサイズがわかっている場合は、すべてのcharsを自分で印刷/使用することもできます。
水たまり
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.