strcat関数とは
strcat関数は、C言語で2つの文字列をつなぎ合わせるための標準ライブラリ関数です。
プログラムを作っていると、「2つの文字列を1つにまとめたい」という場面がよく出てきます。
例えば、「こんにちは、」と「ウサギ」をつなげて「こんにちは、ウサギ」という文字列を作りたいときです。
そういった文字列操作を行いたいときに使うのがstrcat関数です。
strcatの基本概念から具体的な処理の流れ、そして安全に使用するための仕組みについてくわしく見ていきましょう。
strcatの概要
strcat関数は「string concatenation(文字列連結)」の略です。
基本的な書き方は次の通りです。
各要素の意味を確認しておきましょう。
- 連結先:最初の文字列が入っている配列(ここに文字列が追加される)
- 連結元:追加したい文字列
連結先の文字列の末尾に、連結元の文字列が追加されます。連結先の配列は、連結後の文字列全体を格納できる十分な大きさが必要です。
また、strcat関数を使うには、string.hというヘッダーファイルをインクルードする必要があります。
出力結果
ネコイヌstrcatが行う処理の流れ
strcat関数は、内部で次のような処理を行っています。
処理の手順
- 第1引数(連結先)の文字列の終端文字(\0)を探す
- 終端文字の位置から、第2引数(連結元)の文字を1文字ずつコピーする
- 最後に新しい終端文字を配置する
この仕組みを理解しておくと、strcatがどのように動作するかがわかります。
出力結果
連結結果: ハムスターライオンまず「ハムスター」の終端文字(\0)を見つけます。その位置から「ライオン」の文字を1文字ずつコピーしていき、最後に新しい終端文字を配置して完了という流れです。
終端文字(\0)は、制御文字の一種で、文字列の終わりを示す「信号」としての役割しか持っていません。そのため、私たちが見ている文字列の表示としては確認できないというわけです。
内部の処理としては、文字列の終わりを示す目印であるということを認識しておきましょう。
文字列連結の仕組みと終端文字(null文字)の役割
C言語の文字列には、必ず最後に終端文字(\0)が付いています。
strcat関数は、この終端文字を目印にしてどこから文字を追加すればいいかを判断します。終端文字がないと、どこまでが文字列かわからないため、正しく動作しません。
例えば、「ABC」という文字列は、メモリ上で次のように格納されています。
strcatは、この\0を見つけて、その位置から新しい文字列を追加するのです。
出力結果
パンダブタ「パンダ」の後に終端文字\0がありますね。その後ろに「余分な文字」がありますが、strcatは終端文字までしか見ません。
そのため、「パンダ」の終端文字の位置から「ブタ」が追加され、「余分な文字」は無視された出力結果になっています。
strcatの使い方と実例コード
ここからは、strcat関数を実際のプログラムで使う方法を学んでいきます。
基本的な文字列の連結から、複数の文字列を組み合わせる方法や文字列の間にスペースを入れるテクニックなど、実用的な例を通して理解を深めていきましょう。
これらの使い方をマスターすれば、日常的なプログラミングで文字列を自由に扱えるようになります。
基本的な使用例
最も基本的なstrcatの使い方は、2つの文字列を単純につなぎ合わせることです。
重要なのは、連結先の文字列(第1引数)は連結後の文字列全体を格納できる十分な大きさを持つ配列が必要な点です。
例えば、挨拶と動物名を組み合わせてメッセージを作ってみましょう。
出力結果
こんにちは、ウサギmessage配列は50バイトの大きさです。
「こんにちは、」は約18バイトで「ウサギ」は約9バイトです。合計で約27バイト必要ですが、余裕を持って50バイト確保しています。
このように、連結後の文字列より大きなサイズを用意するのが重要です。
複数の文字列を連結する例
3つ以上の文字列をつなぎ合わせたい場合は、strcatを複数回呼び出します。
1回目の連結結果に対して、さらに文字列を追加していくことで、長い文章を段階的に組み立てることができます。
この方法は、動的にメッセージを作りたいときに非常に便利です。
実際の例を見てみましょう。
出力結果
昔々、カメとウマがいました。プログラムの流れを確認しましょう。
- 最初に「昔々、」がstoryに入っている
- 1回目のstrcat:「昔々、」+「カメ」=「昔々、カメ」
- 2回目のstrcat:「昔々、カメ」+「と」=「昔々、カメと」
- 3回目のstrcat:「昔々、カメと」+「ウマ」=「昔々、カメとウマ」
- 4回目のstrcat:「昔々、カメとウマ」+「がいました。」=「昔々、カメとウマがいました。」
このように、strcatは何度でも呼び出すことができます。呼び出すたびに、前の連結結果に新しい文字列が追加されていきます。
文字列間にスペースを挿入する例
文字列をつなぎ合わせる際に、適切なスペースや区切り文字を入れることで、読みやすい文章を作ることができます。
読みやすくするために、区切り文字を入れるようなケースで有用です。
実際の例を見てみましょう。
出力結果
Hello Elephantスペースを挟むことで、単語が適切に区切られます。
特に英語の文章では、単語と単語の間にスペースが必要なので、重宝するテクニックです。
ちなみに、スペースを変数にせず、直接文字列として指定することもできるので、覚えておきましょう。
strcatを使う際の注意点とエラー対策
strcatは便利な関数ですが、間違った使い方をすると深刻な問題を引き起こす可能性があります。
特にメモリ管理に関する問題は、プログラムのクラッシュやセキュリティ上の脆弱性につながるため、事前に対策を知っておくことが非常に大切です。
ここでは、実際に発生しやすい問題とその解決方法についてくわしく解説します。
バッファオーバーフローの危険性
strcatの最も危険な問題は、バッファオーバーフローです。
バッファオーバーフローとは、連結先の配列のサイズを超えて文字列を書き込んでしまうエラーです。
これにより、他のメモリ領域が破壊され、次のような深刻な問題が発生します。
- プログラムが異常終了(クラッシュ)する
- データが壊れる
- セキュリティ上の脆弱性が生まれる(悪意のあるコードが実行される可能性)
例えば、10バイトの配列に20バイトの文字列を連結しようとした場合、配列の範囲を超えてメモリに書き込んでしまいます。
出力結果
バッファサイズ不足のため連結を中止small_bufferは10バイトしかありません。「ゾウ」は6バイト、「キリンカバライオン」は27バイトなので、合計で33バイト必要です。
安全のために、余裕を持ったサイズの配列を用意することが重要です。連結する前に、必ず配列のサイズが十分かを確認しましょう。
一般的に、上記のようなコードは絶対に実行しないようにしましょう。
終端文字(null文字)の不足による不具合
文字列の終端文字(\0)が欠けていると、strcatはどこまでが文字列かを判断できません。それが原因となって、さまざまな問題につながりかねません。
例えば、次のようなことが起こります。
- strcatは終端文字を見つけるまでメモリを読み続ける
- 予期しない場所まで処理を続ける
- プログラムの動作が不安定になる
- デバッグが非常に困難になる
こういった問題を防ぐには、配列を初期化する際に終端文字が正しく配置されることを確認する必要があります。
出力結果
正常に連結: サルヒツジchar proper_string[20] = "サル";のように初期化すると、自動的に終端文字が追加されます。
もし、配列を手動で1文字ずつ設定する場合は、最後に必ず\0を追加する必要があります。
例えば、このようなコードです。
このように、配列の各要素に一文字ずつ文字(シングルクォート ' ' で囲む)を設定する場合、コンパイラは文字列だと判断せず、終端文字を自動では追加してくれません。
もし\0を入れ忘れると、strcatやprintfは文字列の終わりを見失い、メモリ上の関係ないデータまで読み込んでしまったり、プログラムがエラーで停止したりする原因になります。
注意点として覚えておきましょう。
十分なバッファ確保の重要性
安全にstrcatを使用するためには、連結後の文字列全体を格納できる十分なサイズのバッファを用意する必要があります。バッファサイズは連結する全ての文字列の長さと終端文字分を合計した値以上である必要があります。余裕を持ったサイズを確保することで安全性が向上します。
安全にstrcatを使うには、連結後の文字列全体を格納できる十分なサイズの配列を用意する必要があります。
必要なサイズを計算すると、次のようになります。
配列のサイズ ≥ 連結先の文字列の長さ + 連結元の文字列の長さ + 1(終端文字の分)
バッファサイズは連結する全ての文字列の長さと終端文字分を合計した値以上である必要があります。安全のため、余裕を持ったサイズを確保するべきです。
出力結果
小さなリスと大きなクマ少なくとも、実際に必要なサイズの2倍程度を確保しておくと、安全性が高まります。
目安として押さえておきましょう。
strcat_sの使い方
C11標準で追加されたstrcat_s関数は、バッファサイズを指定することで、バッファオーバーフローを防ぐ、より安全な関数です。
基本的な書き方は、次の通りです。
strcat_sには次のようなメリットがあります。
- バッファサイズを明示的に指定します
- サイズを超える連結を試みた場合、エラーを返します
- メモリ破壊を防げます
セキュリティを重視する場面で有効です。
実際の例を見てみましょう。
出力結果
安全に連結: トラシカサイズを超える場合はエラーが返され、安全に処理を中断できます。
ポインタとstrcatの関係
strcatの動作を深く理解するには、ポインタとの関係を知ることが欠かせません。
C言語における文字列操作は、本質的にポインタ操作です。strcatも内部でポインタを使用して文字列を処理しています。
ここでは、strcatがどのようにポインタを活用しているか、そしてポインタの扱い方によってどのような問題が発生するかを学びましょう。
strcatの内部処理とポインタの動き
strcatは、内部でポインタを使って効率的に文字列を連結しています。
内部処理の流れは次のようなイメージです。
- 連結先のポインタを、終端文字(\0)が見つかるまで1文字ずつ進める
- 終端文字の位置で止まる
- その位置から、連結元の文字を1文字ずつコピーする
- 最後に新しい終端文字を配置する
このように、ポインタを移動させながら文字列を処理することで、効率的な連結が実現されています。
出力結果
連結結果: 森のオオカミ文字列配列とポインタの違い
C言語で文字列を扱う際、char str[]として宣言する方法と、char *strとして宣言する方法があります。
この2つは似ていますが、strcatでの使い方が大きく異なります。
出力結果
クジラが泳ぐこのコード例を理解するために、配列とポインタの違いを確認しておきましょう。
配列として宣言した場合(変更可能)
のように宣言すると、変更可能な文字列配列が作られます。strcatの連結先として使用できます。
ポインタとして宣言した場合(変更不可能)
のように宣言すると、文字列リテラル(プログラム中に直接書かれた文字列)を指すポインタになります。
文字列リテラルは読み取り専用のメモリ領域にあるため、strcatで連結先として使用すると実行時にエラーが発生します。
array_strは内容を変更できるので、strcatの連結先として使うことができます。
pointer_strは文字列リテラルを指すポインタなので、内容を変更できません。そのため、もしstrcat(pointer_str, addition);を実行しようとすると、プログラムがクラッシュします。
strcatの連結先には、必ず変更可能な配列を使いましょう。
ちなみに、ポインタは連結元としてであれば、読み取るだけなので使うことができます。
ポインタ操作ミスによるエラー例
初心者がよく犯すポインタ関連のミスには、次のようなものがあります。
- 初期化されていないポインタを使う
- 不正なメモリ領域を指すポインタを使う
- 動的メモリのサイズを間違える
こういったミスは、プログラムのクラッシュや予期しない動作の原因となります。
出力結果
ペンギンの行進malloc(50)を使って、50バイトのメモリを動的に確保しています。
確保したメモリ領域にstrcpyで「ペンギン」という文字列を設定した後、strcatを使って「の行進」を追加しています。
また、重要なのは動的に確保したメモリは使い終わったら必ずfreeで解放する必要があることです。これを忘れると、メモリリークという問題が発生してしまいます。
メモリリークとは、使われなくなったメモリが解放されず、プログラムが使用できるメモリがどんどん減っていく問題のことです。
strcatを使わずに文字列を結合する方法
strcatには安全性の問題があるため、より安全で制御しやすい別の方法を知っておくことが大切です。
代替手法を使うことで、バッファオーバーフローのリスクを減らし、より堅牢なプログラムを作成できる可能性があります。
用途に応じて適切な方法を選択することで、効率的かつ安全な文字列操作ができるようになりましょう。
strncatによる安全な連結
strncat関数は、strcatよりも安全だといえる特徴があります。
strcatとの最大の違いであるその特徴は、連結する文字数の上限を指定できる点です。第3引数で最大連結文字数を指定することで、バッファオーバーフローを防ぐことができます。
出力結果
コアラとカンガルーsizeof(buffer) - strlen(buffer) - 1という計算で、連結できる最大文字数を求めています。配列の中がどれだけ空いているかを知るための計算です。
sizeof(buffer):配列全体のサイズ(40バイト)
strlen(buffer):現在の文字列の長さ
最後の-1:終端文字
この計算により、配列のサイズを超えないように安全に連結できます。
strncatは終端文字を自動的に追加するため、配列のサイズから1を引いた値を指定する点に注意が必要です。
自作関数で文字列を連結する方法
自分で文字列連結関数を作ることで、処理の流れを完全に制御できます。
for文やwhile文を使って独自の連結関数を作成すると、連結処理の各段階を詳細に管理でき、特殊な要件にも対応できます。また、文字列操作の仕組みを深く理解するのにも役立ちます。
出力結果
アザラシとセイウチこのプログラムでは、safe_concatという関数を作って、文字列を安全に連結しています。文字列を安全に連結するための自作関数です。
第1引数(dest)が連結先、第2引数(src)が連結元、第3引数(max_size)が配列の最大サイズを表しています。
大まかなプログラムの流れを確認しましょう。
- まず、連結先の文字列(dest)の長さをstrlenで調べます
- 次に、for文で連結元の文字列(src)を1文字ずつコピーしていきます
- コピー中、配列のサイズ(max_size)を超えないように条件をチェックします
- 最後に、文字列の終わりを示す終端文字(\0)を配置します
初めは難しく感じるかもしれませんが、このような安全な処理を意識することで、より信頼性の高いプログラムが書けるようになります。
sprintfによる文字列結合の応用
sprintf関数を使うと、複数の文字列や数値をまとめて1つの文字列にできます。
例えば、「動物の名前は〇〇で、年齢は△歳です」のように、文字と数字を組み合わせた文章を作りたいときに便利です。
printfは画面に表示する関数ですが、sprintfは文字列として保存する関数だと考えると、わかりやすいでしょう。
複数の情報を1つの文にまとめたいときや、後で使うために文字列を作っておきたいときに活躍します。
出力結果
キツネとタヌキが3匹いますsprintf関数は、printf関数と同じ書式指定子が使えます。書式指定子とは、%から始まる特別な記号のことで、どんな種類のデータを表示するかを指定します。
よく使う書式指定子は、次の3つです。
- %d:整数(10進数の数値)を表示する
- %c:1文字だけを表示する
- %s:文字列(複数の文字)を表示する
この例では、%sで文字列(animal1とanimal2)、%dで整数(count)を指定して、複数の要素を組み合わせた文字列をresult配列に格納しています。
ただし、sprintf関数にも注意点があります。
strcatと同じように、バッファサイズをチェックしないため、結果が配列のサイズを超えないように気を付けましょう。
よくある質問(Q&A)
Q: strcatで文字列が正しく連結されません
A: 連結先の文字列が正しく初期化されていない可能性があります。char配列を宣言時に初期化するか、strcpyで最初の文字列を設定してからstrcatを使用してください。
Q: strcatの戻り値は何に使えますか?
A: strcatは連結先文字列のポインタを返すため、連続した関数呼び出しや他の文字列関数との組み合わせに活用できます。ただし、可読性を考慮して適度に使用することをおすすめします。
Q: 数値をstrcatで連結できますか?
A: strcatは文字列専用のため、数値を直接連結することはできません。sprintfやitoaなどで数値を文字列に変換してから連結するか、最初からsprintfを使用して組み合わせる方法があります。
Q: strcatは元の文字列を変更しますか?
A: はい、strcatは第1引数の文字列を直接変更します。元の文字列を保持したい場合は、事前に別の変数にコピーを作成してから連結処理を行ってください。
まとめ
C言語のstrcat関数は、2つの文字列を連結するための基本的な関数です。
この記事では、strcatの基本的な使い方から、実際に使う際の注意点、そして安全な代替手法まで解説してきました。
strcatが特に有用なのは、次のような場面です。
strcatが活躍する場面
- 複数の文字列を組み合わせてメッセージを作成したいとき
- 動的に文字列を組み立てたいとき
- 既存の文字列に新しい情報を追加したいとき
- ファイルパスや文章を段階的に構築したいとき
最後に、この記事の重要なポイントをおさらいしましょう。
重要なポイント
- strcat関数は連結先の文字列の末尾に、連結元の文字列を追加する
- バッファオーバーフローの危険性があるため、十分なサイズの配列を用意する
- 終端文字(NULL文字、\0)が正しく配置されていないと正常に動作しない
- 文字列リテラルを指すポインタは連結先として使えない
- 安全な代替手法として、strncat、strcat_s、sprintf、自作関数などがある
strcat関数を正しく理解することで、C言語の文字列操作とメモリ管理の基礎が身に付くのは間違いありません。しかし、実際の開発ではより安全な代替手法を使うことが推奨されます。strcatの仕組みを学ぶことは重要ですが、実用する際は安全性を最優先に考えましょう。
今回は、strcatを中心に取り上げましたが、この知識を土台として今後は、strncatやsnprintfといった配列のサイズを意識した安全な関数へステップアップしていきましょう。