C言語基礎|ポインタによる文字列の操作

添字を捨てて、ポインタで読む!―Cらしい文字列操作の最短ルートを身につけよう。

第9章で学んだ文字列操作は、添字を使って str[i] のように扱う方法が中心でした。
でもC言語では、ポインタを動かして文字列を走査する書き方がとてもよく使われます。

添字が悪いわけではないんですが、ポインタ走査は

  • ループが短く書ける。
  • 文字列の「先頭から終端まで」を自然にたどれる。
  • ライブラリ関数の実装イメージが掴みやすい。

といったメリットがあって、C言語っぽさが一気に増します。
ここではまず「文字列の長さを調べる」を題材にして、ポインタによる走査のコツをしっかり固めましょう。

文字列の長さを調べる(ポインタ走査の基本)

サンプルプログラム

プロジェクト名:chap11-4-1 ソースファイル名:chap11-4-1.c

#include <stdio.h>

// 文字列sの長さを返す(ポインタで走査)
int str_length(const char *s)
{
    int len = 0;

    while (*s++) {   // 終端 '\0' まで進む
        len++;
    }

    return len;
}

int main(void)
{
    char str[128];

    printf("短い単語を入力してください:");
    scanf("%127s", str);

    printf("入力した「%s」の文字数は %d です。\n", str, str_length(str));

    return 0;
}

このプログラムで言いたいこと

  • main は「文字列を受け取って長さを表示」するだけ。
  • 重要なのは str_length の中身で、添字を一切使わずに * と ++ で進んでいく点です。

仮引数 const char *s の意味

仮引数宣言の読み方

宣言意味うれしい点
const char *s文字列(先頭文字)を指すポインタを受け取る。指す先の文字は変更しない約束。文字列リテラルや読み取り専用領域も安全に扱える。関数が勝手に書き換えないことが明確。

表の説明

  • char * は「char を指すポインタ」
  • const char * は「指している先の char を変更しない」
    (ポインタそのもの s を動かすのはOKです。変更禁止なのは *s の中身のほう)

while (*s++) の評価順序がポイント

ここが一番おいしいところです。
while の条件式 *s++ は、見た目は短いですが、内部では順序があります。

*s++ の意味を分解

ざっくり意味
s++いま指している場所を使った後で、ポインタを1つ進める(後置増分)
*ss が指す1文字を取り出す(参照外し)
*s++「いまの1文字」を取り出して判定に使い、その直後に s を次の文字へ進める

表の説明

  • 後置 ++ なので「先に参照、あとで進む」になります
  • つまり while の判定は、現在の文字が '\0' かどうかを見て、見終わったら次へ進みます

図でつかむ:ポインタ走査の動き

例として入力が "Neko" だったとしましょう(実際はどんな文字列でも同じです)。

s が文字を1つずつ指して進む

図の説明

  • s は「いま見ている文字」を指す
  • ループ1回ごとに s が右へ1つ進む
  • '\0' を見つけた瞬間に止まる
  • len は「ループが回った回数」=「文字数」になる

重要ルール:ポインタの ++ と -- は何をする?

文章中の重要ポイントを、誤解しにくい形で整理します。

ポインタに対する ++ / --

表現等価な意味起きること(配列や文字列の場合)
p++p = p + 11要素後ろを指す(次の文字へ)
p--p = p - 11要素前を指す(前の文字へ)

表の説明

  • ++ 自体が特別というより、p + 1 が「次の要素」を指すのがCの規則です
  • char の配列なら 1 バイトずつ進むイメージになって、文字列走査にぴったりです

添字版との比較(同じ動作だけど見え方が違う)

同じ長さ計算でも、添字版とポインタ版で発想が変わります。

添字走査とポインタ走査

方式典型例目線
添字走査while (s[i] != '\0') i++;i が進む。文字列は固定で見に行く
ポインタ走査while (*s++) len++;s 自体が進む。いま見てる場所が移動する

表の説明

  • 添字は「位置 i」を変えてアクセス
  • ポインタは「指す場所」を変えてアクセス
  • Cのライブラリ関数(strlen など)は、ポインタ走査の発想がベースになりやすいです

登場する命令(関数)の書式と役割

scanf

  • 書式:scanf(書式文字列, 格納先のアドレス...);
  • 何をする命令?:標準入力から読み取り、指定した場所へ格納する
  • 今回のポイント:%127s
    最大127文字まで読み、終端 '\0' を付ける(配列サイズ128に合わせた安全策)

printf

  • 書式:printf(書式文字列, 引数...);
  • 何をする命令?:書式に従って表示する
  • 今回の %s:文字列(先頭文字へのポインタ)を受け取り '\0' まで表示