【C++】string型をcharに変換/コピーする方法【値 配列 ポインタ string to char】


目次

※ 逆にchar型の文字/配列/ポインタをstd::string型に変換する方法は以下のページへ。

char, char[], char* をstd::stringに変換 …

スポンサーリンク

std::string → const char*

string型メンバ関数c_strで、C言語の文字列をポインタとして取得することが出来ます。

std::string str("abc");
const char* cstr = str.c_str();
取得した文字列データを勝手に変更しないよう注意してください。
ちなみにC++11環境であればc_str関数と同一の働きをするdata関数を利用することも可能です。ただしC++11よりも古い規格ではdata関数はNULL終端されていない文字列参照を返すため注意が必要です。

ダングリングポインタに注意する

string型オブジェクトの寿命が尽きた場合、c_str関数やdata関数で取得していたポインタは不定なものになります(ダングリングポインタ)。

const char *cstr;
{
  std::string str = "a";
  const char *cstr = str.c_str();
  std::cout << cstr; // OK
}
std::cout << cstr;   // NG(開放済みのポインタを参照する危険性あり)

std::string → char*

上記のダングリングポインタを回避するためには、手動によるメモリ確保と値コピーが必要です。

std::string str = "abc";
char* cstr = new char[str.size() + 1]; // メモリ確保
std::strcpy(cstr, str.c_str());        // コピー

cstr; // (char[4]){'a', 'b', 'c', '\0'};

delete[] cstr; // 開放

char_traits::copy

上記のstrcpyはC言語関数ですので、よりC++らしく書きたい場合にはstd::char_traits<char>::copy関数を用います。

std::string str = "abc";

char* cstr = new char[str.size() + 1];
std::char_traits<char>::copy(cstr, str.c_str(), str.size() + 1);

strncpy

固定長配列へのコピーを行う場合には先程のchar_traits<char>::copy関数やstrncpy関数を用いる必要があります。

std::string str = "abc";

char cstr[3];
std::strncpy(cstr, str.c_str(), 3);

cstr; // (char[3]){'a', 'b', 'c'};

strdup

移植性には難がありますが、POSIX規格のstrdup関数でメモリ確保とコピーを一度に行う方法もあります。この場合、strdup内部でstrlen関数由来のサイズ計算が行われるため、若干処理が遅くなる場合があります。

std::string str = "abc";
char* cstr = strdup(str.c_str()); // メモリ確保 & コピー

puts(cstr); // "abc"
free(cstr); // 開放

std::string → char[]

固定長配列の場合はバッファオーバーフローの危険性を考慮し、より安全なstrncpy関数や# char_traits::copy関数を使うと良いでしょう。

std::string str = "abc";

char cary[2];
std::strncpy(cary, str.c_str(), 2);

cary; // {'a', 'b'}

配列の動的確保(可変長配列)が行える環境であれば、以下の方法が利用出来ます。

std::string str = "abc";

char cary[str.size() + 1];
std::strcpy(cary, str.c_str());

cary; // {'a', 'b', 'c', '\0'}
可変長配列(variable-length array)はC言語の機能であり、C++14では正式な機能ではなくコンパイラの独自拡張として提供されているため注意してください。

std::string → char

添字演算子[]の利用が一番手軽ですが、空文字チェックや範囲チェックは必ず行うようにしましょう。*str.c_str()でポインタ先頭の値を取得するテクニックもあります。

std::string str = "ab";
 
if (str.size() > 1) {  // 境界チェック
  char c = str[1];     // 'b'
}
 
if (!str.empty()) {    // 空文字チェック
  char c = str[0];     // 'a'
}
 
char c = *str.c_str(); // 'a'
 
std::string emp = "";
char e = *emp.c_str(); // '\0'

他にもat関数やfront関数を用いる方法がありますが、空文字時の挙動には注意が必要です(例外や未定義動作)。

参考までに、string型の変数が空文字だった場合のそれぞれの挙動をまとめておきます。

std::string s = "";
char a = s.at(0);    // 例外発生: libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
char b = s.front();  // '\0' !!!: 未定義動作: 例外は発生しないが、アサーションエラーが発生する可能性がある
char c = s[0];       // '\0' !!!: 未定義動作: (C++98) ただし(C++11)の場合は末尾文字として'\0'を返す
char d = *s.c_str(); // '\0'

std::u16string → char16_t

char_traits<char16_t>::copy

char16_tやchar32_tの場合はchar_traits<T>::copyテンプレート関数で適切なコピーが行えます。

std::u16string str = u"abc";

char16_t* cstr = new char16_t[str.size() + 1]; // メモリ確保
std::char_traits<char16_t>::copy(cstr, str.c_str(), str.size() + 1); // コピー

delete[] cstr; // 開放

memcpy

std::u16stringのchar16_t型やstd::wstringのwchar_t型の場合は、型のサイズが1バイト以上になるため、strcpy関数は利用できません。代わりに、memcpy関数を使います。

またC言語製ライブラリとの連携を意識する場合にはmallocでのメモリ確保を行うことになりますが、その場合はN * sizeof(char16_t)による倍数指定でのサイズ確保が必要になります。

std::u16string str = u"abc";
size_t size = (str.size() + 1) * sizeof(char16_t);

char16_t* cstr = (char16_t*)malloc(size); // メモリ確保
std::memcpy(cstr, str.c_str(), size);     // コピー

free(cstr); // C言語ライブラリ側で適切に開放

std::string → char* (固定長)

char*に固定長の値をコピーする場合にはchar[]の時と同等の方法を用いることが可能ですが、strncpyはNULL終端を行わない場合がある点に注意しましょう。

手動で終端をゼロ詰めするか、開発環境によってはより便利なstrlcpy関数やstrcpy_sを用いることが可能です。

std::string str = "abc";
char* cstr = new char[3];

std::strncpy(cstr, str.c_str(), 2);
puts(cstr); // "abxx" !!!: 不定

/* 手動でNULL終端 */
cstr[3 - 1] = '\0';
puts(cstr); // "ab"

/* BSDの粋な計らいを享受する */
strlcpy(cstr, str.c_str(), 3); // 末尾が常にNULL終端される
puts(cstr); // "ab"

広告