web-dev-qa-db-ja.com

Std :: stringをconst char *またはchar *に変換するにはどうすればいいですか?

std::stringchar*またはconst char*に変換する方法を教えてください。

835
user37875

std::stringを必要とする関数に const char* を渡したいだけなら、

std::string str;
const char * c = str.c_str();

char *のように書き込み可能なコピーを入手したい場合は、これを使ってそれを行うことができます。

std::string str;
char * writable = new char[str.size() + 1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()] = '\0'; // don't forget the terminating 0

// don't forget to free the string after finished using it
delete[] writable;

編集 :上記は例外セーフではないことに注意してください。 new呼び出しとdelete呼び出しの間に何かがスローされると、自動的にdeleteを呼び出すものがないため、メモリリークが発生します。これを解決する2つの直接的な方法があります。

boost :: scoped_array

boost::scoped_array はスコープ外に出るとメモリを削除します。

std::string str;
boost::scoped_array<char> writable(new char[str.size() + 1]);
std::copy(str.begin(), str.end(), writable.get());
writable[str.size()] = '\0'; // don't forget the terminating 0

// get the char* using writable.get()

// memory is automatically freed if the smart pointer goes 
// out of scope

std :: vector

これは標準的な方法です(外部ライブラリは必要ありません)。 std::vector を使用すると、完全にメモリを管理できます。

std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.Push_back('\0');

// get the char* using &writable[0] or &*writable.begin()
990

言う...

std::string x = "hello";

「string」から「char *」または「const char *」を取得する

xname__がスコープ内にあり、さらに変更されていないときに有効な文字ポインターを取得する方法

C++ 11は物事を単純化します。以下はすべて、同じ内部文字列バッファへのアクセスを許可します。

const char* p_c_str = x.c_str();
const char* p_data  = x.data();
char* p_writable_data = x.data(); // for non-const x from C++17 
const char* p_x0    = &x[0];

      char* p_x0_rw = &x[0];  // compiles iff x is not const...

上記のポインタはすべて、同じ値-バッファ内の最初の文字のアドレスを保持します。空の文字列でも「バッファの最初の文字」があります。これは、C++ 11が明示的に割り当てられた文字列コンテンツの後に常に余分なNUL/0ターミネータ文字を保持することを保証するためです(たとえば、std::string("this\0that", 9)には"this\0that\0"を保持するバッファがあります)。

上記のいずれかのポインターが与えられた場合:

char c = p[n];   // valid for n <= x.size()
                 // i.e. you can safely read the NUL at p[x.size()]

Nonconstname__ポインターp_writable_dataおよび&x[0]からのみ:

p_writable_data[n] = c;
p_x0_rw[n] = c;  // valid for n <= x.size() - 1
                 // i.e. don't overwrite the implementation maintained NUL

NULを文字列の他の場所に書き込むと、stringname__のsize()not変更されます。 stringname__には、任意の数のNULを含めることができます-std::stringによる特別な処理は行われません(C++ 03と同じ)。

C++ 03では、物事はかなり複雑でした(主な違いhighlighted):

  • x.data()

    • const char*を文字列の内部バッファに返します。これは、NUL(つまり['h', 'e', 'l', 'l', 'o']が続く可能性があります)初期化されていない値またはガベージ値によって、undefined behaviour)を持つ偶発的なアクセスがあります。
      • x.size()文字は安全に読み取れます。つまり、x[0]からx[x.size() - 1]まで
      • 空の文字列の場合、0を安全に追加できる非NULLポインターが保証されます(万歳!)が、そのポインターを逆参照しないでください。
  • &x[0]

    • 空の文字列の場合、これには未定義の動作があります(21.3.4)
      • 例えばf(const char* p, size_t n) { if (n == 0) return; ...whatever... }を指定すると、f(&x[0], x.size());のときにx.empty()を呼び出してはいけません-f(x.data(), ...)を使用するだけです。
    • それ以外の場合は、x.data()に従いますが、
      • nonconstxname__の場合、これはnonconstchar*ポインターを生成します。文字列コンテンツを上書きできます
  • x.c_str()

    • const char*を値のASCIIZ(NUL終了)表現に返します(つまり、['h'、 'e'、 'l'、 'l'、 'o'、 '\ 0'])。
    • 実装がそうすることを選択したとしてもほとんどありませんが、C++ 03標準は、文字列実装が別個のNUL終了バッファオンザフライx.data()および&x[0]によって「露出」された潜在的に非NUL終了バッファから
    • x.size() + 1文字は安全に読み取れます。
    • 空の文字列(['\ 0'])でも安全であることが保証されています。

外部の法的インデックスへのアクセスの結果

どちらの方法でポインターを取得する場合でも、上記の説明で保証されている文字よりもポインターに沿ってメモリにアクセスしないでください。これを試みると、undefined behaviourが発生し、読み取りでもアプリケーションクラッシュやガベージ結果が発生する可能性が非常に高く、さらに、大量のデータ、スタック破損、書き込みのセキュリティ脆弱性があります。

それらのポインターはいつ無効になりますか?

stringname__を変更する、またはさらに容量を予約するstringname__メンバー関数を呼び出す場合、上記のメソッドのいずれかによって事前に返されるポインター値はinvalidatedです。これらのメソッドを再度使用して、別のポインターを取得できます。 (ルールは、stringname__sへのイテレーターの場合と同じです)。

下記も参照してくださいxname__がスコープを離れた後、またはさらに変更された後でも文字ポインターを有効にする方法.

では、どちらをbetterとして使用しますか?

C++ 11から、ASCIIZデータには.c_str()を使用し、「バイナリ」データには.data()を使用します(以下でさらに説明します)。

C++ 03では、.c_str()が適切であることが確実でない限り、.data()を使用し、空の文字列に対して安全であるため、&x[0]よりも.data()を優先します。

...必要に応じてdata()を使用するのに十分なプログラムを理解するか、他の間違いを犯す可能性があります...

.c_str()によって保証されるASCII NUL '\ 0'文字は、関連する安全なデータの終わりを示すセンチネル値として多くの関数で使用されます。これは、たとえばfstream::fstream(const char* filename, ...)などのC++のみの関数と、strchr()printf()などのCと共有される関数の両方に適用されます。

返されたバッファに関するC++ 03の.c_str()の保証が.data()のスーパーセットであることを考えると、いつでも安全に.c_str()を使用できますが、人々は時々そうしないことがあります。

  • .data()を使用すると、ソースコードを読んでいる他のプログラマーに、データがASCIIZではない(むしろ、文字列を使用してデータのブロックを保存している(実際にはテキストではないこともある))か、それを渡していることを伝える「バイナリ」データのブロックとして扱う別の関数に。これは、他のプログラマーのコード変更がデータを適切に処理し続けることを保証するための重要な洞察となります。
  • C++ 03のみ:stringname__実装は、NUL終了バッファを準備するために、追加のメモリ割り当てやデータコピーを行う必要がある可能性がわずかにあります。

さらにヒントとして、関数のパラメーターが(constname__)char*を必要とするが、x.size()の取得を要求しない場合、関数おそらくはASCIIZ入力を必要とするため、.c_str()が適切な選択です(関数はテキストがどこで終わるかを知る必要があるので、独立したパラメーターでない場合は、長さのプレフィックスやセンチネル、または何らかの固定長のような規則にしかなれません)。

xname__がスコープを離れるか、さらに変更された後でも、文字ポインターを有効にする方法

copystringxname__の内容をxname__の外の新しいメモリ領域にコピーする必要があります。この外部バッファは、別のstringname__や文字配列変数など、さまざまな場所に存在する可能性があります。異なるスコープ(名前空間、グローバル、静的、ヒープ、共有メモリ、メモリマップなど)にあるため、xname__と異なるライフタイムを持つ場合とそうでない場合がありますファイル)。

テキストをstd::string xから独立した文字配列にコピーするには:

// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
std::string old_x = x;
// - old_x will not be affected by subsequent modifications to x...
// - you can use `&old_x[0]` to get a writable char* to old_x's textual content
// - you can use resize() to reduce/expand the string
//   - resizing isn't possible from within a function passed only the char* address

std::string old_x = x.c_str(); // old_x will terminate early if x embeds NUL
// Copies ASCIIZ data but could be less efficient as it needs to scan memory to
// find the NUL terminator indicating string length before allocating that amount
// of memory to copy into, or more efficient if it ends up allocating/copying a
// lot less content.
// Example, x == "ab\0cd" -> old_x == "ab".

// USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
std::vector<char> old_x(x.data(), x.data() + x.size());       // without the NUL
std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1);  // with the NUL

// USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"
// (a bit dangerous, as "known" things are sometimes wrong and often become wrong)
char y[N + 1];
strcpy(y, x.c_str());

// USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)
char y[N + 1];
strncpy(y, x.c_str(), N);  // copy at most N, zero-padding if shorter
y[N] = '\0';               // ensure NUL terminated

// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTH
char* y = alloca(x.size() + 1);
strcpy(y, x.c_str());

// USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)
char y[x.size() + 1];
strcpy(y, x.c_str());

// USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = new char[x.size() + 1];
strcpy(y, x.c_str());
//     or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());
// use y...
delete[] y; // make sure no break, return, throw or branching bypasses this

// USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE
// see boost shared_array usage in Johannes Schaub's answer

// USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = strdup(x.c_str());
// use y...
free(y);

stringname__からchar*またはconst char*を生成したいその他の理由

それで、上記で(constname__)char*を取得する方法と、元のstringname__から独立したテキストのコピーを作成する方法を見てきましたが、それで何ができますかdo?例のランダムな散らばり...

  • printf("x is '%s'", x.c_str());のように、C++のstringname__のテキストに「C」コードのアクセス権を付与します
  • xname__のテキストを関数の呼び出し元によって指定されたバッファー(strncpy(callers_buffer, callers_buffer_size, x.c_str())など)、またはデバイスI/Oに使用される揮発性メモリー(for (const char* p = x.c_str(); *p; ++p) *p_device = *p;など)にコピーします
  • すでにASCIIZテキストを含む文字配列にxname__のテキストを追加します(例:strcat(other_buffer, x.c_str()))-バッファーをオーバーランしないように注意してください(多くの場合、strncatname__を使用する必要があります)
  • 関数からconst char*またはchar*を返す(おそらく歴史的な理由-クライアントが既存のAPIを使用している-またはC互換性のためにstd::stringを返したくないが、呼び出し元のためにstringname__のデータをコピーしたい) 。]
    • そのポインターが指すローカルstringname__変数がスコープを離れた後に、呼び出し元によって逆参照される可能性のあるポインターを返さないように注意してください
    • 異なるstd::string実装用にコンパイル/リンクされた共有オブジェクト(STLportとコンパイラネイティブなど)を使用する一部のプロジェクトでは、競合を避けるためにASCIIZとしてデータを渡すことができます
186
Tony Delroy

const char *には.c_str()メソッドを使用してください。

あなたは&mystring[0]ポインタを得るためにchar *を使うことができます、しかし、いくつかの落とし穴があります:あなたは必ずしもゼロで終わる文字列を得ない、そしてあなたは文字列のサイズを変更することができません。特に、文字列の末尾を超えて文字を追加しないように注意する必要があります。そうしないと、バッファオーバーラン(およびおそらくクラッシュ)が発生します。

C++ 11までは、すべての文字が同じ連続バッファーの一部になるという保証はありませんでしたが、実際には、std::stringのすべての既知の実装がいずれにせよそのように機能しました。 “&s [0]”はstd :: string内の連続した文字を指していますか? を参照してください。

多くのstringメンバ関数は内部バッファを再割り当てし、保存したポインタを無効にすることに注意してください。直ちに使用してから廃棄するのが最善です。

33
Mark Ransom

C++ 17

C++ 17 (今後の標準)は、テンプレートbasic_stringの概要を変更し、constでないオーバーロードdata()を追加します。

charT* data() noexcept;

戻り値:[0、size()]の各iに対してp + i ==&演算子となるようなポインタp。


CharT const *からのstd::basic_string<CharT>

std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()

CharT *からのstd::basic_string<CharT>

std::string str = { "..." };
char * p = str.data();

C++ 11

CharT const *からのstd::basic_string<CharT>

std::string str = { "..." };
str.c_str();

CharT *からのstd::basic_string<CharT>

C++ 11以降、この規格は次のように述べています。

  1. basic_stringオブジェクト内のcharのようなオブジェクトは連続して格納されます。つまり、すべてのbasic_stringオブジェクトsに対して、アイデンティティー&*(s.begin() + n) == &*s.begin() + n0 <= n < s.size()のようにnのすべての値に対して成り立ちます。

  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    戻り値:*(begin() + pos)の場合はpos < size()、それ以外の場合は値CharT()を持つCharT型のオブジェクトへの参照。参照された値は変更されません。


  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    戻り値:p + i == &operator[](i)の各iに対して[0,size()]となるようなポインタp。

非const文字ポインタを取得するための切断可能な方法があります。

1. C++ 11の連続した記憶域を使用する

std::string foo{"text"};
auto p = &*foo.begin();

プロ

  • シンプルで短い
  • 速い(コピーを伴わない唯一の方法)

短所

  • 最後の'\0'は変更しないでください/必ずしも非constメモリの一部ではありません。

2. std::vector<CharT>を使う

std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();

プロ

  • 単純な
  • 自動メモリ処理
  • 動的

短所

  • 文字列のコピーが必要

3. Nがコンパイル時定数である(そして十分に小さい)場合はstd::array<CharT, N>を使用してください。

std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());

プロ

  • 単純な
  • スタックメモリ処理

短所

  • 静的
  • 文字列のコピーが必要

自動ストレージ削除による生メモリ割り当て

std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);

プロ

  • 小さなメモリ使用量
  • 自動削除
  • 単純な

短所

  • 文字列のコピーが必要
  • 静的(動的使用法にはもっと多くのコードが必要です)
  • ベクトルや配列よりも少ない機能

5.手動操作による生のメモリ割り当て

std::string foo{ "text" };
char * p = nullptr;
try
{
  p = new char[foo.size() + 1u];
  std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
  // handle stuff with p
  delete[] p;
}
catch (...)
{
  if (p) { delete[] p; }
  throw;
}

プロ

  • 最大 'コントロール'

コン

  • 文字列のコピーが必要
  • エラーに対する最大の責任/感受性
  • 複雑な
20
Pixelchemist

私は入力としてchar*を取得する多くの関数を持つAPIを使って作業しています。

私はこの種の問題に直面するために小さなクラスを作成しました、私はRAIIイディオムを実装しました。

class DeepString
{
        DeepString(const DeepString& other);
        DeepString& operator=(const DeepString& other);
        char* internal_; 

    public:
        explicit DeepString( const string& toCopy): 
            internal_(new char[toCopy.size()+1]) 
        {
            strcpy(internal_,toCopy.c_str());
        }
        ~DeepString() { delete[] internal_; }
        char* str() const { return internal_; }
        const char* c_str()  const { return internal_; }
};

そしてあなたはそれを次のように使うことができます。

void aFunctionAPI(char* input);

//  other stuff

aFunctionAPI("Foo"); //this call is not safe. if the function modified the 
                     //literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); //this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string 
                                                //implement reference counting and 
                                                //it may change the value of other
                                                //strings as well.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); //this is fine

既存の文字列の詳細で一意なコピーを作成しているため(DeepStringはコピーできません)、クラスDeepStringを呼び出しました。

9
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
7
cegprakash

これを見てください。

string str1("stackoverflow");
const char * str2 = str1.c_str();

ただし、これはconst char *を返すことに注意してくださいchar *の場合は、strcpyを使用して別のchar配列にコピーします。

7
devsaw