目次
はじめに
C++でプログラミングをする際、printfやmemcpy、あるいは古いライブラリの関数など、引数としてC言語スタイルの文字列(終端NULL文字 (\0) で終わる char 配列へのポインタ)を要求するものを使いたい場面が時々あります。
しかし、C++のモダンな std::string オブジェクトは、直接これらの関数に渡すことはできません。この両者の橋渡しをするのが、std::string が提供する .c_str() と .data() というメンバ関数です。
この記事では、これら2つの関数を使って、std::string の内容をC言語スタイルのインターフェースに安全に渡す方法を解説します。
.c_str() / .data() を使ったサンプルコード
このコードは、std::string オブジェクトの内容を、.c_str() を使って printf で出力し、.data() を使って char 配列に memcpy でコピーします。
完成コード
#include <iostream>
#include <string>
#include <cstdio> // printf
#include <cstring> // memcpy
using namespace std;
int main() {
string modern_string = "Hello";
// --- 1. .c_str(): C言語スタイルの読み取り専用文字列として渡す ---
// printfは const char* 型の引数を要求する
printf("printfで出力: %s\n", modern_string.c_str());
// --- 2. .data(): 文字データのブロックとしてアクセスする ---
// コピー先のchar配列を準備
char c_style_buffer[20] = {}; // ゼロで初期化
// .data()でstringの内部データへのポインタを取得し、内容をコピー
memcpy(c_style_buffer, modern_string.data(), modern_string.length());
// memcpyは終端NULL文字をコピーしないため、手動で追加する必要がある
c_style_buffer[modern_string.length()] = '\0';
cout << "memcpyでコピーした文字列: " << c_style_buffer << endl;
return 0;
}
コードの解説
1. .c_str()
- 機能:
std::stringの内容を表す、終端NULL文字 (\0) で終わるC言語スタイルの文字列へのポインタ (const char*) を返します。 const char*: 返されるポインタは読み取り専用です。このポインタを通じて文字列の内容を変更しようとすることは禁止されています。- 使いどころ:
printf,fopen,strcmpなど、引数としてconst char*を要求する全てのC言語関数にstd::stringを渡す際に使います。
2. .data()
- 機能:
std::stringが内部で保持している文字データの、先頭要素へのポインタ (const char*) を返します。 .c_str()との違い (C++11以降):- C++11以降、
.data()が返すポインタの指す先も、.c_str()と同様に終端NULL文字で終わることが保証されるようになりました。そのため、現代のC++では、読み取り専用として使う限り、両者の機能はほぼ同じです。 - 意味合いとして、
.c_str()は「C言語の文字列として」、.data()は「単なる文字データの連続したブロックとして」アクセスするというニュアンスの違いがあります。
- C++11以降、
memcpyとの連携:memcpyは指定されたバイト数だけをコピーするため、終端NULL文字はコピーされません。そのため、memcpyでstringの内容をchar配列にコピーした後は、buffer[size] = '\0';のように、手動で終端NULL文字を追加する必要があります。
まとめ
今回は、std::string とC言語スタイルのインターフェースを連携させるための .c_str() と .data() を解説しました。
- C言語スタイルの文字列 (
const char*) が必要な場面では.c_str()を使うのが最も一般的で意図が明確。 .data()も同様に使えるが、こちらはより汎用的な「データブロック」というニュアンス。- これらの関数が返すポインタは、元の
stringオブジェクトが破棄されたり、内容が変更されたりすると無効になるため、一時的な利用に留める必要がある。
古いAPIやライブラリを扱う際に、これらの関数は不可欠な橋渡し役となります。
