🇯🇵 日本語 | 🇺🇸 English

第22回 文字列

C言語の文字列(char配列)の扱い方。strcpy, strlen, strcmp等の文字列関数。

📖 このページで覚えること
✅ 最低限ここだけ覚える
  • 文字列 = char 配列 + 末尾 \0
  • printf("%s", s); で表示
  • char の実体は整数 (ASCIIコード)
  • strlen, strcpy, strcmp<string.h>
⭐ 余裕があれば読む
  • 大文字小文字変換 c + ('a'-'A')
  • strcpy は危険、snprintf を使う
  • 日本語は char 1個に入らない
💪 初心者は1回で分からなくて普通
文字列はポインタと配列の両方が絡むので、2つの概念が未消化だと混乱しがちです。
再挑戦のステップ
  1. 配列 を先に理解 (char s[10] も普通の配列)
  2. ポインタ を学んでから戻ってくる
  3. 末尾の \0(ヌル終端)の図を覚える — 「ここで終わり」の目印
  4. char の実体は整数 (ASCII) と思い出す
  5. わからない関数 (strlen など) は先に使い方だけ覚える
💡 コツ: "Hello" は実は {'H','e','l','l','o','\0'} の6バイト配列。文字列=配列+終端、これだけ掴めばOK。

新しい型「char」を学ぶ ― 1 文字を入れる箱

これまでに int(整数)と double(実数)を使ってきましたね。ここで初めて char(チャー)型を学びます。char は 1 文字 を入れる箱です。サイズは 1 バイト。書き方はとてもシンプル:
char c = 'A';          // c に文字 A を入れる
printf("%c\n", c);    // → A   (画面に A が出る)

🎨 ビジュアルで見る ― char は「1 マスの箱」

char c =
'A'
1 バイト
printf("%c", c)
画面出力
A
画面

⚠️ 'A'(シングル)と "A"(ダブル)はまったく別物

同じ A でも、クォートが違うと型も意味も別物です。間違えやすいので最初に押さえましょう。
🔵 'A' ― シングルクォート
A
1 文字(char 型 / 1 バイト)
char c = 'A';
🟠 "A" ― ダブルクォート
A
\0
文字列(char 配列 / 末尾に \0
char s[] = "A";
この後で扱う「文字列」は、右の "A" のように char を並べて末尾に \0 を付けたものです。

💡 実は char の中身は「数字」 ― ここからは少し奥の話

使い方の基本は上で全部です。ここから先は 「char はなぜ計算できるのか」 の理由を知りたい人向け。
実は char 型の中身は 1 バイトの整数で、画面に 'A' として見えるのは 「数値 65 を文字として解釈している」だけ なんです。
覚えておくと便利:
char c = 'A';char c = 65;完全に同じ意味
printf("%c", 65) → A / printf("%d", 'A') → 65
・このおかげで 'A' + 1'B' になる、という芸当ができる

ASCII コード早見表

それぞれの文字に「番号」が割り当てられています。大事な文字は 数字 (0〜9)大文字 (A〜Z)小文字 (a〜z) の 3 グループ。

① 制御文字 と 空白(よく使うもの)

文字10進16進説明
\000x00ヌル文字(文字列の終端の目印)
\t90x09水平タブ
\n100x0A改行
320x20半角スペース

② 数字 (10 個)

文字0123456789
10進48495051525354555657

③ 大文字アルファベット (26 個)

文字ABCDEFGHIJKLM
10進65666768697071727374757677
文字NOPQRSTUVWXYZ
10進78798081828384858687888990

④ 小文字アルファベット (26 個)

文字abcdefghijklm
10進979899100101102103104105106107108109
文字nopqrstuvwxyz
10進110111112113114115116117118119120121122
👀 気づき: 大文字 A=65、小文字 a=97。'a' - 'A' = 32 がいつでも成立。これが「大文字 ↔ 小文字 変換」のキーになります。

実例: 文字と数字は行き来できる

#include <stdio.h>

int main(void) {
    char c = 'A';
    printf("c = %c\n", c);     // → A   (文字として表示)
    printf("c = %d\n", c);     // → 65  (整数として表示)

    // 文字の「計算」もできる
    printf("%c\n", c + 1);    // → B   (65+1=66、'B'のコード)
    printf("%c\n", 'Z' - 3);  // → W

    // 65 を char に入れても 'A' と等価
    char d = 65;
    printf("%c\n", d);        // → A
    return 0;
}

定番テクニック

① 大文字 → 小文字 変換
char c = 'A';
char lower = c + ('a' - 'A');   // 'A' + 32 = 'a'
// または: char lower = c + 32;
② 文字の数字を本当の整数に
char ch = '7';            // これは 55 ('7'のASCII)
int n = ch - '0';        // 55 - 48 = 7 (本当の整数7)
printf("%d\n", n);       // → 7
③ 文字の判定(大文字か、数字か)
if (c >= 'A' && c <= 'Z') // 大文字?
if (c >= '0' && c <= '9') // 数字?
// 標準ライブラリ <ctype.h> にも isupper(c), isdigit(c) がある

なぜ char はそもそも整数なのか?

1960年代の ASCII(American Standard Code for Information Interchange)で「英字・数字・記号 = 7bit の数値」という対応表が決められました。C言語はこれを引き継ぎ、文字を整数として扱う設計になっています。この「シンプルな割り切り」が、文字演算を楽にし、Cの大きな特徴になっています。
注意: 日本語は char 1つに入らない
char c = 'あ'; はエラーか警告になります。日本語の1文字は複数バイト(UTF-8 なら 3バイト)必要なので、charの配列で扱うか、wchar_t を使うか、文字コードを自分で扱う必要があります。ASCII の範囲(英数字と記号)だけが char で安全に扱える文字です。
💡 思い出そう ― コンピュータは「数字」しか扱えない
第 1 回でも触れたとおり、コンピュータの中身は 0 と 1(電気の ON / OFF)だけ
画面に 'A''あ' が見えていても、メモリには 結局すべて数値(番号)として保存 されています。char が「実は整数」なのは特別な仕様ではなく、「文字に番号を割り当てた」だけ という当たり前の話です。
この感覚を持っておくと、後で出てくる 文字列・ファイル・通信・画像 なども「結局はバイト(数字)の並び」として理解しやすくなります。

char を scanf で入力する

char の使い方が分かったので、次は キーボードから 1 文字を読み取る 方法を学びます。基本はとても簡単ですが、連続入力でハマる「改行残り」の罠 があるので、後半でそれも押さえます。

基本の使い方

printf と対になるのが scanf。書式は %c(character の c)。int の %d と同じ感覚で、変数の住所 & を渡す のがポイントです。
#include <stdio.h>

int main(void) {
    char c;
    printf("1 文字入力してね: ");
    scanf("%c", &c);          // ← & を忘れない!
    printf("入力された文字: %c\n", c);
    return 0;
}
/* 実行例
   1 文字入力してね: A   ← A を入力して Enter
   入力された文字: A
*/
覚え方:printf("%c", c)表示scanf("%c", &c)入力」。違いは & の有無 だけ。

⚠️ 注意:他の scanf と続けると「改行」が入ってしまう

基本はこれだけですが、2 回以上 scanf を続けて呼ぶ ときに 1 つだけ罠があります。たとえば「数字を読んでから 1 文字を読む」と…
int  n;
char c;
scanf("%d", &n);  // 数字を入力 → Enter
scanf("%c", &c);  // 続けて 1 文字を入力したい… が、うまくいかない!
printf("n=%d c=[%c]\n", n, c);
5 Enter A Enter」と入力したつもりでも、c には 'A' ではなく '\n'(改行)が入ってしまいます。原因は、入力が 「入力バッファ」という見えない待合室 を経由するから。

🔍 何が起きているか ― 入力バッファを覗く

あなたが「5 Enter」と打つと、入力バッファにはこう並ぶ:
5
数字
\n
Enter で入る改行
scanf("%d", &n)5 だけを取り出して n に入れる。改行 \n残ったまま:
5
→ n=5 へ
\n
残ってる!
② 続く scanf("%c", &c)バッファの先頭の 1 文字 を取る。先頭は \n なので…:
\n
バッファ先頭
c = '\n'
意図と違う!
A を入力する前に、c はもう \n で埋まってしまう。

✅ 対策 ― " %c" と先頭スペースを入れる

%c の前に 半角スペース 1 つ を書くと、scanf は 空白文字(スペース・タブ・改行)を全部読み飛ばして から 1 文字を取ってくれます。
int  n;
char c;
scanf("%d", &n);
scanf(" %c", &c); // ← " %c" の先頭スペースが \n を吸収!
printf("n=%d c=[%c]\n", n, c); // → n=5 c=[A]  期待通り!
覚え方: %c の前にはスペースを 1 つ」。これだけで連続入力の改行残り問題がほぼ解決します。
%d%s はもともと先頭の空白を読み飛ばす仕様なので、この問題が起きません。%c だけ別ルール)

文字列は「char の配列」で表す

前のセクションで char1 文字 を入れる箱だと学びました。
1 文字を入れる箱を並べたもの = 配列、つまり char の配列 を使えば、複数の文字 = 文字列 が表現できます。
'C'
[0]
'l'
[1]
'a'
[2]
'u'
[3]
'd'
[4]
'e'
[5]
'\0'
[6]
char の箱を 7 個 並べて、文字列 "Claude" を表現
ポイント:文字列の 最後には必ず '\0'(ヌル文字) を置きます。「ここで文字列が終わり」の目印で、これがないと printf がどこまで出力していいか分かりません。
char name[20] = "Claude";   // char の箱を 20 個用意(最大19文字 + '\0')
printf("%s\n", name);       // → Claude
%s は文字列用の書式指定子(string の s)。配列名だけを渡します(先頭の箱の住所が渡る)。

文字配列とメモリ配置

文字列 "Hello" がメモリにどう並ぶかを見てみましょう。
char msg[6] = "Hello"; ← 6バイト確保される(5文字 + '\0')
サイズに注意!「Hello」5文字でも、終端のヌル文字('\0')を含めて6バイト必要です。配列サイズが足りないとバッファオーバーフローのバグになります。

文字列の入出力

文字列を入出力するには %s を使います。scanfでは & を付けないのが特徴!
char name[20];
printf("名前は? ");
scanf("%s", name); // & は不要
printf("こんにちは、%sさん\n", name);
なぜ & が不要? 配列名 name は、それ自体が先頭要素のアドレスとして扱われます。だから & を付けなくてもアドレスが渡るのです。
⚠️ scanf("%s") の落とし穴:空白で区切られる&入力長のチェックをしません。長すぎる入力で配列をオーバーフローする危険があります(実務では fgets が安全)。

文字列の長さを数える

strlen(標準ライブラリ)を使えば簡単ですが、仕組みを知るため「自作版」を見てみましょう。
// '\0' が来るまで数える
int myStrlen(char s[]){
  int i = 0;
  while(s[i] != '\0') i++;
  return i;
}

int main(void){
  char msg[] = "Hello";
  printf("%d\n", myStrlen(msg)); // → 5
}
ヌル文字 '\0' がなぜ重要か分かりますね。終端を判定する目印として必須なのです。

string.h 関数一覧

#include <string.h> で使える、文字列操作の主要関数をまとめます。
関数機能使用例注意点
strlen(s)文字列の長さstrlen("abc") → 3'\0' は含まない
strcpy(dst, src)文字列コピーstrcpy(s, "Hello")dst に十分なサイズが必要
strncpy(dst, src, n)最大n文字コピーstrncpy(s, src, 10)バッファオーバーフロー防止
strcat(dst, src)文字列の連結strcat(s, " World")dst に連結後のサイズが必要
strcmp(s1, s2)文字列の比較strcmp(a, b) == 00なら等しい、正/負で辞書順
strncmp(s1, s2, n)先頭n文字を比較strncmp(s, "ab", 2)部分一致の判定に便利
strchr(s, c)文字を検索strchr(s, 'o')見つからなければNULL
strstr(s, sub)部分文字列を検索strstr(s, "llo")見つからなければNULL

strcpy と strcat の例

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

int main(void) {
    char greeting[50];
    strcpy(greeting, "Hello");     // greeting = "Hello"
    strcat(greeting, ", World!"); // greeting = "Hello, World!"
    printf("%s (%lu文字)\n", greeting, strlen(greeting));
    return 0;
}
== で文字列を比較しない!
if (s1 == s2) はアドレスの比較であり、文字列の内容は比較されません。
文字列比較には必ず strcmp(s1, s2) == 0 を使いましょう。
バッファオーバーフローに注意:
strcpystrcat は書き込み先のサイズをチェックしません。
十分な大きさの配列を用意するか、strncpy で最大文字数を制限しましょう。

自分で書いてみよう ― 文字列

文字列を表示し、長さを出力するプログラムです。
string.c
出力
「実行」を押してください...
💡 こんなことも試してみよう

関連する講座

繰り返し・配列・文字列
第20回 配列
C言語の配列の宣言・初期化・アクセス方法。メモリ配置も図解。
入門編
第4回 printf・scanf
C言語のprintf関数とscanf関数の使い方。書式指定子を一覧で解説。
発展編
第34回 ポインタの基礎
C言語のポインタをメモリ可視化で理解。アドレスと間接参照を図解。
← 前の講座
第21回 確認問題(配列)
次の講座 →
第23回 確認問題(文字列)

確認クイズ

この講座の理解度をチェックしましょう!

Q1. C言語の文字列の末尾には何がある?

改行文字 \n
ヌル文字 \0
スペース

C言語の文字列は必ずヌル文字 \0 で終端されます。これにより文字列の長さを判別できます。

Q2. "Hello" を格納するのに必要な配列サイズは?

5
6
7

"Hello" は5文字 + 終端の \0 で合計6バイト必要です。char s[6] 以上が必要です。

Q3. 文字列のコピーに使う関数は?

strcpy
strcmp
strlen

strcpy がコピー、strcmp が比較、strlen が長さ取得です。string.h をインクルードして使います。

この記事をシェア
X(Twitter)でシェア Facebookでシェア LINEで送る はてブ

この講座の理解を深めるおすすめ書籍

サイトで動きを理解し、書籍で演習量を補うと効果的です

📘
苦しんで覚えるC言語
MMGames 著
初心者向けの定番入門書。丁寧な解説で基礎を固められます。
Amazonで見る
📗
新・明解C言語 入門編
柴田望洋 著
図解が豊富で、演習問題も充実。大学の教科書としても採用多数。
Amazonで見る
📙
プログラミング言語C 第2版
B.W.カーニハン, D.M.リッチー 著
通称K&R。C言語の原典。基礎を終えた後のステップアップに最適。
Amazonで見る

※ 上記リンクはアフィリエイトリンクです。購入によりサイト運営を支援いただけます。