閉じる

ヌル文字に注意!strlenで文字列長を正しく測る方法

C言語の文字列はchar配列で、末尾のヌル文字(‘\0’)で終端します。

長さを測るときは標準関数strlenを使いますが、ヌル終端の扱い、sizeofとの違い、日本語のバイト数などで混乱しがちです。

本記事ではヌル文字に注意してstrlenで正しく長さを測るための基本と安全な使い方を丁寧に解説します。

C言語のstrlenとは?

文字列長を返す仕組み

内部的な動きのイメージ

strlenは、文字配列の先頭からヌル文字'\0'に出会うまで1バイトずつ数え上げます。

したがって、配列の途中に'\0'があればそこでカウントは止まり、その後ろのデータは「文字列の一部」と見なされません。

コード例: 自作strlenで仕組みを確認

以下はstrlenの挙動をまねた簡易実装です。

C言語
#include <stdio.h>
#include <string.h> // 本物の strlen を使うため

// 教材用の簡易実装: 先頭から '
#include <stdio.h>
#include <string.h> // 本物の strlen を使うため
// 教材用の簡易実装: 先頭から '\0' まで数える
size_t my_strlen(const char *s) {
// 注意: 本物の strlen と同じく NULL チェックはしません
size_t n = 0;
while (s[n] != '\0') {
n++;
}
return n;
}
int main(void) {
const char *s = "Hello";
printf("my_strlen(\"%s\") = %zu\n", s, my_strlen(s));
printf("strlen(\"%s\")    = %zu\n", s, strlen(s));
return 0;
}
' まで数える size_t my_strlen(const char *s) { // 注意: 本物の strlen と同じく NULL チェックはしません size_t n = 0; while (s[n] != '
#include <stdio.h>
#include <string.h> // 本物の strlen を使うため
// 教材用の簡易実装: 先頭から '\0' まで数える
size_t my_strlen(const char *s) {
// 注意: 本物の strlen と同じく NULL チェックはしません
size_t n = 0;
while (s[n] != '\0') {
n++;
}
return n;
}
int main(void) {
const char *s = "Hello";
printf("my_strlen(\"%s\") = %zu\n", s, my_strlen(s));
printf("strlen(\"%s\")    = %zu\n", s, strlen(s));
return 0;
}
') { n++; } return n; } int main(void) { const char *s = "Hello"; printf("my_strlen(\"%s\") = %zu\n", s, my_strlen(s)); printf("strlen(\"%s\") = %zu\n", s, strlen(s)); return 0; }
実行結果
my_strlen("Hello") = 5
strlen("Hello")    = 5

戻り値(size_t)とヘッダ<string.h>

size_tとは

strlenの戻り値は符号なし整数型のsize_tです。

size_tは配列サイズを表すのに適した型で、環境によりビット幅が異なります。

表示には%zuを使います。

C言語
#include <stdio.h>
#include <string.h>  // strlen を使うために必須

int main(void) {
    size_t len = strlen("ABC");
    printf("len = %zu\n", len); // %zu は size_t 専用の書式
    return 0;
}
実行結果
len = 3

ヘッダ<string.h>を必ずインクルード

strlen#include <string.h>で宣言されます。

これを忘れると未宣言関数の使用になり、警告や未定義動作の原因になります。

使いどころ

strlenは以下のような場面で使われます。

文字列入力の長さチェック、繰り返し処理での終端判定、バッファに収まるかの検証、動的メモリ確保(malloc)時の必要サイズ計算などです。

どれもヌル終端を前提に正しく使うことが重要です。

ヌル文字とstrlenの基本

ヌル終端(‘\0’)までを数える

途中の’\0’でカウントが止まる例

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    // 'A','B','
#include <stdio.h>
#include <string.h>
int main(void) {
// 'A','B','\0','C','D' という配列
char s[] = { 'A', 'B', '\0', 'C', 'D', '\0' };
printf("strlen = %zu\n", strlen(s)); // 'A','B' の 2 で止まる
return 0;
}
','C','D' という配列 char s[] = { 'A', 'B', '
#include <stdio.h>
#include <string.h>
int main(void) {
// 'A','B','\0','C','D' という配列
char s[] = { 'A', 'B', '\0', 'C', 'D', '\0' };
printf("strlen = %zu\n", strlen(s)); // 'A','B' の 2 で止まる
return 0;
}
', 'C', 'D', '
#include <stdio.h>
#include <string.h>
int main(void) {
// 'A','B','\0','C','D' という配列
char s[] = { 'A', 'B', '\0', 'C', 'D', '\0' };
printf("strlen = %zu\n", strlen(s)); // 'A','B' の 2 で止まる
return 0;
}
' }; printf("strlen = %zu\n", strlen(s)); // 'A','B' の 2 で止まる return 0; }
実行結果
strlen = 2

空文字(“”)は長さ0

空文字の定義と結果

""は最初から'\0'だけを含む文字列です。

長さは0になります。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    char empty[] = "";
    printf("empty の長さ = %zu\n", strlen(empty));
    return 0;
}
実行結果
empty の長さ = 0

ヌル文字が無いchar配列は危険

ヌル終端の欠落は未定義動作

ヌル終端のないchar配列にstrlenを呼ぶのは未定義動作です。

どこまでも読み続けてメモリ破壊やクラッシュにつながる可能性があります。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    char bad[3] = { 'A', 'B', 'C' }; // ← '
#include <stdio.h>
#include <string.h>
int main(void) {
char bad[3] = { 'A', 'B', 'C' }; // ← '\0' が無い
// printf("%zu\n", strlen(bad)); // 危険: 未定義動作になる可能性
// 安全策: 明示的に終端を付ける
char ok[4] = { 'A', 'B', 'C', '\0' };
printf("安全な ok の長さ = %zu\n", strlen(ok));
return 0;
}
' が無い // printf("%zu\n", strlen(bad)); // 危険: 未定義動作になる可能性 // 安全策: 明示的に終端を付ける char ok[4] = { 'A', 'B', 'C', '
#include <stdio.h>
#include <string.h>
int main(void) {
char bad[3] = { 'A', 'B', 'C' }; // ← '\0' が無い
// printf("%zu\n", strlen(bad)); // 危険: 未定義動作になる可能性
// 安全策: 明示的に終端を付ける
char ok[4] = { 'A', 'B', 'C', '\0' };
printf("安全な ok の長さ = %zu\n", strlen(ok));
return 0;
}
' }; printf("安全な ok の長さ = %zu\n", strlen(ok)); return 0; }
実行結果
安全な ok の長さ = 3

初心者がつまずくポイント

strlenとsizeofの違い

strlen'\0'までの文字数、sizeofは型や配列のバイト数です。

ポインタに対するsizeofは文字列の長さではなくポインタ自体のサイズになります。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    char a[]  = "ABC";     // 配列要素: 'A','B','C','
#include <stdio.h>
#include <string.h>
int main(void) {
char a[]  = "ABC";     // 配列要素: 'A','B','C','\0' → sizeof(a) は 4
char b[10] = "ABC";    // 10バイト配列 → sizeof(b) は 10
const char *p = "ABC"; // 文字列リテラルへのポインタ → sizeof(p) はポインタのサイズ
printf("strlen(a)=%zu, sizeof(a)=%zu\n", strlen(a), sizeof(a));
printf("strlen(b)=%zu, sizeof(b)=%zu\n", strlen(b), sizeof(b));
printf("strlen(p)=%zu, sizeof(p)=%zu\n", strlen(p), sizeof(p));
return 0;
}
' → sizeof(a) は 4 char b[10] = "ABC"; // 10バイト配列 → sizeof(b) は 10 const char *p = "ABC"; // 文字列リテラルへのポインタ → sizeof(p) はポインタのサイズ printf("strlen(a)=%zu, sizeof(a)=%zu\n", strlen(a), sizeof(a)); printf("strlen(b)=%zu, sizeof(b)=%zu\n", strlen(b), sizeof(b)); printf("strlen(p)=%zu, sizeof(p)=%zu\n", strlen(p), sizeof(p)); return 0; }

実行結果64bit環境の一例
strlen(a)=3, sizeof(a)=4
strlen(b)=3, sizeof(b)=10
strlen(p)=3, sizeof(p)=8

概要を表にまとめます。

対象strlenの結果sizeofの結果
char a[] = “ABC”34
char b[10] = “ABC”310
const char *p = “ABC”3ポインタサイズ(例: 8)

NULLポインタにstrlenしない

NULLポインタにstrlenを呼ぶのは未定義動作です。

必ずチェックしましょう。

C言語
#include <stdio.h>
#include <string.h>

size_t safe_strlen(const char *s) {
    if (s == NULL) {
        // 呼び出し側のバグの可能性。ここでは 0 を返す例。
        return 0;
    }
    return strlen(s);
}

int main(void) {
    const char *s1 = "ABC";
    const char *s2 = NULL;
    printf("safe_strlen(s1) = %zu\n", safe_strlen(s1));
    printf("safe_strlen(s2) = %zu\n", safe_strlen(s2)); // ここでは 0
    return 0;
}
実行結果
safe_strlen(s1) = 3
safe_strlen(s2) = 0

改行(‘\n’)やスペースも文字数に含まれる

strlen空白や改行も普通の文字としてカウントします。

fgetsで読み込むと末尾に'\n'が付くことがあるので必要なら取り除きます。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    const char with_nl[] = "ABC\n";
    const char with_sp[] = "A B";

    printf("with_nl の長さ = %zu\n", strlen(with_nl)); // 4 ('\n' を含む)
    printf("with_sp の長さ = %zu\n", strlen(with_sp));  // 3 (スペース含む)

    // fgets で読み取ったと仮定した文字列の末尾改行を取り除く例
    char buf[100] = "hello world\n";
    size_t len = strlen(buf);
    if (len > 0 && buf[len - 1] == '\n') {
        buf[len - 1] = '
#include <stdio.h>
#include <string.h>
int main(void) {
const char with_nl[] = "ABC\n";
const char with_sp[] = "A B";
printf("with_nl の長さ = %zu\n", strlen(with_nl)); // 4 ('\n' を含む)
printf("with_sp の長さ = %zu\n", strlen(with_sp));  // 3 (スペース含む)
// fgets で読み取ったと仮定した文字列の末尾改行を取り除く例
char buf[100] = "hello world\n";
size_t len = strlen(buf);
if (len > 0 && buf[len - 1] == '\n') {
buf[len - 1] = '\0';
len--;
}
printf("改行除去後: \"%s\" (長さ %zu)\n", buf, len);
return 0;
}
'; len--; } printf("改行除去後: \"%s\" (長さ %zu)\n", buf, len); return 0; }
実行結果
with_nl の長さ = 4
with_sp の長さ = 3
改行除去後: "hello world" (長さ 11)

日本語(UTF-8など)は文字数でなくバイト数

strlenは「文字数」ではなく「バイト数」を返すことに注意してください。

UTF-8では日本語1文字が3バイトになることが多く、見かけの文字数とstrlenの結果が一致しません

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    // ソースコードが UTF-8 保存である前提
    const char *s1 = "あ";     // UTF-8 では通常 3 バイト
    const char *s2 = "日本";   // 通常 6 バイト

    printf("\"あ\" の strlen = %zu\n", strlen(s1));
    printf("\"日本\" の strlen = %zu\n", strlen(s2));
    return 0;
}

実行結果例(UTF-8環境):

"あ" の strlen = 3
"日本" の strlen = 6

人間の文字数を数えたい場合はマルチバイト/ワイド文字API(mbstowcs, mbrtowc など)や外部ライブラリの利用を検討してください。

未初期化メモリの文字列に注意

未初期化のchar配列にstrlenを呼ぶのは危険です。

必ずヌル終端を用意しましょう。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    char buf[16];           // 未初期化
    // printf("%zu\n", strlen(buf)); // 危険: どこまで読みに行くか不明

    // 安全: 空文字として初期化
    buf[0] = '
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[16];           // 未初期化
// printf("%zu\n", strlen(buf)); // 危険: どこまで読みに行くか不明
// 安全: 空文字として初期化
buf[0] = '\0';
printf("初期化後の長さ = %zu\n", strlen(buf)); // 0
return 0;
}
'; printf("初期化後の長さ = %zu\n", strlen(buf)); // 0 return 0; }
実行結果
初期化後の長さ = 0

strlenの使い方パターン

入力の文字列長チェックに使う

最小・最大長を検証する

ユーザー入力が要件を満たすかstrlenで検証します。

ここでは疑似的な入力で例示します。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    const size_t MIN_LEN = 3;
    const size_t MAX_LEN = 8;

    // 例: 入力された文字列がここに入ったと仮定
    char username[64] = "Alice\n";

    // 末尾の改行を取り除く
    size_t len = strlen(username);
    if (len > 0 && username[len - 1] == '\n') {
        username[len - 1] = '
#include <stdio.h>
#include <string.h>
int main(void) {
const size_t MIN_LEN = 3;
const size_t MAX_LEN = 8;
// 例: 入力された文字列がここに入ったと仮定
char username[64] = "Alice\n";
// 末尾の改行を取り除く
size_t len = strlen(username);
if (len > 0 && username[len - 1] == '\n') {
username[len - 1] = '\0';
len--;
}
if (len < MIN_LEN) {
printf("短すぎます(最小 %zu 文字)\n", MIN_LEN);
} else if (len > MAX_LEN) {
printf("長すぎます(最大 %zu 文字)\n", MAX_LEN);
} else {
printf("OK: \"%s\" は %zu 文字\n", username, len);
}
return 0;
}
'; len--; } if (len < MIN_LEN) { printf("短すぎます(最小 %zu 文字)\n", MIN_LEN); } else if (len > MAX_LEN) { printf("長すぎます(最大 %zu 文字)\n", MAX_LEN); } else { printf("OK: \"%s\" は %zu 文字\n", username, len); } return 0; }
実行結果
OK: "Alice" は 5 文字

ループではstrlenを一度だけ計算

繰り返しのたびに呼ばない

ループの条件にstrlen(s)を直接書くと毎回末尾まで走査します。

一度だけ長さを求めて変数に保持しましょう。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    const char *s = "The quick brown fox jumps over the lazy dog";
    // 悪い例: strlen が毎回呼ばれる
    // for (size_t i = 0; i < strlen(s); ++i) { ... }

    // 良い例: 1回だけ計算
    size_t n = strlen(s);
    for (size_t i = 0; i < n; ++i) {
        // ここで s[i] を使った処理を行う
    }
    printf("長さ %zu を1回だけ計算してループしました\n", n);
    return 0;
}
実行結果
長さ 43 を1回だけ計算してループしました

バッファ長と比較して安全に扱う

オーバーフローを事前に防ぐ

コピーや連結前にstrlenで長さを調べ、バッファに収まるか確認します。

C言語
#include <stdio.h>
#include <string.h>

int main(void) {
    char dest[8];
    const char *src = "hello"; // 5 文字

    if (strlen(src) >= sizeof(dest)) {
        printf("エラー: src が dest に収まりません\n");
        return 1;
    }
    // 収まるなら安全にコピー
    strcpy(dest, src); // 今回は事前検査済みなので OK
    printf("コピー成功: \"%s\"\n", dest);
    return 0;
}
実行結果
コピー成功: "hello"

動的確保時の必要サイズ

ヌル終端の1バイト分を忘れずに確保します。

malloc(strlen(src) + 1)が基本形です。

C言語
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    const char *src = "sample";
    size_t need = strlen(src) + 1; // +1 は '
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
const char *src = "sample";
size_t need = strlen(src) + 1; // +1 は '\0' の分
char *p = (char *)malloc(need);
if (!p) {
perror("malloc");
return 1;
}
strcpy(p, src);
printf("確保サイズ %zu でコピー: \"%s\"\n", need, p);
free(p);
return 0;
}
' の分 char *p = (char *)malloc(need); if (!p) { perror("malloc"); return 1; } strcpy(p, src); printf("確保サイズ %zu でコピー: \"%s\"\n", need, p); free(p); return 0; }
実行結果
確保サイズ 7 でコピー: "sample"

まとめ

strlenはヌル終端'\0'までのバイト数を返す関数です。

戻り値はsize_t%zuで表示し、<string.h>を必ずインクルードします。

ヌル終端の欠落、NULLポインタ、未初期化配列への適用は未定義動作であり、空文字は長さ0、改行やスペースも長さに含まれる点を押さえましょう。

また、UTF-8の日本語は「文字数」ではなく「バイト数」になるため要件に合わせたAPI選択が必要です。

実用ではループでの重複計算を避け、バッファサイズと比較して安全に扱う、動的確保では+1を忘れない、という基本を守れば、strlenを正しく安全に使いこなせます

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!