閉じる

【C言語】strcatの使い方入門|文字列の連結・結合をわかりやすく解説

C言語で文字列を扱うとき、複数の文字列を1つにまとめて扱いたい場面はとても多いです。

そうしたときに役立つ関数がstrcatです。

本記事では、strcatの基本から安全な使い方、実践的な活用例までを、図解とサンプルコードを交えながら丁寧に解説します。

初心者の方でも読み進めれば、文字列連結の基礎をしっかり身につけられる内容になっています。

strcatとは

strcatの基本的な役割と動作

strcatは、C言語の標準ライブラリに含まれる文字列連結(結合)用の関数です。

役割はとてもシンプルで、次のように説明できます。

  • 第1引数で渡した文字列の末尾に、第2引数の文字列を続けて書き足す関数です。
  • 文字列の終端を表す'\0'(ヌル文字)を自動的に付け直してくれます。
  • 返り値として、第1引数と同じポインタを返します。

もう少し具体的に動作を言い換えると、strcatは次の3ステップで動きます。

  1. 第1引数の文字列の中を先頭から順に調べて'\0'を見つける。
  2. その'\0'の位置から、第2引数の文字列を1文字ずつコピーしていく。
  3. 第2引数の終端'\0'までコピーし終えたところに、新しい'\0'を書き込む。

このように、第1引数のバッファの中身を書き換える点が重要です。

第1引数として、書き込みができない領域(リテラル文字列など)を渡すと、未定義動作になり非常に危険です。

strcatの関数プロトタイプとヘッダファイル

strcatを利用するためには、対応するヘッダファイルをインクルードする必要があります。

C言語標準でのプロトタイプは次のように定義されています。

C言語
#include <string.h>

char *strcat(char *dest, const char *src);

それぞれの引数と戻り値の意味は次のとおりです。

  • dest
    連結先となる文字列へのポインタです。既に有効なC文字列(終端'\0'付き)であることが前提です。この領域にsrcの内容が後ろに付け足されるため、十分なサイズを確保しておく必要があります。
  • src
    連結したい文字列へのポインタです。こちらも終端'\0'付きのC文字列である必要があります。srcの内容は変更されません
  • 戻り値
    destと同じポインタが返されます。連結結果をそのまま別の処理に渡したいときなどに便利です。

strcatとstrncatの違い

C言語では、文字列連結用の関数としてstrncatも用意されています。

名前が似ているため混同しがちですが、動作には重要な違いがあります。

C言語
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
  • strcat
    src全体を末尾まで'\0'も含めてコピーします。コピーされる文字数を呼び出し側で制限できないため、バッファオーバーフローを起こしやすいのが難点です。
  • strncat
    最大でn文字までsrcからコピーします。バッファサイズに合わせてコピー量を制限できるため、より安全な書き方がしやすい関数です。ただし「必ず安全」というわけではない点に注意が必要です。

違いのポイントは「呼び出し側がコピーする最大文字数を制御できるかどうか」です。

後半の章で詳しく説明しますが、現代的なコードではstrcatよりstrncatや他の安全な代替関数を使うことが推奨される場合が多いです。

strcatの基本的な使い方

文字列の連結(結合)の基本例

まずは、もっとも基本的な使い方から確認します。

次のようなシンプルな例です。

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

int main(void) {
    char buf[20] = "Hello";   // 連結先となるバッファ
    const char *add = " World"; // 連結したい文字列

    // bufの末尾にaddの内容を連結します
    strcat(buf, add);

    printf("%s\n", buf);  // 結果: "Hello World" が表示されます
    return 0;
}
実行結果
Hello World

この例では、bufという配列にあらかじめ"Hello"が入っています。

strcatを呼ぶとbufの末尾に” World”が追加され、最終的に"Hello World"という1つの文字列になります。

ここで重要なのは、bufのサイズを十分に大きく取っていることです。

buf[20]で20バイト確保しているので、

  • “Hello” → 5文字 + 終端'\0'で6バイト
  • ” World” → 6文字 + 終端'\0'で7バイト
    (実際には2つ目の'\0'は消費されます)

合計しても20バイト以内に収まるため、安全に連結できます。

末尾に文字列を追加するサンプルコード

実際には、1回だけでなく何度も末尾に追加していくケースも多くあります。

その使い方を確認してみましょう。

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

int main(void) {
    // 連結結果を格納するバッファ
    char message[64] = "C";

    // いくつかの文字列を順番に連結していきます
    strcat(message, " language");
    strcat(message, " strcat");
    strcat(message, " tutorial");

    printf("結果: %s\n", message);

    return 0;
}
実行結果
結果: C language strcat tutorial

このように、1つのバッファに対して連続してstrcatを呼び出すことができます。

ただし、そのたびに合計の長さがバッファ容量を超えていないかを意識しておく必要があります。

後ほど詳しく触れますが、ここを考えずにstrcatを使うとバッファオーバーフローの原因になります。

日本語(マルチバイト文字)とstrcatの注意点

strcatは「文字」ではなく「バイト列」を扱う関数です。

そのため、日本語のようなマルチバイト文字を扱うときには、次の点に注意が必要です。

  1. strcatは'\0'が出てくるまでバイトをコピーするだけであり、文字コードの中身は一切理解していません。
  2. そのため途中でマルチバイト文字を「分断」してしまうような誤った使い方をしなければ、基本的には「連結そのもの」は問題なく行われます。
  3. しかし、バッファサイズを計算するときに「1文字=1バイト」と思い込むと、すぐに実際のバイト数を超えてしまい、バッファオーバーフローを起こしやすくなります。

例として、UTF-8環境を想定したサンプルを見てみます。

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

int main(void) {
    // UTF-8環境を想定すると、「あ」は3バイト、「い」も3バイト程度になることが多いです。
    char buf[32] = "あ";      // "あ" のバイト列 + 終端'\0'
    const char *add = "い";   // こちらも "い" のバイト列

    strcat(buf, add);         // バイト列として単純に後ろに連結される

    printf("%s\n", buf);

    return 0;
}
実行結果
あい

この例のように、単純な連結であれば動き自体は問題ありません。

ただし、バッファサイズの計算だけは特に注意が必要です。

  • 「10文字の日本語だから10バイトで足りるだろう」と考えると確実にバッファ不足になります。
  • 実際には「1文字あたり何バイトか」「終端'\0'は1バイト必要」といった点も考慮する必要があります。

まとめると、strcatはマルチバイト文字でもバイト列として正しく終端'\0'がある限り、動作そのものは変わりません。

ただし、日本語環境では特に「バッファサイズの見積もり」に十分な注意が必要です。

strcat使用時の注意点と安全な書き方

バッファオーバーフローと危険性

strcatを使う際にもっとも重要な注意点は、バッファオーバーフローです。

バッファオーバーフローとは、確保した配列のサイズを超えてデータを書き込んでしまうことを指します。

strcatは、第1引数destのバッファサイズを「自分では」知りません。

そのため、

  • destにどれくらい空きがあるか
  • srcを連結しても安全かどうか

といった点は、すべて呼び出し側が自分でチェックしなければなりません

バッファオーバーフローが発生すると、次のような深刻な問題を引き起こします。

  • 他の変数の値を不正に上書きしてしまう。
  • プログラムが異常終了(クラッシュ)する。
  • 悪意ある攻撃者に悪用されると、任意コード実行などのセキュリティ脆弱性になり得る。

このような背景から、現代のC言語プログラミングでは、安易なstrcatの使用は推奨されないことが多くなっています。

strcatで必ず確認すべきバッファサイズ

strcatを安全に使うためには、呼び出す前にバッファに十分な空き容量があるかどうかを確認する必要があります。

考え方を式で表すと、次のようになります。

destの確保サイズ − 現在のstrlen(dest) − 1(終端'\0'のぶん) ≥ strlen(src)

ここでのポイントは、次の3つです。

  1. destの確保サイズ
    たとえばchar buf[32];なら、確保サイズは32バイトです。
  2. strlen(dest)
    これはdest現在の文字列長です。終端'\0'は含まれません。
  3. 終端’\0’の1バイト
    連結後の文字列にも'\0'が必要なので、その1バイト分を必ず残しておく必要があります。

具体例で確認してみます。

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

int main(void) {
    char buf[16] = "Hello";
    const char *add = " World";

    size_t buf_size = sizeof(buf);      // 16
    size_t len_now  = strlen(buf);      // "Hello" → 5
    size_t len_add  = strlen(add);      // " World" → 6

    // 空き容量 = 全体サイズ - 現在の長さ - 終端'\0'用
    size_t free_size = buf_size - len_now - 1;

    printf("buf全体: %zu, 現在の長さ: %zu, 空き容量: %zu, 追加する長さ: %zu\n",
           buf_size, len_now, free_size, len_add);

    if (free_size >= len_add) {
        strcat(buf, add);
        printf("連結結果: %s\n", buf);
    } else {
        printf("バッファ容量が不足しているため連結できません。\n");
    }

    return 0;
}
実行結果
buf全体: 16, 現在の長さ: 5, 空き容量: 10, 追加する長さ: 6
連結結果: Hello World

このように、strcatを呼ぶ前に「空き容量 ≥ 追加する文字列の長さ」を必ず確認する習慣を付けておくと、安全性がぐっと高まります。

strncatや安全な代替関数の活用方法

バッファオーバーフローの危険性を減らすために、strncatや安全な代替関数を活用することがよく行われます。

strncatでバイト数を制限する

strncatは、第3引数でコピーする最大文字数を指定できます。

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

int main(void) {
    char buf[16] = "Hello";
    const char *add = " World!!!";

    size_t buf_size = sizeof(buf);
    size_t len_now  = strlen(buf);
    size_t free_size = buf_size - len_now - 1; // 終端'\0'用の1バイトを残す

    // free_sizeを上限として連結する
    strncat(buf, add, free_size);

    printf("結果: %s\n", buf);

    return 0;
}
実行結果
結果: Hello World!!

この例では、” World!!!” を完全には連結できませんが、バッファ容量を超えない範囲で可能な分だけ連結しています。

ただし注意が必要なのは、strncat

  • 「第3引数にbuf全体のサイズを渡す」のではなく、
  • 残り空きサイズを渡す」必要がある

という点です。

ここを間違えると、依然としてバッファオーバーフローの危険があります。

さらに安全な代替関数(strlcat, strcat_sなど)

環境によっては、次のようなより安全な設計の関数を利用できる場合があります。

  • strlcat (BSD系、macOSなど)
  • strcat_s (C11 Annex Kや一部コンパイラ)

例として、strlcatのプロトタイプは次のようになっています。

C言語
size_t strlcat(char *dst, const char *src, size_t dstsize);

この関数は、

  • dstsize「バッファ全体のサイズ」を渡します。
  • 連結後も必ず'\0'で終端してくれます。
  • 戻り値として「本来必要だった長さ」を返すので、オーバーフローが起きたかどうか判定できます。

ただし、すべての環境で使えるわけではないため、使用前に自分の開発環境で利用可能かどうかを確認する必要があります。

ポインタと配列を使うときの注意点

strcatでは、第1引数destとして渡したポインタの指す先に対して書き込みが発生します。

そのため、「書き込み可能な領域」を必ず渡さなければなりません。

次のコードは典型的な誤りです。

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

int main(void) {
    // 文字列リテラルへのポインタ(多くの処理系で読み取り専用)
    char *msg = "Hello";

    // ここでstrcatを呼ぶのは非常に危険 (未定義動作)
    strcat(msg, " World");  // NG

    printf("%s\n", msg);
    return 0;
}

文字列リテラルは多くの場合、読み取り専用の領域に配置されます。

そこに書き込もうとすると、クラッシュを含む未定義動作を招きます。

正しい書き方は、書き込み可能な配列を用意することです。

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

int main(void) {
    // 書き込み可能なバッファを確保し、そこに"Hello"を初期値として格納
    char msg[32] = "Hello";

    strcat(msg, " World");  // OK: msgは書き込み可能

    printf("%s\n", msg);
    return 0;
}

ポインタを使用する場合も、mallocで確保したメモリなど自分で確保した書き込み可能な領域を指すようにする必要があります。

実践的なstrcat活用例

動的にメッセージ文字列を組み立てる

現実のプログラムでは、状況に応じてメッセージを動的に生成したい場面があります。

例えば、ユーザー名や数値などを組み込んだ説明文を作るケースです。

ここでは、固定長バッファを使いつつ、strcatでメッセージを組み立てる例を示します。

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

int main(void) {
    char message[128] = "";   // 結果を格納するバッファ(十分なサイズを確保)
    const char *name = "Taro";
    int score = 85;

    // ひな型となるメッセージをまずコピー
    strcat(message, "Hello ");
    strcat(message, name);
    strcat(message, " さん、\n");

    // スコアに応じて一部のメッセージを変更
    if (score >= 80) {
        strcat(message, "テストの結果はとても良好です。");
    } else if (score >= 60) {
        strcat(message, "テストの結果はまずまずです。");
    } else {
        strcat(message, "テストの結果は改善の余地があります。");
    }

    strcat(message, "\nスコア: ");

    // 数値を文字列に変換して連結
    char buf[16];
    snprintf(buf, sizeof(buf), "%d", score); // snprintfで安全に整数を文字列化
    strcat(message, buf);
    strcat(message, " 点です。\n");

    printf("%s", message);

    return 0;
}
実行結果
Hello Taro さん、
テストの結果はとても良好です。
スコア: 85 点です。

この例では、

  • 静的な文字列部分にはstrcatを、
  • 数値部分はsnprintfで文字列に変換してからstrcatで追加する、

という組み合わせでメッセージを組み立てています。

実際のアプリケーションでも、このような手法は非常によく使われます。

ファイルパスやURL文字列の連結

strcatは、ファイルパスやURLを組み立てる場面でもよく使われます。

例えば、「ベースディレクトリ」と「ファイル名」を連結してフルパスを作るような処理です。

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

int main(void) {
    char base[64] = "/home/user";  // ベースディレクトリ
    const char *filename = "data.txt";

    char path[128];

    // まずbaseをpathにコピー
    strcpy(path, base);

    // 末尾が'/'で終わっていなければ付け足す
    if (path[strlen(path) - 1] != '/') {
        strcat(path, "/");
    }

    // ファイル名を連結
    strcat(path, filename);

    printf("ファイルパス: %s\n", path);

    return 0;
}
実行結果
ファイルパス: /home/user/data.txt

同じように、URLを組み立てるときにもstrcatは利用できます。

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

int main(void) {
    char url[256] = "https://example.com";
    const char *path = "/api/v1/resource";
    const char *query = "?id=123&format=json";

    strcat(url, path);
    strcat(url, query);

    printf("URL: %s\n", url);

    return 0;
}
実行結果
URL: https://example.com/api/v1/resource?id=123&format=json

このように、「ベース」+「パス」+「クエリ」といった具合に、部分ごとに分かれている情報を順番に連結して1本の文字列にまとめる処理にstrcatは向いています。

ただし、ここでもやはりバッファサイズを十分大きく取ることが重要です。

ループで複数の文字列を順に結合する

複数の文字列をループで順番に連結して、1本の文字列にまとめたいというケースもよくあります。

例えば、カンマ区切りのリストを作る場面です。

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

int main(void) {
    const char *fruits[] = { "apple", "banana", "orange" };
    size_t count = sizeof(fruits) / sizeof(fruits[0]);

    char result[128] = "";  // 結果を入れるバッファ

    for (size_t i = 0; i < count; i++) {
        if (i > 0) {
            // 2番目以降の要素の前には区切り文字を付ける
            strcat(result, ", ");
        }
        strcat(result, fruits[i]);
    }

    printf("フルーツ一覧: %s\n", result);

    return 0;
}
実行結果
フルーツ一覧: apple, banana, orange

このコードでは、

  • 最初の要素の前には区切り文字を付けず、
  • 2番目以降の要素の前にだけ", "を追加する、

というロジックをループの中で実現しています。

このように、strcatは「リストや配列の内容を1つの文字列にまとめる」処理とも相性が良い関数です。

ただし、要素数や各要素の長さによっては結果の長さが大きくなりやすいため、resultのバッファサイズには十分な余裕を持たせておく必要があります。

まとめ

strcatは、C言語で文字列を末尾に連結するための基本的な関数です。

使い方自体はシンプルで、1つのバッファに対して複数の文字列を順に結合していくことができます。

しかし、バッファサイズの管理と書き込み先の領域の正当性を常に意識しなければ、バッファオーバーフローや未定義動作を引き起こす危険が高い関数でもあります。

実務的には、strncatや環境が提供する安全な代替関数と組み合わせたり、事前にstrlensizeofでサイズを確認したうえで使うことが重要です。

この記事で紹介した例や注意点を踏まえれば、strcatをより安全かつ効果的に活用できるようになります。

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

URLをコピーしました!