ユーニックス総合研究所

  • home
  • archives
  • c-char-array

C言語のchar型の配列(文字列)の使い方

  • 作成日: 2021-09-17
  • 更新日: 2023-12-24
  • カテゴリ: C言語

C言語のchar型の配列の詳しい使い方

C言語ではchar型の配列を定義することができます。
これは文字列として使うことができる配列です。

この記事ではC言語のchar型の配列について詳しく解説します。
具体的には↓を見ていきます。

  • char型の配列のおおざっぱな使い方
  • charとは?
  • 配列とは?
  • char型の配列の定義方法
  • char型の配列の参照方法
  • char型の配列の出力方法
  • char型の配列をポインタ変数に代入する
  • 普通の文字列(const char *)との違い

char型の配列のおおざっぱな使い方

最初に結論としてchar型の配列のおおざっぱな使い方を見てみたいと思います。
char型の配列sを定義するには↓のようにします。

#include <stdio.h>  

int main(void) {  
    char s[] = "Hello, World!";  
    printf("%s\n", s);  
    return 0;  
}  

↑のchar型の配列sには文字列Hello, World!が代入されます。
そしてsprintf()で出力するには%sを指定します。
この%sは文字列を出力する場合の指定ですが、このようにchar型の配列は文字列として出力することができます。

charとは?

charとはC言語のデータ型の一種です。
おもに文字列やバイト列を定義するのに使われます。

charのバイト数

charのデータバイト数は1バイトです。
このためASCII文字列やバイト列を扱うのに向いているデータ型と言えます。
ただしユニコードなどの文字列を扱う場合は、データサイズが小さいので不向きと言えます。
その場合はwchar_tなどを使うのが一般的です。

charの読み方

charの読み方は↓の2通りあります。

  • チャー
  • キャラ

ネット上ではどちらの読み方も存在しており混在している状態です。
「チャー」派の人はcharをそのまま読んだ場合は「チャー」になるからこっちの方が正しい、という自負を持っています。
いっぽう「キャラ」派の人はcharcharacter(キャラクター、文字)の略だからこっちのほうが正しい、という自負を持っています。

🦝 < 読み方の宗派が分かれているのね

🐭 < みなさんはどっち派?

charはどんな時に使うか?

charはどんなときに使うのでしょうか?
繰り返しになりますがcharは文字列やバイト列の定義で使われます。
特に画像処理やソケット通信のパケットなどでは馴染みの深いデータ型です。
また文字列を定義して参照することができるので、C言語のチュートリアルではわりと最初の方で解説されます。

配列とは?

配列とはC言語のデータ型の1つで、連続するデータ領域を持った構造のことを言います。
これは型に[]を付けることで定義することが出来ます。

配列はC言語のデータ型の1つ

C言語には関数型などの色々な型が存在しますが、配列もその型の一種です。
ポインタで定義した文字列と配列で定義した文字列は明確に区別されて扱われるので注意が必要です。

連続するデータ領域

配列のデータ領域は連続しています。
そのためインデックスでアクセスすることが可能です。
たとえば↓のようにです。

    char s[] = "good";  
    putchar(s[0]);  // g  
    putchar(s[1]);  // o  

配列はどんな時に使うか?

C言語では配列は色々なシーンで使われます。
char型の配列を定義する場合は、主に定義した変数を変更可能な文字列として扱いたい場合に使われます。

char型の配列の定義方法

C言語のchar型の配列の定義方法を見てみます。
このchar型の配列の定義方法は複数あり、主に↓の3つがあります。

  • 要素数を省略する定義
  • 要素数を省略しない定義
  • 文字列で定義

要素数を省略する定義

配列の要素数を省略する定義方法は↓のように行います。

    char s1[] = { 'g', 'o', 'o', 'd', 0 };  

↑の場合、s1は配列ですが、要素数はg, o, o, d, 0の合わせて5つになります。
このように配列を定義する場合は、要素数を省略することが出来ます。
その場合、要素数は初期化子リスト({}で囲まれた部分)の内容で決定されます。

要素数を省略しない定義

配列の要素数を省略しない場合は↓のように書くことが出来ます。

    char s2[10] = { 'g', 'o', 'o', 'd', 0 };  

↑の場合、s2は要素数10を持つ配列です。
[]の中の要素数が初期化子リストの要素数より少ない場合は、コンパイラによっては警告が出力されます。
つまり

🐭 < おいおい、要素数が足りないぜ?

ということです。
GCCでは↓のような警告が出力されます。

define1.c:5:30: warning: excess elements in array initializer  
    5 |     char s2[2] = { 'g', 'o', 'o', 'd', 0 };  

文字列で定義

char型の配列は文字列で定義することも可能です。
↓のようにです。

    char s3[] = "good";  

この場合、s3にはg, o, o, o, \0の5要素が格納されます。
文字列の場合は暗黙的にナル文字が入っていることに注意が必要です。

初期化リストで定義する場合の注意点

char型の配列を初期化子リストで初期化する場合、その変数を文字列として扱うなら注意が必要です。
初期化子リストは暗黙的にナル文字を格納しません。
そのため、文字列として扱いたい場合は明示的にナル文字を入れておく必要があります。
↓の初期化子リストの末尾の0がナル文字です。

    char s1[] = { 'g', 'o', 'o', 'd', 0 };  
    char s2[10] = { 'g', 'o', 'o', 'd', 0 };  

char型の配列の参照方法

C言語のchar型の配列の参照方法を解説します。
char型の配列の要素を参照するには?
char型の配列の要素に代入するには?

要素の参照方法

char型の配列の要素はインデックスで参照可能です。

    char s[] = "good";  
    printf("%c\n", s[0]);  // g  
    printf("%c\n", s[1]);  // o  

↑の場合、変数sは配列でgoodという文字列で初期化されています。
この変数sをインデックス0で参照するとgと表示され、1で参照するとoが出力されます。

範囲外のインデックスアクセスは、セグフォになる場合もあるしならない場合もあります。
筆者の環境では何もエラーになりませんでした。

🦝 < セグフォになってほしかった

要素への代入方法

char型の配列の要素に値を上書きして代入するには↓のようなコードを書きます。

    char s[] = "good";  

    s[0] = 'f';  
    printf("%s\n", s);  // food  

↑の場合、sには文字列foodが入ってますが、その第1要素をfに変更してfoodにしています。

char型の配列の出力方法

C言語のchar型の配列の出力方法はおもに↓の3つです。

  • printf()の%sで文字列として出力
  • printf()の%cで文字を出力
  • printf()の%dで整数として出力

printf()の%sで文字列として出力

printf()の書式指定子%sを使うと、char型の配列を文字列として出力できます。

    char s[] = "good";  
    printf("%s\n", s);  // good  

ただ注意点として、この場合は配列sはナル終端されている必要があります。
ナル文字が無い場合セグフォなどになる場合があります。

printf()の%cで文字を出力

printf()の書式指定子%cを使うと、配列の要素を文字として出力できます。

    char s[] = "good";  
    printf("%c\n", s[0]);  // g  

charは1バイトなので、ユニコードなどの文字列はこの場合うまく表示されません。
その場合はwchar_tを使う必要があります。

printf()の%dで整数として出力

printf()の書式指定子%dを使うと、配列の要素を整数として出力できます。

    char s[] = "good";  
    printf("%d\n", s[0]);  // 103  

char型の配列をポインタ変数に代入する

char型の配列はchar型のポインタ変数に代入することができます。

    char s[] = "good";  
    char *p = s;  
    printf("%s\n", p);  // good  

注意点としては、ポインタ型と配列型は基本的に違うものです。
ポインタ型と配列型を混同しないように注意してください。

普通の文字列`(const char *)`との違い

char型の配列の文字列はconst char *などで定義する文字列とは何が違うのでしょうか?
これは一言で言うと型が違います。
char型の配列の文字列の型は配列型ですが、ポインタで定義した文字列はポインタ型です。

またこの両者で文字列を扱う場合は微妙な違いが生じます。
簡単に言うと配列で定義した文字列は変更可能で、ポインタで定義した文字列は変更不可ということです。

`(const char *)`は変更不可能、char配列は変更可能

const char *の場合はconstが付いているのでその文字列の要素は変更できません。
しかしchar型の配列の場合は変更できます。
ただchar型の配列にもconstをつければ変更はできなくなります。

    const char s[] = "good";  
    s[0] = 'f';  // error: assignment of read-only location ‘s[0]’  

文字列リテラルは変更するとセグフォになる場合があるが、char配列はセグフォにならない

char *に代入した文字列リテラルは変更しようとすると処理系によってセグフォになる場合があり、変更しないのがセオリーです。
文字列リテラルの要素の変更は未定義動作になっており、変更した場合に何が起こるかは処理系依存です。
しかしchar型の配列に代入した文字列リテラルは配列にコピーされるので配列自体は変更できます。

    char *s = "good";  // 文字列リテラルのポインタ  
    s[0] = 'f';   // Segmentation fault?  

もっともこれはchar *の変数sに文字列リテラルのアドレスが保存されているからです。
sに文字配列のアドレスが保存されている場合などは変更可能です。
また繰り返しますが文字列リテラルの変更は未定義動作になっており環境によってはセグフォにならない場合もあります。
上記のコードはUbuntuではセグフォになりますがWindowsでは何も表示されませんでした。

おわりに

今回はC言語のchar型の配列の詳しい使い方について見てみました。
char型の配列は変更可能な文字列を定義する場合によく使われます。
ポインタで定義する場合と挙動が異なるので注意が必要です。

🦝 < 配列を代入したポインタは配列みたいに使えるけどね

🐭 < ややこしいよね