あなたはchar型を使ってますか?
char型は文字・文字列や画像を256階調で表す場合によく使われます。特に文字列は配列やポインタで扱うので複雑に感じるかもしれません。
この記事では、char型について「文字(char)の使い方」「文字列(char配列)の使い方」という基本的な内容から、「文字列を扱う関数」など応用的な使い方を、わかりやすく解説していきます。
文字(char)の使い方
charとは1文字を格納するデータ型です。char型の変数に値を代入する際には代入する文字を「’ ’」(クオーテーションマーク)で囲みます。
char型変数の宣言と初期化例:
char 変数名 = ‘文字’;
printf関数などを使って出力表示する際の変換指定子は「c」になります。char型には符号付きの「singned char」型と符号なしの「unsigned char」型があります。
charの前に何も記述しない場合は、基本的に「singned char」になります。※環境によってはunsigned charとなるケースもあります。char型は文字のほかにも数値を格納することができます。文字を代入した変数をint型の変換指定子「d」で出力表示すると、対応するASCIIコードの数値が表示されます。
「singned char」型の変数は-128~127の値をとります。
「unsigned char」型の変数は0~255の値をとります。
これらの範囲以外の値を代入すると意図しない値で扱われることになるので注意しましょう。char型の変数のサイズは1バイトです。ちなみに日本語の全角文字は2バイト以上の文字となりますので、char型の変数には格納できません。
それではサンプルコードで確認していきましょう。このコードではchar型の値の範囲を確認するために、ヘッダーファイル「limits.h」をインクルードしています。
#include <stdio.h> #include <limits.h> int main(void) { char chr1 = 'a'; // 宣言と初期化 // char型の変換指定子はc printf("char1の文字は: %c\n", chr1); // char型の文字コード表示 printf("char1の文字コードは: %d\n", chr1); // 文字コードの表示 for(char c = 'a'; c < 'd'; c++) { printf("%c: %d\n", c, c); } /* 日本語の全角文字は2バイト以上の文字でchar型とはならない * warning: multi-character character constant char chr2 = 'あ'; */ // (signed)char型、unsigned char型のサイズおよび最小値、最大値 printf("char型のバイト数は%ldバイト\n", sizeof(chr1)); printf("signed charの最小値: %d\n", CHAR_MIN); printf("signed charの最大値: %d\n", CHAR_MAX); printf("unsigned charの最小値: %d\n", 0); printf("unsigned charの最大値: %d\n", UCHAR_MAX); return 0; }
実行結果:
char1の文字は: a char1の文字コードは: 97 a: 97 b: 98 c: 99 char型のバイト数は1バイト signed charの最小値: -128 signed charの最大値: 127 unsigned charの最小値: 0 unsigned charの最大値: 255
このサンプルコードではchar型の変数「chr1」を宣言初期化し、変換指定子「c」を使って出力表示しています。またint型の変換指定子「d」を使ってASCIIコードでも出力表示しています。
文字「a」~「c」のASCIIコードをfor文を使って表示しています。またヘッダーファイル「limits.h」で定義されているchar型のサイズや値の範囲を表示して確認しています。
文字列(char配列)の使い方
char型の配列は文字列として扱うことができます。数値の配列と同じように配列名のあとに「[ ]」記号を使って文字列サイズを指定し宣言する方法と、ポインタを使って宣言する方法があります。
それぞれの方法について確認していきましょう。
基本的な使い方
数値の配列と同じように配列名のあとに「[ ]」記号を使って文字列サイズを指定し宣言する方法について説明します。
配列を使った文字列の宣言:
char 配列名[文字列サイズ];
宣言と同時に初期化も行う場合は代入する文字列を「” ”」(ダブルクオーテーションマーク)で囲み、下記のように記述します。
宣言と初期化:
char 配列名[文字列サイズ] = “文字列”;
この場合、文字列サイズの記述を省略することができます。省略した場合、文字列の長さに1加えた値でサイズが決まります。
これは文字列の最後の文字のあとに終端文字「\0」(NULL)を入れる決まりになっているからです。配列の初期化と同じように1文字ずつ要素を「,」カンマで区切り、全要素を「{ }」記号で囲む下記のような記述も可能です。
宣言と初期化:
char 配列名[文字列サイズ] = {‘文字[0]’, ‘文字[1]’, ・・・};
この場合も、文字列サイズの記述を省略することができます。省略した場合同じように文字列の長さに1加えた値でサイズが決まり、文字列の最後の文字のあとに終端文字「\0」(NULL)が入ります。printf関数などを使って出力表示する際の変換指定子は「s」になります。
それではサンプルコードで確認していきましょう。
#include <stdio.h> int main(void) { char str1[] = "Hello"; // 宣言と初期化 // 文字列の変換指定子はs printf("str1の文字列は: %s\n", str1); // 1文字ずつ表示 for(int i = 0; i < sizeof(str1); i++) { printf("%d番目の文字は%c\n", i + 1, str1[i]); } char str2[] = {'H', 'e', 'l', 'l', 'o', '\0'}; printf("str2の文字列は: %s\n", str2); // 1文字ずつ表示 for(int i = 0; i < sizeof(str2); i++) { printf("%d番目の文字は%c\n", i + 1, str2[i]); } return 0; }
実行結果:
str1の文字列は: Hello 1番目の文字はH 2番目の文字はe 3番目の文字はl 4番目の文字はl 5番目の文字はo 6番目の文字は str2の文字列は: Hello 1番目の文字はH 2番目の文字はe 3番目の文字はl 4番目の文字はl 5番目の文字はo 6番目の文字は
このサンプルコードでは文字列「str1」を宣言、初期化しています。この文字列「str1」の文字列サイズをsizeof関数を使って計算し、for文を使って1文字ずつ表示しています。
出力表示を確認すると5文字の文字列「Hello」に対して「6番目の文字は」と表示されています。また6番目の文字は表示されていません。これは文字列の最後の文字「o」のあとに終端文字「\0」(NULL)が入っているためです。
文字列を「” ”」(ダブルクオーテーションマーク)で囲んで初期化した場合も、「{ }」記号で囲んで初期化した場合も、最後の文字のあとに終端文字「\0」(NULL)が入っていることが確認できます。
ポインタの使い方
char型のポインタを使って宣言する方法について説明します。
ポインタを使った宣言の例:
char *(ポインタ名) = “文字列”;
この場合も、文字列の最後の文字のあとに終端文字「\0」(NULL)が入ります。printf関数などを使って出力表示する際の変換指定子は同じように「s」になります。
それではサンプルコードで確認していきましょう。
#include <stdio.h> int main(void) { char *ptr = "Hello"; // char型のポインタの宣言と初期化 // 文字列の変換指定子はs printf("ptrの文字列は: %s\n", ptr); // 1文字ずつ表示 (あとで説明しますが、この部分はよくない例です) for(int i = 0; i < sizeof(ptr); i++) { printf("%d番目の文字は%c\n", i + 1, ptr[i]); } return 0; }
実行結果:
ptrの文字列は: Hello 1番目の文字はH 2番目の文字はe 3番目の文字はl 4番目の文字はl 5番目の文字はo 6番目の文字は 7番目の文字はp 8番目の文字はt
このサンプルコードではchar型のポインタ「ptr」を宣言、初期化しています。ポインタ「ptr」の文字列サイズをsizeof関数を使って計算し、for文を使って1文字ずつ表示しています。しかし実行結果の表示がおかしいですね……。
実は、ポインタをsizeofに渡した場合、あくまでそのポインタ型のサイズしか取得することができないのです。配列のサイズを取得できる訳ではありません。
したがってfor文を使って1文字ずつ表示するには、別の方法で文字列サイズを算出するのが適した方法になりますので注意しましょう。別の方法については後述します!
文字列を扱う関数について
C言語にはstrlen、strcpy、strncpy、strcat、strcmpなどの文字列を扱う関数が標準ライブラリに用意されています。これらの関数を使う際にはヘッダーファイル「string.h」をインクルードする必要があります。
これらの関数の使い方をみていきましょう!
strlenで文字列長さを取得する方法
strlen関数は第1引数に記述した文字列の文字列長さを取得します。取得する値は終端文字を除いた文字列長さになります。
それではサンプルコードで確認していきましょう。
#include <stdio.h> #include <string.h> int main(void) { char *ptr; // char型のポインタの宣言 ptr = "Hello"; // ポインタの初期化 // 文字列の変換指定子はs printf("ptrの文字列は: %s\n", ptr); // 1文字ずつ表示 for(int i = 0; i < sizeof(ptr); i++) { printf("%d番目の文字は%c\n", i + 1, ptr[i]); } // 文字列長さの分だけ繰り返す for(int i = 0; i < strlen(ptr); i++) { printf("%d番目の文字は%c\n", i + 1, ptr[i]); } return 0; }
実行結果:
ptrの文字列は: Hello 1番目の文字はH 2番目の文字はe 3番目の文字はl 4番目の文字はl 5番目の文字はo 6番目の文字は 7番目の文字はp 8番目の文字はt 1番目の文字はH 2番目の文字はe 3番目の文字はl 4番目の文字はl 5番目の文字はo
このサンプルコードでは前述のポインタを使った方法のコードに、修正を追記しています。strlen関数を使って終端文字を除いた文字列長さの分だけfor文で繰り返しています。
strcpy、strncpyで文字列をコピーする方法
文字列は数値の配列と同じように「=」記号で代入してコピーすることができません。文字列をコピーしたい場合はstrcpyもしくはstrncpy関数を使用しますstrncpy関数はコピー元の文字列の先頭から文字数を指定してコピーすることができます。
それではサンプルコードで確認していきましょう。
#include <stdio.h> #include <string.h> int main(void) { char str1[16] = "Hello World!"; char str2[16], str3[16]; /* 配列の代入は不可 str2 = str1; */ // 文字列のコピー strcpy(str2, str1); printf("str2の文字列は: %s\n", str2); // 指定文字数分の文字列のコピー strncpy(str3, str1, 5); printf("str3の文字列は: %s\n", str3); return 0; }
実行結果:
str2の文字列は: Hello World! str3の文字列は: Hello
このサンプルコードでは文字列「str1」の値を文字列「str2」、「str3」にコピーしています。文字列「str2」には「str1」と同じ文字列をコピーしています。
文字列「str3」には「str1」の先頭から5文字をコピーしています。
strcatで文字列を連結する方法
strcat関数を使って文字列を連結します。strcat関数の第1引数の文字列の後に第2引数の文字列をつなげてポインタで返します。
それではサンプルコードで確認していきましょう。
#include <stdio.h> #include <string.h> int main(void) { char str1[8] = "Hello "; char str2[8] = "World!"; char *str3; // 文字列の連結 str3 = strcat(str1, str2); printf("str3の文字列は: %s\n", str3); return 0; }
実行結果:
str3の文字列は: Hello World!
このサンプルコードでは文字列「str1」のあとに「str2」をつなげてポインタ「str3」に返しています。
strcmpで文字列を比較する方法
strcmp関数を使って文字列同士が同じか否か比較します。数値同士の比較は「==」記号を使いますが、文字列(Char型配列)で「==」記号を使うとアドレスが同じか否かの比較となるので注意しましょう。(またこの際、動作には影響しませんが、環境によっては警告が出る場合もあります)
それではサンプルコードで確認していきましょう。
#include <stdio.h> #include <string.h> int main(void) { char str1[16] = "Hello World!"; char str2[16] = "Hello World!"; char str3[16] = "Hello"; // アドレスの比較 if(str1 == str2) { printf("%s と %s は同じ文字列です\n", str1, str2); } else { printf("%s と %s は違う文字列です\n", str1, str2); } // 文字列の比較 if(strcmp(str1, str2) == 0) { printf("%s と %s は同じ文字列です\n", str1, str2); } else { printf("%s と %s は違う文字列です\n", str1, str2); } if(strcmp(str1, str3) == 0) { printf("%s と %s は同じ文字列です\n", str1, str3); } else { printf("%s と %s は違う文字列です\n", str1, str3); } return 0; }
実行結果:
Hello World! と Hello World! は違う文字列です Hello World! と Hello World! は同じ文字列です Hello World! と Hello は違う文字列です
このサンプルコードでは文字列「str1」と「str2」、「str3」を比較しています。「==」記号を使うと、同じ「Hello World!」の文字ですがfalseと判定されています。これはアドレスを比較してアドレス先が違うためです。
strcmp関数を使うと文字が同じか否か判定できていることがわかります。
まとめ
ここでは、char型について説明しました。char型の配列とポインタを使って文字列を扱うことができます。
string.hの関数群とあわせて使いこなすことができるように、この記事を何度も参考にして下さいね!