C言語 strncatの使い方

この記事のポイント

  • strncat関数の基本概念と安全性向上のポイント
  • strcat関数との違いとバッファオーバーフロー防止策
  • 文字列長の計算と適切な制限値の設定方法
  • 多段連結操作における安全性の保証テクニック
  • 実用的なコード例と一般的なエラーパターンの回避策

目次

C言語のstrncatとは?

C言語のstrncat関数は、文字列連結の操作を安全に行う目的に設計された標準ライブラリ関数です。

strncat関数の基本機能は、通常のstrcat関数と同様に、文字列をつなぎ合わせることです。ただしstrncat関数には、最大コピー文字数を明示的に指定できる特徴があります。この制限は、プログラム内で起こるバッファオーバーフローの大幅なリスク軽減や、より安全なプログラミングで非常に役立つものです。

strncat関数はプログラム内で、第一引数の文字列(デスティネーション)の末尾に対して、第二引数の文字列(ソース)を、第三引数で指定された最大文字数まで追加します。

そこで重要となるのは、この関数が常に結果の文字列をNULL終端することです。これは、strcat関数にも共通する特徴となります。具体的には、デスティネーション文字列の終端NULL文字を見つけ、その位置からソース文字列を最大n文字コピーし、最後に新しいNULL文字を追加する形です。

strncat関数には、注意すべき点もあります。

strncat関数で指定するn値は、追加する最大文字数であり、デスティネーション配列の総サイズではありません。適切なn値を計算するには、デスティネーション配列のサイズから、現在の文字列長さと新しいNULL終端のための1バイトを差し引いた値を使用することが必要です。

また、strncat関数は、同じく文字列操作で使用するstrcpy関数とは異なり、常にNULL終端を追加するものです。こうした特徴があるなかでも、デスティネーションバッファを十分な大きさにすべき責任はプログラマー側にあります。ソース文字列がn文字未満の場合、NULL文字に達した時点でコピーが停止するため、指定したn文字全てがコピーされるわけではありません。

strncat関数には注意点が多いものの、適切に使用すれば文字列操作の安全性を高める強力なツールとなります。特にユーザー入力や、外部データを扱うセキュリティが重要なアプリケーションで活躍するでしょう。

【関連】
C言語をもっと詳しく学ぶならpaizaラーニング

基本構文

strncat関数の基本構文は、以下のとおりです。

char *strncat(char *dest, const char *src, size_t n);

この関数は、srcからdestへ最大n文字を連結します。destは元の文字列を含む配列で、srcはdestの末尾に追加される文字列、nはsize_t型で追加する最大文字数をあらわします。戻り値はdestポインタです。

strncat関数との違いを見るために、strcatの構文も示しておきましょう。

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

これらの関数を使う際には、string.hヘッダーファイルをインクルードする必要があります。基本的な使用例は以下のとおりです。

#include <string.h> char destination[50] = "Dog and "; const char *source = "Cat and Hamster"; strncat(destination, source, 4); // 最大4文字追加 // Result: destination = "Dog and Cat"

上記の例では、destination文字列の末尾にsource文字列から最大4文字を追加しています。この場合、「Cat」までが追加され、「and Hamster」は追加されません。strncat関数には、結果の文字列に常にNULL終端を追加する特徴があります。プログラムの安全性を高めるうえでも有効でしょう。

実用例

このセクションでは、動物を用いたわかりやすいシナリオのなかで、strncat関数の基本や実用的なコードパターンを紹介していきます。

アプリケーション開発などで適材適所の関数を使うためには、自分のプログラムに実装してみる「経験」が不可欠です。

実際のコードに触れると、想定外の動作やエラーへの対処をする過程で、strncat関数ならではの特徴や、ほかの文字列操作の関数との違いが見えてくることもあるでしょう。

このセクションで紹介するコード例は、実際のプログラムで使えるものばかりです。strncat関数を完全マスターするためにも、実用的なコードをぜひ活用してください。

基本的な制限付き連結

以下のコードパターンは、strncat関数の最も基本的な使用例です。

この例では、「Dog and」という文字列に「Cat and Hamster」から最初の3文字(「Cat」)だけを連結しています。そこでstrncat関数を使うと、第三引数で指定された最大文字数(この場合は3)までソース文字列をコピーし、必ず結果にNULL終端を追加します。

この機能は、元の文字列に一部だけを連結する細かい制御を可能とします。バッファサイズ(50バイト)は両方の文字列とNULL終端を格納するのに十分な大きさを確保しています。バッファオーバーフローを防ぐうえでも役立つ関数です。

#include <stdio.h> #include <string.h> int main() { char destination[50] = "Dog and "; const char *source = "Cat and Hamster"; strncat(destination, source, 3); // 最大4文字追加 printf("Result: %s\n", destination); // 出力確認 return 0; }

出力結果:

結果: Dog and Cat

バッファ容量を考慮した連結

以下のコードパターンは、バッファオーバーフローを確実に防ぐために、バッファの残り容量を計算して連結するテクニックです。

このコード例では、現在の文字列長をstrlen関数で取得し、バッファの総サイズから差し引いて残りの容量を計算しています。そこからNULL終端のための1バイトを引いた値をstrncat関数の第三引数として使用することで、バッファの境界を超えることなく安全に連結可能となります。この手法は、特にバッファサイズが固定されている場合や、複数回の連結操作を行う場合に重要です。

#include <stdio.h> #include <string.h> int main() { char buffer[10] = "Horse"; size_t space = sizeof(buffer) - strlen(buffer) - 1; strncat(buffer, "ZebraHippo", space); printf("安全連結: %s\n", buffer); return 0; }

出力結果:

安全連結: HorseZebr

部分文字列の連結

以下のコードパターンは、ソース文字列の特定の部分だけを連結するテクニックを示したものです。strncat関数とポインタ演算を組み合わせることで、文字列の中間部分だけを抽出して連結することが可能となります。

以下のコードで行っているのは、「ElephantGiraffeLion」という文字列の6文字目から始まる9文字(「ntGiraffe」)だけを連結するというものです。このテクニックは、特定のフォーマットのデータから必要な部分だけを取り出して新しい文字列を構築する場合などに役立ちます。strncat関数の柔軟性を示す良い例でしょう。

#include <stdio.h> #include <string.h> int main() { char src[] = "ElephantGiraffeLion"; char dest[20] = "Big "; strncat(dest, src + 6, 9); printf("部分連結: %s\n", dest); return 0; }

出力結果:

部分連結: Big ntGiraffe

複数文字列の安全な連結

以下のコードパターンでは、複数の文字列を安全に連結する方法を示しています。strncat関数を連続して使う場合、各ステップでバッファの残り容量を再計算することが重要です。

以下では、3つの動物名を連結しています。ここでポイントになるのは、各連結の前に、現在の文字列長に基づいて残りのスペースを計算している点です。このテクニックによって、どの連結操作でもバッファオーバーフローが発生しないことが保証されます。実際のアプリケーション開発をするうえでは、このような動的な計算が安全性の鍵となるでしょう。

#include <stdio.h> #include <string.h> int main() { char result[30] = ""; strncat(result, "Lion", sizeof(result) - 1); strncat(result, "Tiger", sizeof(result) - strlen(result) - 1); strncat(result, "Bear", sizeof(result) - strlen(result) - 1); printf("%s\n", result); return 0; }

出力結果:

LionTigerBear

NULL終端の保証

以下のコードパターンでは、strncat関数がNULL終端を確実に追加することを示しています。このコード内でstrncat関数は、指定した最大文字数まで連結したあと、必ず結果の文字列にNULL終端を追加します。これは、文字列コピーで使われるstrncpy関数とは対照的な特徴です。

strncpy関数の場合、必ずしもNULL終端を追加しません。一方でstrncat関数を用いた以下の例では、デバッグのためにバッファの各バイトを表示し、連結後に文字列がきちんとNULL終端されていることを確認しています。この特性はstrncat関数における安全性の高さを示すものでしょう。

#include <stdio.h> #include <string.h> int main() { char buf[10] = "Croc"; strncat(buf, "Whale", 6); printf("文字列: %s\nバイト: ", buf); for (int i = 0; i < 10; i++) { printf("%d ", buf[i]); } return 0; }

出力結果:

文字列: CrocWhale
バイト: 67 114 111 99 87 104 97 108 101 0

strncat vs strcat の安全性比較

以下のコードパターンは、strncat関数とstrcat関数の安全性の違いを示したものです。小さなバッファに対して長い文字列を連結する場合、strcat関数では、単純にバッファを超えた書き込みを続けてしまうことで、バッファオーバーフローを引き起こします。

一方でstrncat関数を使った場合、最大文字数を制限することで、バッファの境界を超えないようにすることが可能です。

以下のコード例では、実際のオーバーフローは発生させず、各関数の動作の違いを説明しています。セキュリティ意識の高いプログラミングでは、strncat関数の利点が明確にあらわれてくるでしょう。

#include <stdio.h> #include <string.h> int main() { char safe[10] = "A"; // char unsafe[10] = "A"; // 危険な操作(ここではコメントアウト) // strncat: 安全に最大8文字追加 strncat(safe, "BCDEFGHIJKLMN", sizeof(safe) - strlen(safe) - 1); // strcat: // 危険な操作(ここではコメントアウト) // strcat(unsafe, "BCDEFGHIJKLMN"); printf("安全: %s\n", safe); return 0; }

出力結果:

安全: ABCDEFGHI

ユーザー入力の安全な処理

以下のコードパターンは、ユーザー入力を安全に処理するものです。

外部からの入力データは、基本的に長さが予測できないケースが多いです。この理由から、外部データはバッファオーバーフローの主要なリスク源になりえます。

以下のコードで行っているのは、固定サイズのバッファに対してユーザー入力を連結する際に、strncat関数を使用して安全な処理を行う操作です。バッファの残りスペースを正確に計算し、その範囲内でのみ連結を行うことで、悪意ある入力や単純なミスからプログラムを保護できます。実際のアプリケーションでは、このテクニックの活用が重要なセキュリティ対策につながってくるでしょう。

#include <stdio.h> #include <string.h> int main() { // 実演用(実際はキーボード入力等で取得) char input[] = "Giraffe"; char buffer[20] = "Animal: "; strncat(buffer, input, sizeof(buffer) - strlen(buffer) - 1); printf("安全な入力処理: %s\n", buffer); return 0; }

出力結果:

安全な入力処理: Animal: Giraffe

繰り返し連結のパフォーマンス最適化

以下のコードパターンで示しているのは、繰り返し連結を行う場合のパフォーマンス最適化手法です。

多数の文字列を連結する場合にstrncat関数を毎回呼び出した場合、関数は毎回デスティネーション文字列全体をスキャンして終端を見つける必要がでてきます。これはとても効率が悪い操作です。

この問題を防ぐために、以下のコードパターンでは、ポインタを使って現在の文字列の終端位置を追跡し、strcpy関数と組み合わせることで無駄なスキャンを省いています。

この最適化は、長い文字列や頻繁な連結操作を行うプログラムで特に役立つものです。ただし、バッファオーバーフローを防ぐための計算は依然として必要となります。注意しましょう。

#include <stdio.h> #include <string.h> int main() { char buffer[30] = "Hippo"; char *end = buffer + strlen(buffer); size_t space_left = sizeof(buffer) - strlen(buffer) - 1; strncpy(end, "Rhino", space_left); end += strlen("Rhino"); space_left -= strlen("Rhino"); printf("最適化連結: %s\n", buffer); return 0; }

出力結果:

最適化連結: HippoRhino

まとめ

C言語のstrncat関数は、文字列連結を安全に行ううえで不可欠なツールです。

strncat関数には通常のstrcat関数とは異なり、コピーする文字数を制限できる機能があります。これは、バッファオーバーフローのリスクを大幅に軽減するうえで役立つ特徴でしょう。特にユーザー入力や外部データを扱うセキュリティ重視のアプリケーションでは、必須の機能となります。

strncat関数を効果的に使用するためには、バッファサイズと現在の文字列長を常に把握し、残りのスペースを正確に計算することが重要です。また、複数回の連結操作を行う場合は、各ステップでバッファの残り容量を再計算するか、ポインタを使用したパフォーマンス最適化の検討が必要となります。

なお、strncat関数は常にNULL終端を追加する特徴から、結果が常に有効な文字列となることが保証されている利点もあります。適切に使用すれば、信頼性の高い文字列処理が実現し、プログラムの安全性と堅牢性が向上するでしょう。

レベルを更に上げたい方はpaizaプログラミングスキルチェックへ

  1. paizaラーニングトップ
  2. リファレンス
  3. C言語のリファレンス記事一覧
  4. C言語 strncatの使い方