24 文字列の扱い

C言語に「文字列型」は存在しない。
文字列は char 型の配列として扱われる。この章でその仕組みを学ぶ。


24-1 C言語における文字列

C言語の文字列は char の配列 に文字を並べ、
末尾に ヌル文字('\0' を置いたものとして表現される。

"Hello" というリテラルをメモリ上で見ると:

[0][1][2][3][4][5]
'H''e''l''l''o' '\0'

文字数は5文字だが、ヌル文字の分で配列サイズは 6 必要になる。

⚠ ヌル文字 '\0' は ASCII コードが 0 の文字。数字の '0'(コード48)とは別物。
配列サイズを文字数ぴったりで宣言するとヌル文字が入らず、文字列として正しく動作しない。

24-2 文字列の宣言と初期化

char 配列名[サイズ] = "文字列";
char 配列名[サイズ] = {'文', '字', '\0'};  /* 同じ意味 */
char name[10] = "Taro";    /* サイズ10、"Taro"+'\0' の5文字分使用 */
char msg[]    = "Hello";   /* サイズは自動で 6(文字数+1)になる */
24_01_strDecl.c
#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

24-3 文字と文字列の違い

文字文字列
クォートシングル 'A'ダブル "A"
char(1バイト)char[](2バイト以上)
末尾なしヌル文字 '\0' が自動で付く
書式指定子%c%s
'A' は1文字で1バイト。"A" は文字列で 'A' + '\0' の2バイト。

24-4 文字列を1文字ずつ操作する

配列の添字でアクセスすれば1文字ずつ読み書きできる。
ヌル文字('\0')が文字列の終端なので、ループの終了判定に使える。

24_02_strLoop.c
#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' がループ終了条件のパターン。
文字列の長さを事前に知らなくても末尾まで処理できる。

24-5 文字列の長さを自分で実装する

ヌル文字までの文字数が文字列の長さ。実際には strlen() を使うが、
原理を理解するために自前で実装してみよう。

24_03_strLength.c
#include <stdio.h>

int main(void)
{
    char str[] = "Program";
    int  len   = 0;

    while (str[len] != '\0') {
        len++;
    }
    printf("長さ: %d\n", len);
    return 0;
}
長さ: 7

24-6 scanf で文字列を受け取る

24_04_strInput.c
#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章参照)。
⚠ 宣言した配列サイズより長い文字列を入力するとバッファオーバーフローになる。
配列サイズは十分に大きく確保しておくこと。

24-7 文字列の比較と代入は == と = でできない

文字列(配列)は == で比較したり = で代入したりできない。
これは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 */
24_05_strCompare.c
#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

24-8 演習

📝 演習① 文字列を逆順に表示する

文字列を入力して、逆順に1文字ずつ表示するプログラムを作れ。
まず strlen() で長さを取得してから逆順ループを書く。

24_06_reverse.c
#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]);
    }
📝 演習② 大文字の数を数える

文字列を入力して、その中に含まれる大文字英字の数を表示するプログラムを作れ。

24_07_countUpper.c
#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++;
        }
    }

24-9 理解度チェック

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" になる。文字列は配列なので個別の要素を変更できる。