C言語/文字と文字列

大まかな説明を言うと、C言語でいう(文字列ではなく)「文字」とは基本、たった1文字からなる半角英数字および半角記号のことです。半角スペースも文字になります。実行環境にもよる可能性がありますが、日本語はプログラム上は文字ではありません。
「文字列」とは、文字を並べたものです。「Hello」 とか「Hello World」は文字列です。
C言語には、直接的に文字列を扱う専用機能は組み込まれていないので、文字列は文字の配列として定義されています。Cでは、文字配列を文字のリストではなく、文字列で表現することができ、末尾には自動的にヌル終端文字('\0')が追加されます。例えば、"String" という文字列を格納するには、次のように書きます。
あるいは
最初の例では、文字列の末尾にコンパイラによって自動的にヌル文字が付加されます。規約としてライブラリ関数は文字列の末尾にヌル文字('\0')が付加されることを想定しています。 後者の宣言では、個々の要素を示しているため、ヌル文字の終端を手動で追加する必要があります。
文字列は、必ずしも明示的な変数に代入する必要はありません。文字列は無名の文字列(文字列リテラルといいます)として直接作成し使用することができます(printf関数の第一引数など)。
特別に長い文字列を作成するには、文字列を複数のセクションに分割し、最初のセクションを引用符で閉じて、次の行で文字列を再開する必要があります(これも引用符で始まり、引用符で終わります)。
文字列は、行末にバックスラッシュ文字を置くことで複数行にまたがることもできますが、この方法は非推奨です。
文字列処理ルーチンの便利なライブラリがありますので、別のヘッダーをインクルードすることで使用できます。
この標準的な文字列ライブラリ <string.h> は、文字列に対して様々な処理を行うことができます。
文字定数(リテラル)は、'p'のように引用符(')で囲まれ、指定されたcharの値の整数値にコンパイルされます。 文字定数の型はconst intです。
文字列定数(リテラル)は、"Hello world!"のように二重引用符(")で囲まれ、指定されたcharの値の配列にコンパイルされ、さらに文字列の終わりを示すヌル終端文字('\0')コードが付加されます。 文字列定数の型はchar const []です。
文字列リテラルは、ソースコードに直接、改行などの制御文字や、文字列の中で特別な意味を持つ文字を埋め込んではいけません。 このような文字を文字列に含めるためには、次のようにバックスラッシュ・エスケープを使用することができます。 なお、バックスラッシュを入力した場合、多くの日本語環境では 円マーク \ が表示されます。
C言語では、char型の配列が、文字列を表現する際に使われます。 文字列を「""(ダブルクォーテーション)」で囲むと、その文字列を表現する配列となります。 文字列は「'\0'」( ヌル文字、 0x00 )で終わります。
上の例では、配列strの値は次の表のようになります。
また、同様の処理をポインタを用いて記述することができます。
複数の文字列を共通の名前でまとめて扱いたい場合、多次元の配列を用います。
一番右の次元の要素数で、扱う文字列1個あたりの長さ(ヌル文字を含む)を指定し、残りの次元の要素数で、扱う文字列の数を指定します。
たとえば英数字10文字(終端の '\0' も含め)までの長さで、4単語までを格納できる用意したい場合、
のようになります。
つまり構文は、
です。
文字列の配列のある文字列にアクセスしたい場合、配列の添字に一番右の次元を除く次元を指定します。
上記例のように、char 宣言時の配列は2次元配列の書式であっても(たとえばnum[3][6] のような書式)、 printfなど利用時には次元が一段階だけ下がるので書式は1次元配列でアクセスすることになるので(たとえばnum[1] のような書式)、間違えないように注意せよ(初心者はよく間違えてエラーになり悩む)。

また、同様の処理をポインタの配列を用いて記述することができます。
上記コードのようにポインタ形式で *p[3] で定義した場合の数値「3」の意味は、文字配列の要素数です(文字列長ではありません)。 なので、たとえば"one"を"onevvvvvvvvvvvv"のように長くしても、コンパイラの許可する範囲内の長さならエラーになりません。 また、当然だが要素数を足していって "three", "four" などと足していくと、宣言時の3個ぶんの確保をオーバーするのでエラーになります。 ともかく上記コードのようにポインタ形式で宣言することにより、宣言時の書式の次元とprintfなど利用時の書式が次元が一致するので、用途によっては、ポインタ方式で宣言するのもよいでしょう。
C言語はワイド文字列をサポートしています。ワイド文字列はwchar_t型の配列として定義され、(少なくとも)16ビットの値を持ちます。文字列の前にLを付けて次のように書きます。
この機能により、256種類以上の文字が必要な文字列が可能になります(可変長のchar文字列も使用可能です)。これらの文字列はゼロ値のwchar_tで終わります。これらの文字列は<string.h>の関数ではサポートされていません。その代わり、<wchar.h>で宣言された独自の関数を持っています。
ワイド文字は、型のサイズや文字集合とエンコーディングが規定されておらずプラットフォームおよび処理系依存でした。 この問題を統一的な文字集合とエンコーディングの体型を考案し解決すべく、Unicodeが国際規格として登場し、CもC11から言語仕様レベルで対応が始まりました。
尚、JIS規格の、『プログラム言語C』はC99の和訳なのでUnicodeへの言及はありません。
ISO/IEC 9899:2011(通称C11) では、新たに2つの文字型 char16_t と char32_t が導入されました。 これらはそれぞれ UTF-16 と UTF-32 を内部表現とします。 u'c' や U'c' あるいは u"str" や U"str" のように小文字の u あるいは大文字の U を前置することで、それぞれ char16_t と char32_t の文字定数・文字列リテラルを表現します。
ただし、C23で明文化されましたが、code>char16_t も char32_tコードポイントの値が 1 つのコードユニットとして符号化できる場合に限ります。そうでない場合は2つのコードユニットのペア「サロゲートペア」としてエンコードされます。
また、u8 を前置することで UTF-8 の文字列リテラルを表現しますが、C11には、char8_t 型は存在せず、従来の char 型で代用することにしました。 これは単純で大きな間違いで、Cではcharの符号の有無を実装依存としており、UTF-8のcode unitの値域は0x80から0xffの範囲にも及んでおり、符号の有無が曖昧なcharでは表現できることが担保できないのです。 このため、C23ではchar8_t 型が定義されました(後述)。
char16_tおよびchar32_tはそれぞれuint_least16_tおよびuint_least32_tのtypedefエイリアスです。
char16_t型のサイズは16ビットよりも大きい可能性があるが、格納される値は16ビット幅です。同様に、char32_t型のサイズは32ビットよりも大きい可能性があるが、格納される値は32ビット幅である。
2022年10月現在策定中のC23では、UTF-8 を格納する方として正式にchar8_t 型が定義されました。
UTF-8 文字定数は char8_t 型です。UTF-8 文字定数は、16 進数または 8 進数のエスケープシーケンスで生成されない場合、その値は ISO/IEC 10646 のコードポイント値に等しく、ただしコードポイント値が単一の UTF-8 コードユニットとして符号化されることが条件です。それ以外の場合、UTF-8 文字定数の値は、16 進数または 8 進数のエスケープシーケンスで指定された数値になります。
charとwchar_tがどのような文字エンコーディングを表すかは、C言語の標準では規定されていませんが、0x00と0x0000という値は文字列の終わりを示しており文字ではありません。 文字エンコーディングの影響を直接受けるのは、入力コードと出力コードです。その他のコードはあまり影響を受けないはずです(この仮定でプロクラムを作るとエンコーディングの種類によっては(例えば ShiftJISでは)1つの文字の2バイト目に '\ の様なエスケープ文字が現れた時、正しくエンコーディング出来ず、場合によってはバッファオーバーフロー等を引き起こします。)。また、ソースコードに文字列を書き込めるようにするためには、エディターもエンコーディングに対応していなければなりません。
符号化方式には大きく分けて3種類あります。
ユーザ定義関数の引数に文字列を使いたい場合、例えば下記のようになります。