C言語の標準ライブラリには、文字列(文字文字列とバイト文字列)に対する操作を実行する関数群が用意されています。コピー、連結、トークン化、検索など、さまざまな操作がサポートされています。文字文字列の場合、標準ライブラリでは文字列がヌル終端されるという慣例が用いられています。つまり、 n文字の文字列はn +1個の要素からなる配列として表現され、最後の要素は数値0の「ヌル文字」となります。
プログラミング言語自体における文字列のサポートは、コンパイラが引用符で囲まれた文字列定数をヌル終端文字列に変換するという点のみである。
文字列は、最初のゼロコードユニット(しばしばNULコードユニットと呼ばれる)で終端される、連続したコードユニットのシーケンスとして定義されます。 [ 1 ]これは、最初に現れるゼロコードユニットが文字列の終わりを示すため、文字列にはゼロコードユニットを含めることができないことを意味します。文字列の長さは、ゼロコードユニットより前のコードユニットの数です。[ 1 ]ゼロ終端を格納するためのスペースが必要なため、文字列が占めるメモリは常に長さより1コードユニット多くなります。
一般的に、文字列という用語は、コード単位が 型である文字列を意味しchar、これはすべての最新のマシンで正確に 8 ビットです。C90は、コード単位が 型であるワイド文字列[ 1 ]を定義しており、これは最新のマシンで 16 ビットまたは 32 ビットです。これはUnicodewchar_t用に意図されていましたが、代わりに通常の文字列で Unicode にUTF-8 を使用することがますます一般的になっています。
文字列は、最初のコードユニットへのポインタを渡すことで関数に渡されます。char*と はwchar_t*異なる型であるため、ワイド文字列を処理する関数は通常の文字列を処理する関数とは異なり、名前も異なります。
文字列リテラル("text"C ソースコード内)は、コンパイル時に配列(char[]C またはconst char[]C++ 内)に変換されます。[ 2 ]結果として、すべての文字と末尾のゼロ コード ユニットを含むコード ユニットの配列が生成されます。C90 では、L"text"ワイド ストリングが生成されます。文字列リテラルはゼロ コード ユニットを含むことができます(\0ソースに を挿入する方法の 1 つ)が、これにより文字列はその位置で終了します。リテラルの残りの部分はメモリに配置されます(末尾に別のゼロ コード ユニットが追加されます)が、これらのコード ユニットが文字列リテラルから変換されたものであることを知ることは不可能であるため、そのようなソース コードは文字列リテラルではありません。[ 3 ]
char各文字列は、適切な種類のゼロ コード ユニット (または)の最初の出現で終了しますwchar_t。したがって、バイト ストリング ( char*)には、 ASCIIまたは任意のASCII 拡張の非NUL文字を含めることができますが、 UTF-16などのエンコーディングの文字を含めることはできません(16 ビット コード ユニットがゼロでない場合でも、その上位または下位バイトがゼロになる可能性があります)。ワイド ストリングに格納できるエンコーディングは、 の幅によって定義されます。ほとんどの実装では、は少なくとも 16 ビットであるため、UCS-2などのすべての 16 ビット エンコーディングを格納できます。 が32 ビットの場合、UTF-32などの 32 ビット エンコーディングを格納できます。(標準では「任意のワイド文字を保持する型」が要求されていますが、Windows では UCS-2 から UTF-16 への移行以降、これはもはや当てはまりません。これは標準の欠陥として認識され、C++ で修正されました。) [ 4 ] C++11 およびC11では、明示的な幅 と を持つ 2 つの型が追加されました。[ 5 ]wchar_twchar_twchar_tchar16_tchar32_t
可変長エンコーディングは、バイト文字列とワイド文字列の両方で使用できます。文字列の長さとオフセットは、バイトまたはバイト単位で測定されwchar_t、「文字」単位ではないため、初心者プログラマーにとっては混乱を招く可能性があります。UTF -8とShift JISは C バイト文字列でよく使用され、UTF-16は 16 ビットの場合に C ワイド文字列でよく使用されますwchar_t。可変長文字を含む文字列を などの関数で切り捨てると、strncpy文字列の末尾に無効なシーケンスが生成される可能性があります。切り捨てられた部分が入力が有効であると想定するコードによって解釈される場合、これは安全ではありません。
(UTF-8) や(UTF-16 または UTF-32、 に依存します)などの Unicode リテラルのサポートは実装依存であり、[ 6 ]ソース コードが同じエンコーディングである必要がある場合があります。特に、コンパイラが引用符の間にあるものをそのままコピーする可能性がある の場合です。一部のコンパイラまたはエディタでは、UTF-8 の各バイト、および/または UTF-16 の各ワードに対して、すべての非 ASCII 文字をシーケンスとして入力する必要があります。C11 (および C++11) 以降、のようにバイト文字列リテラルに対して UTF-8 を保証する新しいリテラル接頭辞が使用可能になりました。[ 7 ] C++20およびC23以降、UTF-8 文字を格納することを目的とした型が追加され、u8 接頭辞の付いた文字リテラルと文字列リテラルの型がそれぞれ と に変更されました。charfoo[512]="φωωβαρ";wchar_tfoo[512]=L"φωωβαρ";wchar_tchar\xNN\uNNNNu8charfoo[512]=u8"φωωβαρ";char8_tchar8_tchar8_t[]
過去の文書では、C言語の文字列を表す際に「バイト」の代わりに「文字」という用語がよく使われていたため、これらの関数がUTF-8では正しく動作しないと考える人が多くいました。しかし実際には、すべての長さはバイト単位で定義されており、これはすべての実装で共通です。これらの関数は、UTF-8でもシングルバイトエンコーディングでも同様に動作します。BSDのドキュメントはこの点を明確にするために修正されましたが、POSIX、Linux、Windowsのドキュメントでは、依然として「バイト」または「wchar_t」が正しい箇所で「文字」という用語が使われています。
メモリ バッファを処理する関数は、データの一部としてヌル バイトを含むバイト シーケンスを処理できます。これらの関数の名前は通常、プレフィックスmemとは対照的に、で始まりますstr。
C言語の文字列を操作する関数のほとんどはstring.hヘッダーファイル(cstringC++では)に宣言されていますが、C言語のワイド文字列を操作する関数はwchar.hヘッダーファイル(cwcharC++では)に宣言されています。これらのヘッダーファイルには、メモリバッファの処理に使用される関数の宣言も含まれているため、この名前はやや不適切です。
で宣言された関数は、 C 標準ライブラリstring.hの一部として、C をサポートするあらゆるプラットフォームで動作することが保証されているため、非常に人気があります。しかし、これらの関数には、注意深く適切に使用しないとバッファオーバーフローが発生する可能性があるなど、いくつかのセキュリティ上の問題が存在するため、プログラマはより安全で移植性が低い可能性のあるバリアントを好みます。以下に、その中でも人気のあるものをいくつか示します。これらの関数の中には、文字列ポインタを受け取り、文字列内の非ポインタを返すことでconst の正しさに違反するものもあります。これを修正するために、標準ライブラリの C++ バージョンでは、一部の関数が 2 つのオーバーロードされた関数に分割されています。constconst
これらの機能はすべてmbstate_tオブジェクトは、元々は静的メモリにあり(関数がスレッドセーフではない)、後の追加では呼び出し元が維持する必要があります。これは元々、シフト状態を追跡することを目的としていました。mbエンコーディングですが、UTF-8 などの最新のエンコーディングではこれは必要ありません。ただし、これらの関数は、トイレエンコーディングは可変幅エンコーディングではないため、正確に1つのwchar_t一度に1つずつ、文字列ポインタを使用するのではなく値渡しで渡します。UTF-16は可変長エンコーディングであるため、mbstate_tワイドエンコーディングでサロゲートペアを追跡するために再利用されていますが、呼び出し元は依然としてそれを検出して呼び出す必要がありますmbtowc1文字につき2回。[ 80 ] [ 81 ] [ 82 ]標準への後の追加では、プログラマーが関心を持つ唯一の変換はUTF-8とUTF-16の間であると認め、これを直接提供する。
C標準ライブラリには、数値変換のための関数がいくつか含まれています。バイト文字列を扱う関数はstdlib.hヘッダーファイル(cstdlibC++ではヘッダーファイル)に定義されています。ワイド文字列を扱う関数はwchar.hヘッダーファイル(cwcharC++ではヘッダーファイル)に定義されています。
関数strchr、bsearch、strpbrk、 、strrchr、strstrおよびmemchrそれらのワイド版は、文字列ポインタを受け取り、文字列内の非ポインタを返すため、 const 正しくありません。これはC23で修正されました。[ 95 ]constconst
また、規範的修正1(C95)以降、atoxx関数は関数に包含されると考えられておりstrtoxxx、そのためC95もそれ以降の標準もこれらの関数のワイド文字バージョンを提供していません。反対の論拠atoxxは、それらがエラーとを区別していないことです0。[ 96 ]
BSD、SVID、POSIXなど、いくつかの関連規格は、標準Cが提供する文字列処理機能を拡張しています。また、より新しいC規格( C11やC23など)にも、こうした機能が追加されています。
[ 22 ]および[ 18 ]をバッファオーバーフローを許容しない関数に置き換える必要性が確立されているにもかかわらず、受け入れられた標準は確立されていません。これは、多くの C プログラマーが とが望ましい動作をすると誤解していることが一因です。しかし、どちらの関数もこの目的で設計されたものではありません (これらは、現代のソフトウェアではあまり使用されないデータ形式である、ヌルで埋められた固定サイズの文字列バッファを操作することを目的としていました)。また、動作と引数は直感的ではなく、熟練したプログラマーでさえ誤って記述することがよくあります。[ 108 ]strcatstrcpystrncatstrncpy
最もよく使われる[ a ]代替関数は、1998 年 12 月にOpenBSD 2.4に登場したstrlcat[ 111 ]およびstrlcpy[ 112 ]関数です。 [ 108 ]これらの関数は常に宛先バッファに 1 つの NUL を書き込み、必要に応じて結果を切り捨て、必要なバッファのサイズを返します。これにより、切り捨てを検出でき、切り捨てられない新しいバッファを作成するためのサイズが提供されます。長い間、これらの関数は、効率が悪いという理由で[ 113 ] 、(より優れた代替形式の文字列の代わりに) C文字列の使用を推奨しているという理由で[ 114 ] [ 115 ]、その他の潜在的なエラーを隠蔽しているという理由で、GNU C ライブラリ (Linux 上のソフトウェアで使用) には含まれていませんでした。[ 116 ] [ 117 ] glibc がサポートを追加していなかった間にも、strlcat と strlcpy は OpenBSD、FreeBSD、NetBSD、Solaris、OS X、QNX用の C ライブラリや、2008 年に導入されたlibbsd [ 118 ]や2011 年に導入されたmusl [ 119 ] [ 120 ]などの Linux 用の代替 C ライブラリを含む多くの C ライブラリで実装され、ソース コードがSDL、GLib、ffmpeg、rsyncなどの他のプロジェクトに直接追加され、 Linux カーネル内部でも使用されています。これは 2024 年に変更され、glibc FAQでは、glibc 2.38 の時点でコードがコミットされ[ 121 ]、追加されたと記載されています。[ 122 ]これらの機能は POSIX.1-2024 の一部として標準化されました。[ 123 ] Austin Group Defect Tracker ID 986 は、 POSIX のそのような計画についての議論を追跡しました。
マイクロソフトは、2004 年のセキュリティ開発ライフサイクルの一環として、strcpy_sおよびstrcat_s(その他多数)を含む「セキュア」関数のファミリーを導入しました。 [ 124 ]これらの関数は、ISO/IEC WDTR 24731 で提案されたオプションのC11 (Annex K)の一部として、若干の変更を加えて標準化されました。 [ 125 ]これらの関数は、文字列がバッファに収まるには長すぎるかどうかなど、さまざまなチェックを実行します。チェックが失敗した場合、ユーザー指定の「ランタイム制約ハンドラ」関数が呼び出され、[ 126 ]通常、プログラムは中止されます。[ 127 ] [ 128 ]これらの関数は、当初は Windows でのみ実装され、同時にMicrosoft Visual C++が標準関数の代わりにこれらの関数を使用することを推奨する警告メッセージを生成し始めたため、かなりの批判を集めました。これは、マイクロソフトが開発者を自社のプラットフォームに囲い込もうとする試みであると推測する人もいます。[ 129 ]これらの関数の使用経験から、その採用と使用上のエラーに重大な問題があることが判明したため、C 標準の次の改訂で附属書 K の削除が提案されました。[ 130 ]memset_s不要なコンパイラ最適化を回避する方法として、の使用が提案されています。 [ 131 ] [ 132 ]
strlcpy、 の使用回数は 38,644 回strcpy_s(および の使用回数は 15,286,150 回strcpy)です。{{cite web}}: CS1メンテナンス: 場所 (リンク){{cite web}}: CS1メンテナンス: 場所 (リンク){{cite web}}: CS1メンテナンス: 場所 (リンク){{cite web}}: CS1メンテナンス: 場所 (リンク)この [strlcpy および strlcat] API は、ほとんどの最新のオペレーティングシステムと多くのスタンドアロン ソフトウェア パッケージに採用されています [...]。注目すべき例外は GNU 標準 C ライブラリ glibc で、そのメンテナーは、これらの改良された API を「ひどく非効率的な BSD のガラクタ」とレッテルを貼って、頑なに採用を拒否しています。これは、ほとんどの場合、置き換えられる API よりも高速であるという以前の証拠があるにもかかわらずです。
正しい文字列処理とは、文字列の長さを常に把握し、(strcpy の代わりに) memcpy を使用できることを意味します。
{{cite web}}: CS1メンテナンス: 場所 (リンク){{cite web}}: CS1メンテナンス: 場所 (リンク)