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 DEL00〜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のとても短い整数型である。
日本語などの2byte文字の扱いについてはここでは触れない。 というか標準的な手段が無い。
「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; } |
これを実行すると、
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を用いて文字列を 表示することが出来る。
int strlen(char *s) { int i = 0; while (s[i] != 0) { i++; } return i; }のようなことをやっているはず。
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でそれ以上の大きさの領域を確保してある 必要がある。
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と同様。
int strcmp(char *s1, char *s2) { while (*s1 == *s2) { s1++; s2++; if (*s1 == 0) return 0; } return *s1 - *s2; }のような動作をする。
char s[100]; sprintf(s, "%d", 3);のように使う。最初の引数sに、第2引数以降の情報に従って「printf」してくれる。 なかなか便利であるが、printfの表示文字数がなかなか予測しづらいだけに、 sに十分な大きさの領域を確保するよう注意すること。
double d; sscanf("1.25", "%lf", &d);のように、キーボード入力の代わりに第1引数の文字列を用いて「scanf」する。