char - 文字を表現する型

charは、文字を表現する型です。char型は、ASCII文字コード(0~127)を表現することが想定されていると考えられます。char型は、8bit符号あり整数型か、8bit符号なし整数型として定義されています。

# 文字を表現する型
char

charのサンプルコード

charを使ったサンプルコードです。

#include <stdio.h>
#include <stdint.h>

int main(void) {
  char ch = 'a';
  
  printf("%c\n", ch);
}

printf関数のフォーマット指定子で「%c」を使っています。

出力結果。

a

なぜchar型は、符号ありか符号なしかがはっきりしていないのですか?

歴史的な経緯で、charを8bit符号あり整数として実装した処理系と8bit符号なし整数として実装した処理系があったからだと考えられます。そのためC言語仕様では、charは8bit符号あり整数または、8bit符号なし整数として定義されています。

幅が8bitであることは保証されていますか?

はい、幅が8bitであることは保証されています。

8bitの数値を扱うときは、int8_t, uint8_tを使う

文字ではなく、8bitの数値を表現するときは、符号あり、符号なしが、定義されているint8_t, uint8_tを使いましょう。

charではなく、int8_t, uint8_tを全部の箇所で使ったらいいのでは?

理想的には、そうです。ただし、歴史的な経緯からC言語では文字や文字列を受け取る関数の引数の型は「char」や「char*」です。

文字や文字列を関数の引数に渡すことを考えると、文字の表現には「char」、文字列の表現には「char*」を使っておくと、型キャストがいらないという点で、簡潔でわかりやすくなります。

また、文字の場合は、符号ありと符号なしで区別ができないことで困るということがあまりありません。もし困った場合は、その場合だけ、int8_tあるいはuint8_tに型キャストできます。

すべてを置き換えるというよりも、広く普及している記述を尊重しつつ、必要性が高い部分で、新しく便利な機能を使っていくのは、過去と現在と未来を調和させる非常に良い方法の一つだと考えています。

C言語でUnicodeを表現することはできますか?

まず前提として「コードポイントであるUnicode」と「実際の符号化であるUTF-8やUTF-16やUTF-32」を区別できていることを前提とします。

Unicodeコードポイント

Unicode(デファクトスタンダードであるUCS-4)は、最大31bitで、一つの文字を表現します。つまり、包括的に表現しようと思うと、4バイト必要だということです。つまり、int32_t型が必要です。一番大きなビットの32bit目は、利用されないことが仕様上保証されているので、uint32_t型を使う必要はありません。

またC11で追加されたchar32_tを使うこともできます。

UTF-8

UTF-8は、Unicodeコードポイントを1~4バイトの可変長で符号化する符号化形式です。UTF-8は、単なるバイト列で、エンディアンを意識する必要がないです。つまり、文字あるいは文字列はcharの配列として表現できます。

// C言語におけるUTF-8の表現(ソースコードはUTF-8で保存)
const char* ch = "あ";
const char* string = "あいうえお";

UTF-8はprintf関数で、普通に出力できます。

printf("%s\n", string);

UTF-32

UTF-32は、これが符号化であるという点を除いて、Unicodeコードポイントとまったく同じものです。Unicodeコードポイントの解説を読んでください。

UTF-32は、エンディアンを意識する必要があります。

UTF-16

UTF-16は、Unicodeを一文字を2バイトで表現する符号化形式です。ただし、Unicodeで表現する必要のある文字は、2バイトで表現できる文字の集合を超えています。その場合は、サロゲートペアと呼ばれる方法で、4バイトを使って表現します。

通常は2バイトなので、C言語では、一つの文字はuint16_tで表現できます、4バイトになる場合は、uint16_tふたつで表現できます。

また、C11で追加された、char16_tを使うこともできます。

UTF-16は、エンディアンを意識する必要があります。

関連情報