文字列の結合

文字列操作3

文字列の末尾に別の文字列を結合するにはstrcat関数、strncat関数、またはstrcat_s関数、strncat_s関数を使用します。

strcat_s関数、strncat_s関数はコンパイラによっては使用できません。

strcat関数、strncat関数はVisualStudio既定の設定では使用するとエラーとなります。
詳しくは_s系関数とエラー表示についてを参照してください。

strcat関数、strncat関数


#include <stdio.h>
#include <string.h>

int main()
{
	char strDest1[32] = "ABC";		//結合先1
	char strDest2[32] = "ABC";		//結合先2
	const char strSource[] = "DEF"; //結合元

	strcat(strDest1, strSource);
	strncat(strDest2, strSource, sizeof(strSource));

	printf("%s\n", strDest1);
	printf("%s\n", strDest2);
	getchar();
}
ABCDEF
ABCDEF
char *strcat(
 char *strDestination,
 const char *strSource
);
文字列strDestinationの末尾に文字列strSourceを結合する。
戻り値は文字列strDestination。
char *strncat(
 char *strDest,
 const char *strSource
 size_t count
);
文字列strDestinationの末尾に文字列strSourceをcount文字分結合する。
終端がNULL文字でない場合はNULL文字に置き換える。
戻り値は文字列strDestination。

これらの関数はいずれも結合される文字列の末尾のNULL文字から文字列の結合が開始されます。
つまり文字列strDestinationの末尾のNULL文字は文字列strSourceの一文字目に置き換えられ、文字列と文字列の間にNULL文字が入ることはありません。

strcpy関数の時と同様、strcat関数は配列サイズのチェック機能がないため、結合先配列のサイズが足りないとバッファオーバーランが発生します。

strncat関数は第三引数countで結合する文字数を制限できます。
結合する文字列の文字数がcountよりも大きい場合、文字列の切り捨てが発生するわけですが、このとき文字列の末尾にNULL文字が付加されます。
そのため、結合先配列の空き容量ギリギリの値をcountに指定するとバッファオーバーランが発生してしまいます。
(NULL文字がはみ出る)
なので、countに指定できる値は最大で「空き容量-1」となります。


char strDest[6] = "ABC";
const char strSource[] = "DEF";

//strDest[6]の位置をNULL文字に置き換えてしまう
//(バッファオーバーラン)
//strncat(strDest, strSource, sizeof(strDest) - strlen(strDest));

//strDest[5]の位置がNULL文字になる
strncat(strDest, strSource, sizeof(strDest) - strlen(strDest) - 1);

printf("%s\n", strDest);
ABCDE

strcat_s関数、strncat_s関数


#include <stdio.h>
#include <string.h>

int main()
{
	char strDest1[32] = "ABC";		//結合先1
	char strDest2[32] = "ABC";		//結合先2
	const char strSource[] = "DEF";	//結合元

	strcat_s(strDest1, sizeof(strDest1), strSource);
	strncat_s(strDest2, sizeof(strDest2), strSource, sizeof(strSource));

	printf("%s\n", strDest1);
	printf("%s\n", strDest2);
	getchar();
	getchar();
}
ABCDEF
ABCDEF
errno_t strcat_s(
 char *strDestination,
 size_t numberOfElements,
 const char *strSource
);
サイズnumberOfElementsの文字列strDestinationの末尾に文字列strSourceを結合する。
正常終了すると0を、エラーの場合は0以外を返す。
errno_t strncat_s(
 char *strDest,
 size_t numberOfElements,
 const char *strSource
 size_t count
);
サイズnumberOfElementsの文字列strDestの末尾に文字列strSourceをcount文字分結合する。
正常終了すると0を、エラーの場合は0以外を返す。

strcat_s関数の第二引数numberOfElementsはstrncat関数の第三引数countとは意味が異なります。
strncat関数のcountは結合される文字列のサイズ指定ですが、strcat_s関数のnumberOfElementsは「結合される文字列+結合する文字列+NULL文字」の総サイズを指定します。
要するに第一引数strDestの(空き容量も含めた)配列サイズを指定します。
このサイズ指定よりも実際に生成される文字列のサイズの方が大きいとプログラムが停止します。

strncat_s関数は第四引数countで結合する文字列のサイズを制限することができます。
配列の空容量-1を指定すれば安全に結合が可能です。


#include <stdio.h>
#include <string.h>

int main()
{
	char strDest1[8] = "ABCDE";
	char strDest2[32] = "ABCDE";
	const char strSource[] = "FGHIJ";

	strncat_s(strDest1, sizeof(strDest1), strSource, sizeof(strDest1) - strlen(strDest1) - 1);
	strncat_s(strDest2, sizeof(strDest2), strSource, sizeof(strDest2) - strlen(strDest2) - 1);

	printf("%s\n", strDest1);
	printf("%s\n", strDest2);
	
	getchar();
}
ABCDEFG
ABCDEFGHIJ

第四引数に_TRUNCATEマクロを指定しても同じ動作となります。


strncat_s(strDest, sizeof(strDest), strSource, sizeof(strDest) - strlen(strDest) - 1);
//↑↓同じ意味
strncat_s(strDest, sizeof(strDest), strSource, _TRUNCATE);