C言語に「文字列型」は存在しない。
文字列は char 型の配列として扱われる。この章でその仕組みを学ぶ。
C言語の文字列は char の配列 に文字を並べ、
末尾に ヌル文字('\0') を置いたものとして表現される。
"Hello" というリテラルをメモリ上で見ると:
| [0] | [1] | [2] | [3] | [4] | [5] |
|---|---|---|---|---|---|
| 'H' | 'e' | 'l' | 'l' | 'o' | '\0' |
文字数は5文字だが、ヌル文字の分で配列サイズは 6 必要になる。
'\0' は ASCII コードが 0 の文字。数字の '0'(コード48)とは別物。char 配列名[サイズ] = "文字列";
char 配列名[サイズ] = {'文', '字', '\0'}; /* 同じ意味 */
char name[10] = "Taro"; /* サイズ10、"Taro"+'\0' の5文字分使用 */
char msg[] = "Hello"; /* サイズは自動で 6(文字数+1)になる */
#include <stdio.h>
int main(void)
{
char name[10] = "Taro";
char msg[] = "Hello";
printf("%s\n", name); /* %s で文字列を表示 */
printf("%s\n", msg);
return 0;
}
Taro Hello
| 文字 | 文字列 | |
|---|---|---|
| クォート | シングル 'A' | ダブル "A" |
| 型 | char(1バイト) | char[](2バイト以上) |
| 末尾 | なし | ヌル文字 '\0' が自動で付く |
| 書式指定子 | %c | %s |
'A' は1文字で1バイト。"A" は文字列で 'A' + '\0' の2バイト。
配列の添字でアクセスすれば1文字ずつ読み書きできる。
ヌル文字('\0')が文字列の終端なので、ループの終了判定に使える。
#include <stdio.h>
int main(void)
{
char str[] = "Hello";
int i;
/* 1文字ずつ表示 */
for (i = 0; str[i] != '\0'; i++) {
printf("[%d] = '%c'\n", i, str[i]);
}
return 0;
}
[0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o'
str[i] != '\0' がループ終了条件のパターン。ヌル文字までの文字数が文字列の長さ。実際には strlen() を使うが、
原理を理解するために自前で実装してみよう。
#include <stdio.h>
int main(void)
{
char str[] = "Program";
int len = 0;
while (str[len] != '\0') {
len++;
}
printf("長さ: %d\n", len);
return 0;
}
長さ: 7
#include <stdio.h>
int main(void)
{
char name[30];
printf("名前を入力してください: ");
scanf("%s", name); /* 文字列の場合 & は不要 */
printf("こんにちは、%s さん!\n", name);
return 0;
}
名前を入力してください: Tanaka こんにちは、Tanaka さん!
scanf("%s", name) では & を付けない。%s はスペースで入力が止まる。スペースを含む文字列は fgets() を使う(25章参照)。
文字列(配列)は == で比較したり = で代入したりできない。
これはC言語の重要な落とし穴のひとつ。
char a[] = "Hello";
char b[] = "Hello";
/* 間違い:配列のアドレスを比較してしまう */
if (a == b) { ... } /* NG:意図した動作にならない */
/* 正しい:文字列比較には strcmp を使う */
if (strcmp(a, b) == 0) { ... } /* OK */
/* 間違い:配列への代入はできない */
char str[10];
str = "World"; /* NG:コンパイルエラー */
/* 正しい:strcpy を使う */
strcpy(str, "World"); /* OK */
#include <stdio.h>
#include <string.h>
int main(void)
{
char a[20] = "Hello";
char b[20] = "Hello";
char c[20] = "World";
if (strcmp(a, b) == 0) {
printf("a と b は同じ\n");
}
if (strcmp(a, c) != 0) {
printf("a と c は違う\n");
}
strcpy(a, "C Language");
printf("コピー後の a: %s\n", a);
return 0;
}
a と b は同じ a と c は違う コピー後の a: C Language
文字列を入力して、逆順に1文字ずつ表示するプログラムを作れ。
まず strlen() で長さを取得してから逆順ループを書く。
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[50];
int len, i;
printf("文字列を入力してください: ");
scanf("%s", str);
len = (int)strlen(str);
/* ここに逆順表示の処理を書く */
printf("\n");
return 0;
}
for (i = len - 1; i >= 0; i--) {
printf("%c", str[i]);
}
文字列を入力して、その中に含まれる大文字英字の数を表示するプログラムを作れ。
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char str[50];
int i, cnt = 0;
printf("文字列を入力してください: ");
scanf("%s", str);
/* ここにカウント処理を書く */
printf("大文字の数: %d\n", cnt);
return 0;
}
for (i = 0; str[i] != '\0'; i++) {
if (isupper(str[i])) {
cnt++;
}
}
Q1. char s[] = "ABC"; の配列サイズとして正しいものはどれか。
正解:イ(4)
文字列の末尾にはヌル文字 '\0' が自動で付く。"ABC" は 'A','B','C','\0' の4文字分。
Q2. 文字列の終端を表すヌル文字として正しいものはどれか。
正解:ウ
'\0' は ASCII コード 0 の文字。'0' はコード 48 の数字文字で別物。
Q3. 2つの文字列が等しいか比較する正しい方法はどれか。
正解:ウ
配列(文字列)の == はアドレスの比較になる。文字列の内容を比較するには strcmp を使う。
Q4. 次のコードの出力として正しいものはどれか。
#include <stdio.h>
int main(void)
{
char s[] = "cat";
s[0] = 'b';
printf("%s\n", s);
return 0;
}
正解:イ(bat)
s[0] を 'b' に書き換えると "cat" → "bat" になる。文字列は配列なので個別の要素を変更できる。