2025-10-08 21:43:12
諏訪子
lowlevel
c

【C言語】何故C言語には文字列型がないのか?

多くの高レベルプログラマーにとって、文字列型は必須の変数型です。
C++にはstd::stringがあり、Go言語、PHP、C#、Java、Javascript等多くの言語にstringがあり、Rustには何らかの理由で様々な文字列型があります。
然し、C言語とZigは文字列型が全くない数少ない言語として際立っています。
何故でしょうか?

文字列とは何か?

文字列とは何かを理解するには、先ず文字とは何かを説明する必要があります。
文字は、単なる8ビットの数値に過ぎません。
ASCII文字は00から7Fまでの7ビット(7Fは2進数で0111 1111)ですが、8ビットとして保存されます。
SHIFT-JISは可変長エンコーディングで、1バイト文字はASCIIと互換性があり、2バイト文字は特定の範囲を使用します。

C言語では、文字列は基本的にchar値の配列で、ヌル文字(\0)で終端されます。
配列はメモリ内で固定サイズですが、文字列の論理的な長さ(ヌル終端まで)は変化します。
C言語の文字列は固定サイズのメモリ(静的または動的)に保存されます。
文字列のサイズを変更するには、ポインタと手動のメモリ割り当て(mallocrealloc等)を使用する必要があります。
此れは、高レベル言語が自動的に処理するのとは異なります。
Zigも文字列に関しては同じ哲学に従います。

最近よく書いているアセンブリ言語では、文字はdb(Define Byte)ディレクティブを使用して保存され、8ビットの値が割り当てられます。
此れらのバイトはエンコーディングに基づいて文字として解釈されます。
先程述べた様に、バイトは8ビットで、Cのcharも8ビットです。
Zigはu8u16u32i8i16i32等の変数で此れを簡略化します。

従って、選択した高レベル言語でstring foo = "Hello World";とすると、C言語では基本的にchar foo[12] = "Hello World";、Zigではconst foo: [12]u8 = "Hello World";となります。
何故長さが11ではなく12なのかと疑問に思うかもしん。
此れは、ヌル終端文字のためのスペースを確保する為です。
C言語、Zig、又はアセンブリ言語のプログラマーは此れを手動で考慮する必要があります。
詰り、"Hello World"という文字列は実際には['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '\0']です。
ヌル終端文字を含めないと、割り当てられたメモリを越えて読み込む、又は端末にゴミデータを出力する等の未定義動作を引き起こす可能性があります。
const char *foo = "Hello World";を使用して、読み取り専用メモリに保存された文字列リテラルを指す事が出来ます。
動的な文字列の場合は、char *foo = malloc(12);の様にメモリを割り当て、使用しなくなったらメモリリークを避ける為に手動で解放する必要があります。
メモリを解放しないと、先に述べた問題が発生します。
C++はC++11以降、スマートポインタや安全なコンテナなどの機能を導入し、メモリ安全性を向上させていますが、此れは低レベルプログラミングを可能にする為に依然としてオプションです。

何故手動でメモリを管理するのか?未来はメモリ安全ではないのか?

其れはRustの熱心な支持者が信じさせたい事です。
実際には、此れは取り組んでいるソフトウェアの種類によります。

確かに、殆どの分野でメモリ安全な言語を使用する事は賢明な選択ですが、ゲーム開発、組み込みシステム、レガシーシステムなど、メモリの1ビット1ビットが重要な分野では理想的ではありません。
メモリ安全は依然として抽象化であり、抽象化は基盤となる技術を気にしなくてもよい様にする為の物ですが、制御フローの一部を失い、パフォーマンスと複雑さのコストがかかります。
ゲーム開発者は、フレームレートを高くし、印象的なグラフィックスや優れた音楽を実現する為に、ハードウェアから最大限のパフォーマンスを引き出す必要があります。
組み込みハードウェアは非常に制約された環境で、極端な場合には256バイトのRAMと単一コア740kHzのCPUで動作するハードウェアを扱う事もあります。
組み込みシステムでは、全てのビットとバイトが重要なので、メモリ安全は選択肢になりません。

Linux、FreeBSD、Android、Windowsはすべてカーネルに益々多くのRustコードを追加しており、Rustで作られた完全なOSが動作する事が証明されていますが、長期的には其れが利益になるとは思いません。
カーネルはコンピュータのオペレーティングシステムの最も重要なコンポーネントであり、メモリの完全な制御が依然として不可欠です。

一方、ユーザーランドソフトウェア、スマホアプリケーション、サーバーデーモン、ウェブバックエンド等では、メモリ制御は其れ程重要ではないので、Rust、Zig、C++11以降、又は他のメモリ安全な言語を使用したい場合は、是非使って下さい。

勿論、Bashでウェブサーバーを作ったり、Excelでジェットコースターを作ったりする事も出来ますし、こうした奇妙なアイデアは素晴らしいと思いますが、本番環境でやるのは良いアイデアとは言えません。
だからこそ、ApacheやNginxはCで書かれ、ローラーコースタータイクーンはアセンブリ言語で書かれています。
だから、メモリ安全の要求があるにも関わらず、C言語、C++99、アセンブリ言語で書いているのです。

結論

結局の所、C言語、Zig、アセンブリ言語に文字列型がない理由は、単にそれが必要ないからです。
高レベル言語(C++11以上、Rust、Javascript、C#等)に慣れていると、これは混乱するかもしん。
然し、コンピュータが文字列をどの様に扱うかを理解すれば、何故そうなのかがわかり、文字列型がない事を寧ろ高く評価する様になるでしょう。

以上