C言語の文字列を連結する関数
C言語 では、文字列の連結には strcat 系の関数を利用します。もし使えるのであれば、strlcat や strcat_s を使うのが望ましいです。
読み方
- strcat
- えすてぃーあーるきゃっと、すとらきゃっと、すとりんぐきゃっと
- strncat
- えすてぃーあーるえぬきゃっと、すとらえぬきゃっと、すとりんぐえぬきゃっと
目次
概要
C言語では文字列の連結に
- strcat
- strncat
がよく利用されます。 それ以外には、printf ようにフォーマット指定子が利用できる
- vsprintf
- vsnprintf
も利用されます。
単純な文字列連結であれば、 strcat 系 を利用し、複雑な連結は vsnprintf を利用すると良いでしょう。 strncat のサイズ指定は、コピーするサイズのことなので、安全に使うためには注意が必要です。
strcat/strncat は、気を付けていないと、バッファオーバーランを引き起こしてしまうことがあります。バッファオーバーランとは、設計者の意図しないメモリ領域の破壊が起こるバグの1つ、または、それにより引き起こされた現象のことを言います。
FreeBSD では、 strlcat という関数が用意されているため、もし、使うことができる環境であれば、 strcat の替わりに strlcat を使うとよいでしょう。 また、strcat_s が利用できるなら、 strcat の替わりに使うのが良いでしょう。
strcat と strncat の違い
strcat と strncat の違いは、コピーするサイズを指定できるか、できないか、です。 strcat は、第一引数で指定されたバッファに含まれる文字列の後ろに、第二引数で指定された文字列を連結します。
strncat では、第二引数の文字列のうち「第三引数で指定するサイズ」分を「第一引数のバッファ」にコピーします。
strcat や strncat はデータを破壊するかもしれない
第一引数のバッファにコピーに必要な十分なサイズがあり、ヌルターミネートされた文字列が渡されるのであれば、問題は起きないでしょう。
しかし、十分なバッファがないときに、バッファ以上のデータがコピーされるときに、何がおきるでしょうか?第一引数のメモリの範囲を超えて、コピーが実行され、ほかの変数の領域を上書きしていきます。
ヘッダファイル
#include <string.h> char * strcat(char * restrict s, const char * restrict append); char * strncat(char * restrict s, const char * restrict append, size_t count);
strcat1.c の例
単純なコピーの例から見てみましょう。
ソースコード strcat1.c
/* * strcat1.c * Copyright (C) 2018 kaoru <kaoru@localhost> * * Distributed under terms of the MIT license. */ #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char str1[10] = { "foo" }; char str2[10] = { "bar" }; strcat (str1, str2); puts (str1); return 0; }
コンパイル
cc strcat1.c -o strcat1
実行例
% ./strcat1 foobar
データが破壊されるダメな例 strcat1nobuf.c の例
ここでは、データが破壊されている例をみてみましょう。
ソースコード strcat1nobuf.c
/* * strcat1.c * Copyright (C) 2018 kaoru <kaoru@localhost> * * Distributed under terms of the MIT license. */ #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char str0[5] = { "hoge" }; char str1[6] = { "foo" }; char str2[4] = { "bar" }; char str3[5] = { "hoge" }; strcat (str1, str2); puts (str1); puts (str2); puts (str0); puts (str3); return 0; }
コンパイル
cc strcat1nobuf.c -o strcat1nobuf
実行例
一見、 str1 の出力が foobar となっているので、問題がないように見えます。str2 の出力は bar なので、こちらも問題はありません。 しかし、str0 はどうでしょうか?
puts (str1); puts (str2); puts (str0);
以下の実行例では、3行目の str0 の出力が空っぽになっています。 str0 は、初期化時に hoge を代入しています。
% ./strcat1nobuf foobar bar hoge
スタックは、下から上に伸びていくので、str1 に str2 を連結するときに、 strcat は、 str0 の 先頭に ヌル文字を書き込んでしまったのです。
そのため、 str0 の 2文字目から3文字目は、ちゃんとデータが残っています。
int main(int argc, char *argv[]) { char str0[5] = { "hoge" }; char str1[6] = { "foo" }; char str2[4] = { "bar" }; char str3[5] = { "hoge" }; strcat (str1, str2); puts (str1); puts (str2); puts (str0); puts (str3); printf ("%c, ", str0[1]); printf ("%c, ", str0[2]); printf ("%c\n", str0[3]); return 0; }
上記をコンパイルして、実行してみると以下のようになります。
% ./strcat1nobuf foobar bar hoge o, g, e
strncat1.c の例
strncat は、第二引数から第三引数で指定した数の文字を第一引数のバッファに連結します。 以下は、str2 から 3 文字分、str1 に連結する例です。
ソースコード strncat1.c
/* * strncat1.c * Copyright (C) 2018 kaoru <kaoru@localhost> * * Distributed under terms of the MIT license. */ #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char str1[7] = { "foo" }; char str2[9] = { "bar hoge" }; size_t size = 3; strncat (str1, str2, size); puts (str1); return 0; }
コンパイル
cc strncat1.c -o strncat1
実行例
% ./strncat1 foobar
strlcat とは
strcat は、バッファオーバーランを引き起こす可能性のある、安全ではない関数です。 そのため、 strlcat は、バッファサイズを指定することで、バッファオーバーランを防止することを目的に開発されました。 ただし、どこの環境でも使えるというわけではありません。
strcat_s とは
strcat_s は、マイクロソフトな環境で利用できます。
関連項目
ツイート