👽

C/C++ の多文字リテラル

2022/10/16に公開

はじめに

まず次のサンプルコードを見てください。

#include <stdio.h>

int main() {
    int x = 'abcd';  // 多文字リテラル
    print("0x%x", x);
    return 0;
}

警告は出るかもしれませんが、このコードはコンパイルが通ります。出力結果は次のとおりです。

0x61626364

'abcd' のような書き方は多文字リテラル (multi-character character constant) と呼ばれます。多文字リテラルのことを知らない状態でこのようなコードに出くわしたら驚くのではないでしょうか。
少なくとも過去の私は驚きました……。ダブルクオーテーションとシングルクォーテーションを書き間違えたのかな?コンパイルエラーだよね?って。

この記事の目的

C/C++ の多文字リテラルという言語仕様はあまり知られていないと思いましたので、突然前述のようなコードに出くわしてしまう方が私と同じように驚いてしまわないように、ここに簡単な記事を残そうと思いました。

ただし、多文字リテラルが有用なケースはほとんど無いと思います。特にC言語を学習中の方は自分のソースコードで無理に多文字リテラルを使用しないようにしてください。きっと同じことを実現可能なもっとよい方法が他にあるはずです。私も、実際の使用例は後述する WDK の API を使用したドライバー開発くらいしか知りません。

このような書き方が C/C++ のコードとして合法であることを知るに留めておいてください。

もう少し詳しく

C/C++ 言語仕様

多文字リテラルは、C/C++ の規格書にも定義されています。

6.4.4.4 文字定数

補足説明 単純文字定数は, 一重引用符で囲まれた一つ以上の多バイト文字の並びとする(例えば 'x')。ワイド文字定数は, 英字 L という接頭語が付いていることを除いて単純文字定数と同一とする。後に詳述する幾つかの例外を除いて, 列の要素はソース文字集合の任意の要素とする。それらは実行文字集合の要素に処理系定義の方法で対応付ける。

(中略)

2文字以上を含む(例えば 'ab')又は1バイトの実行文字で表現できない文字若しくは逆斜線表記を含む単純文字定数の値は, 処理系定義とする。単純文字定数が単一の文字又は逆斜線表記を含む場合, その値はその単一の文字又は逆斜線表記の値を持つ char 型のオブジェクトを int 型に変換したときの結果の値とする。

(中略)

2文字以上の多バイト文字を又は実行拡張文字集合で表現できない多バイト文字若しくは逆斜線表記を含むワイド文字定数の値は, 処理系定義とする。

2.13.2 文字リテラル

 文字リテラルは, 1文字又は複数文字を一重引用符でくくる(例えば, 'x')。その前に英字 L を置く(例えば, L'x')こともある。L を置かない通常の文字リテラルは, ナロー文字リテラルとも呼ばれる。1個の《c 文字》をくくった通常の文字リテラルは, char 型をもち, その《c 文字》の実行文字集合での符号数値に等しい値をもつ。2個以上の《c 文字》をくくった通常の文字リテラルを, 多文字リテラル(multicharacter literal)という。多文字リテラルは, int 型をもつ。その値は, 処理系定義とする。
 文字 L で始まる文字リテラル(例えば L'x')を, ワイド文字リテラルという。ワイド文字リテラルは, wchar_t 型をもつ。2個以上の《c 文字》をくくったワイド文字リテラルの値は, 処理系定義とする。

MSVC 固有仕様

一方で、多文字リテラルは Microsoft 固有の仕様 という記載もされています。

実際の使用例

WDK (Windows Driver Kit) に、多文字リテラルを引数に取るAPI(WdfMemoryCreate)が存在します。第3引数 PoolTag が多文字リテラルを取る int 型の引数です。

ドライバー開発のような限られたメモリを使用する状況で、デバッグ用の文字列を受け取るためには、通常の文字列リテラルを使用するのではなく多文字リテラルの方が都合が良かったのだと推測します。

Discussion