文字と文字列


文字型

char型は、「アルファベット1文字」を格納するための変数である。
char c;
c = 'a';
printf("%c\n", c);
このように、文字定数はシングルクオートで囲む。printfで表示 するときは「%c」を使う。

計算機に文字を記憶するために、「文字コード」と呼ばれる数値が各 文字に対して決められており、メモリ上ではその数値で表現されている。 ASCII(アスキーコード)と呼ばれる文字コードが一般的である。 アスキーコードの数値(16進数)と文字の対応は以下の通り。

     00 NUL   01 SOH   02 STX   03 ETX   04 EOT   05 ENQ   06 ACK   07 BEL
     08 BS    09 HT    0A NL    0B VT    0C NP    0D CR    0E SO    0F SI
     10 DLE   11 DC1   12 DC2   13 DC3   14 DC4   15 NAK   16 SYN   17 ETB
     18 CAN   19 EM    1A SUB   1B ESC   1C FS    1D GS    1E RS    1F US
     20 SP    21  !    22  "    23  #    24  $    25  %    26  &    27  '
     28  (    29  )    2a  *    2b  +    2c  ,    2d  -    2e  .    2f  /
     30  0    31  1    32  2    33  3    34  4    35  5    36  6    37  7
     38  8    39  9    3a  :    3b  ;    3c  <    3d  =    3e  >    3f  ?
     40  @    41  A    42  B    43  C    44  D    45  E    46  F    47  G
     48  H    49  I    4a  J    4b  K    4c  L    4d  M    4e  N    4f  O
     50  P    51  Q    52  R    53  S    54  T    55  U    56  V    57  W
     58  X    59  Y    5a  Z    5b  [    5c  \    5d  ]    5e  ^    5f  _
     60  `    61  a    62  b    63  c    64  d    65  e    66  f    67  g
     68  h    69  i    6a  j    6b  k    6c  l    6d  m    6e  n    6f  o
     70  p    71  q    72  r    73  s    74  t    75  u    76  v    77  w
     78  x    79  y    7a  z    7b  {    7c  |    7d  }    7e  ~    7f DEL
00〜1Fと7Fはコントロールコードと呼ばれ、改行を表すなど通常の文字では無い 特殊用途に使われる。20はスペース。

「5C」の部分は、端末によって、「\」(バックスラッシュ)に見えたり、 「¥」(円) に見えたりする。アメリカのASCIIコードを輸入してJISにするとき、 「$があって円が無いと日本では困るだろう。あんまり使わなそうな 右下がり斜線を円に変えちゃえ!」とでも思った人がいたのだろうか。 馬鹿なことをしたものである。

C言語では文字コードの数値と文字を同一視しており(意図的な混用)、 例えば'A'は数字の65(16進数の41=10進数の65)と同じで、

c = 'A';
c = 65;
は全く同じである。 このことをうまく利用すると、 '0'+3が'3'になったりする。'0'は48(16進数の30)と同じで、 48+3は51で、'3'は51(16進数の33)と同じだからである。 char型は単なる8bitのとても短い整数型である。 の整数が格納出来る。単にcharと書いたときそれが unsignedsignedかは処理系依存(コンパイラにより異なる)。

日本語などの2byte文字の扱いについてはここでは触れない。 というか標準的な手段が無い。

文字列

C言語では、組み込み型として文字列型を持っておらず、 文字列は文字の配列として扱う。 C言語以外の多くの言語では文字列を簡単に扱うための機構を 言語に組み込んでおり、文字列処理の面倒さはC言語の欠点の一つと言える。
「CPUにとって面倒な処理はプログラムを書くのが面倒」という C言語の特徴が現れているとも言える。細かいメモリの動的確保が 必要になったり、文字列処理は「意外に」CPUにとっては重い処理である。

さすがに全部手で書くのは大変なので、文字列を扱うための ライブラリが用意されている。 文字列ライブラリを使うには、

#include <string.h>
が必要。以下にサンプルを示す。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char a[6] = "hello";
    /* error */
    /* char a[5] = "hello"; */
    char b[100] = "world";
    char c[100];

    int i, len;

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

    printf("%d\n", strlen(b));

    len = strlen(a);

    for (i=0; i<len; i++) {
        printf("%c\n", a[i]);
    }

    strcpy(c, a);
    printf("%s\n", c);
    strcat(c, b);
    printf("%s\n", c);

    return 0;
}
string-sample.c

これを実行すると、

hello
5
h
e
l
l
o
hello
helloworld
のようになる。

文字列関係の標準ライブラリを使う上でとても重要なことは、 文字列の終端の扱いである。文字列の長さを管理していないので、 文字列の終りを表すのに、文字コード0の特殊文字を暗黙のうちに 用いている。例えば、文字列"hello"は、内部では

0 1 2 3 4 5
'h' 'e' 'l' 'l' 'o' 0

のように6文字使って表現される。従って、文字列を格納するための 文字配列を用意するときは、必ず「そこに格納される可能性のある文字列の 最大長 +1」の長さの配列を用意しなければならない。

サンプルにあるように、printf%sを用いて文字列を 表示することが出来る。

strlen

strlenは文字列の長さを返す。その配列の長さでは無く、 現在格納されている文字列の長さを返すことに注意。内部では文字コード0の 文字が出て来るまでの文字数を数えている。
int strlen(char *s) {
    int i = 0;
    while (s[i] != 0) {
        i++;
    }
    return i;
}
のようなことをやっているはず。

strcpy

strcpyは、文字列のコピーを行う。 strcpy(d, s)sdにコピーする。
char * strcpy(char *d, char *s) {
    int i = 0;
    while (s[i] != 0) {
        d[i] = s[i];
        i++;
    }
    d[i] = 0;
    return d; 
}
または、ポインタで書けば、
char * strcpy(char *d, char *s) {
    char *p = d;
    while (*s != 0) {
        *p = *s;
        p++;
        s++;
    }
    *p = 0;
    return d; 
}
更にトリッキーに書くと(こういう書き方は現在ではあまりお勧めしない)、
char * strcpy(char *d, char *s) {
    char *p = d;
    while (*(p++) = *(s++)) ;
    return d; 
}
のようなことをやっているはず。戻り値はdをそのまま返すことになっている。

d, sは文字型へのポインタであるが、 dの指し示す先には、sをコピーするのに十分な大きさの 領域が存在しなければならない。すなわち、「strlen(s)+1」以上の大きさの 文字配列であるか、mallocでそれ以上の大きさの領域を確保してある 必要がある。

strcat

strcatは、文字列の結合を行う。 strcat(d, s)sdの末尾に結合する。
char * strcat(char *d, char *s) {
    char *p = d;
    while (*p != 0) {
        p++;
    }
    while (*s != 0) {
        *p = *s;
        p++;
        s++;
    }
    *p = 0;
    return d; 
}
のような動作をする。dの指し示す先に十分な大きさの領域が 必要なのはstrcpyと同様。

strcmp

strcmpは、文字列の比較を行う。 strcmp(s1, s2)で、s1がs2より小さい、等しい、大きいとき、 それぞれ負、0、正の数を返す。
int strcmp(char *s1, char *s2) {
    while (*s1 == *s2) {
        s1++;
        s2++;
        if (*s1 == 0) return 0;
    }
    return *s1 - *s2;
}
のような動作をする。

sprintf

char s[100];
sprintf(s, "%d", 3);
のように使う。最初の引数sに、第2引数以降の情報に従って「printf」してくれる。 なかなか便利であるが、printfの表示文字数がなかなか予測しづらいだけに、 sに十分な大きさの領域を確保するよう注意すること。

sscanf

sprintfの逆。
double d;
sscanf("1.25", "%lf", &d);
のように、キーボード入力の代わりに第1引数の文字列を用いて「scanf」する。

その他

その他、strncpystrncatstrncmpatoiatofなどの関数が用意されている。 他の関数は、ここを見て欲しい。
文字と文字列