【C++】文字列リテラルのLとは?|ワイド文字とパス区切りの意味を初心者向けに解説

C++

L"assets\\player.png"。DirectXやWindows APIを触り始めると、こんな書き方が、急に出てきます。文字列の前のLは何なのか。パスの区切りが、\だったり/だったりするのは、なぜなのか。

この記事では、Lプレフィックスの意味、ワイド文字が必要な理由、バックスラッシュを2つ書く理由、初心者がハマりやすいミスを解説します。stringとchar配列の違いの記事も、あわせてどうぞ。

Lプレフィックスとは?

L"..." の L は、ワイド文字列リテラルを表す目印です。ざっくり言うと「1文字を2バイト以上で扱う文字列」です。

"hello"    // const char*     1文字 = 1バイト(ASCII / UTF-8)
L"hello"   // const wchar_t*  1文字 = 2バイト(Windowsでは UTF-16)

普通の "hello" は char の配列ですが、L を付けると wchar_t(ワイド文字)の配列になります。この型の違いが、あとで効いてきます。

なぜワイド文字(L)が必要なのか

理由は、Windows APIが内部でUTF-16(wchar_t)を使っているからです。たとえば画像を読み込むDirectXTexの関数は、こういうシグネチャになっています。

HRESULT LoadFromWICFile(
    const wchar_t* szFile,  // ← wchar_t* を要求している
    ...
);

この関数に "player.png"(char*)を渡すと型が合わずコンパイルエラーになります。L"player.png" と書いて wchar_t* にする必要があるわけです。「Windows APIが要求しているから付ける」のが一番の理由です。

日本語パスを安全に扱える

もう一つ大事なのが日本語ファイル名です。char*(ANSI)だと、日本語を含むパスで文字化けや読み込み失敗が起きがちです。

// wchar_t なら日本語パスも安全に扱える
L"assets\\テクスチャ\\プレイヤー.png"

Windowsで日本語ファイル名を扱うなら、ワイド文字が事実上必須です。これが根本の理由になります。

その他のプレフィックス(参考)

L 以外にも、文字コードを指定するプレフィックスがあります。今すぐ覚えなくても大丈夫ですが、こういう仲間がいると知っておくと安心です。

"hello"     // char      実装依存(ASCII / UTF-8 など)
L"hello"    // wchar_t    Windowsでは UTF-16
u8"hello"   // char8_t    UTF-8(C++20)
u"hello"    // char16_t   UTF-16
U"hello"    // char32_t   UTF-32

/ と \ の違い

次は、ファイルパスの区切り文字の話です。まず結論から。

"assets\\textures\\player.png"   // Windows流(バックスラッシュ)
"assets/textures/player.png"     // Unix流(スラッシュ)

もともとWindowsはバックスラッシュ、Linux/macOSはスラッシュでパスを区切る、という文化の違いです。

なぜ \ を2つ書くのか

ここが最大のポイントです。\(バックスラッシュ)は、C++の文字列の中ではエスケープシーケンスの開始記号として予約されています。

"\n"   // 改行
"\t"   // タブ
"\0"   // ヌル文字
"\\"   // バックスラッシュそのもの
"\""   // ダブルクォート

つまり、バックスラッシュ1文字を表したいときは \\ と2つ書く必要があります。もし1つで書くと、次の文字とくっついて別の意味になってしまいます。

"assets\textures\player.png"
//     ~~ ここが \t と解釈され、タブ文字になってしまう!

\t がタブ、\p は…と、意図しない解釈でパスが壊れます。だから \\ と書くわけです。

Windowsではどちらでも動く

実は、Windows APIは両方を受け付けます。内部で自動的に変換してくれるためです。

L"assets\\textures\\player.png"  // OK
L"assets/textures/player.png"    // OK(こちらも動く)

どっちを使うべき?

個人的には /(スラッシュ)をおすすめします。理由は3つです。

  • エスケープが不要で読みやすい(\\ のごちゃつきがない)
  • クロスプラットフォーム対応(Linux/macOSでもそのまま動く)
  • タイプミスが減る(\ の書き忘れバグがなくなる)

ただし、既存コードが \\ で統一されているなら、混在させず統一するほうが優先です。読みやすさは一貫性から生まれます。

【重要】私が実際に文字列リテラルで困った体験談

DirectXでテクスチャを読み込む処理を書いていたとき、パスをコピペで "assets\textures\player.png" とバックスラッシュ1つで書いてしまい、「ファイルが見つからない」と言われ続けて小一時間ハマりました。

原因は \t がタブ文字になっていたこと。パスの文字列自体が壊れていたので、当然ファイルは見つかりません。\\ に直したら一発で読めました。それ以来、パスは / で書くようにしています。

もう一つ。LoadFromWICFile に L を付け忘れて "player.png" を渡し、「const char* から const wchar_t* に変換できない」というエラーで固まったこともあります。エラー文の wchar_t を見て「あ、L か」と気づけるようになると、この手のエラーは怖くなくなります。

文字列リテラルのよくある失敗例と対処法

①パスをバックスラッシュ1つで書く

体験談のとおり、\t などに化けてパスが壊れます。\\ と2つ書くか、いっそ / で書くのが安全です。

②Lの付け忘れ・付けすぎ

wchar_t* を要求する関数には L が必要。逆に char* の関数に L"..." を渡してもエラーです。関数が要求している型に合わせるのが基本です。エラー文の char / wchar_t を見て判断しましょう。

③char文字列とwchar_t文字列を混ぜる

std::string(char系)と std::wstring(wchar_t系)はそのままでは連結・代入できません。プロジェクト内でどちらを使うか統一しておくと、変換地獄を避けられます。

注意点

  • L は関数が wchar_t* を要求するときに付ける
  • パスの \ は\\ と2つ。迷うなら / が安全
  • char系とwchar_t系はプロジェクトでどちらか統一する

ここまでのおさらい

  • L"..." はワイド文字列。Windows APIが wchar_t* を要求するから付ける
  • 日本語パスを安全に扱うにもワイド文字が有効
  • \ はエスケープ記号なので、1文字表すには \\ と書く
  • Windowsは / でも動く。読みやすさ重視なら /、ただし統一が最優先

Lは、ワイド文字(wchar_t)の文字列を表す印です。\はエスケープ記号なので、1文字を表すには、\\と書きます。Windowsは/でも動くので、読みやすさを重視するなら/ですが、どちらかに統一するのが、最優先です。

関連記事