strncat, strncat_s
ja.cppreference.net から引用
| ヘッダで定義 <string.h>
|
||
char* strncat( char* dest, const char* src, size_t count );
|
(1) | (C99 まで) |
char* strncat( char* restrict dest, const char* restrict src, size_t count );
|
(C99 以降) | |
errno_t strncat_s( char* restrict dest, rsize_t destsz,
const char* restrict src, rsize_t count );
|
(2) | (C11 以降) |
1)
count が指す文字配列から最大 src 文字(ヌル文字が見つかったらそこで停止)を、dest が指すナル終端バイト文字列の末尾に追加する。文字 src[0] が dest の末尾にあるヌル終端文字を置き換える。末尾には常にヌル終端文字が追加される(したがって、関数が書き込む可能性のある最大バイト数は count + 1 である)。 コピー先配列に
dest の内容全体と count 個の src の先頭文字、さらにヌル終端文字を格納するのに十分な領域がない場合、動作は未定義である。コピー元とコピー先のオブジェクトが重なっている場合、動作は未定義である。dest がナル終端バイト文字列へのポインタでない場合、または src が文字配列へのポインタでない場合、動作は未定義である。2) (1) と同じだが、この関数はコピー先配列の残りの部分(最後に書き込まれたバイトから
destsz まで)を上書きする可能性がある点、および以下のエラーが実行時に検出され、現在設定されている 制約ハンドラ 関数を呼び出す点が異なる:
srcまたはdestが NULL ポインタであるdestszcountが 0 または RSIZE_MAX より大きいdestszバイトのdestにヌル文字がない- 切り詰めが発生する:
countまたはsrcの長さのうち小さい方が、destのヌル終端子とdestszの間の利用可能な領域を超える - コピー元文字列とコピー先文字列の間で重なりが発生する
dest が指す文字配列のサイズ < strnlen(dest, destsz) + strnlen(src,count) + 1 < destsz よりも小さい場合、動作は未定義である。言い換えれば、誤った destsz の値がバッファオーバーフローを露呈させることはない。src が指す文字配列のサイズ < strnlen(src,count) < destsz よりも小さい場合も同様に動作は未定義である。言い換えれば、誤った count の値がバッファオーバーフローを露呈させることはない。 他の境界チェック関数と同様に、
strncat_s は実装によって __STDC_LIB_EXT1__ が定義されていて、ユーザーが __STDC_WANT_LIB_EXT1__ を整数定数 1 として定義してから <string.h> をインクルードした場合にのみ利用可能である。引数
| dest | - | 追加先のナル終端バイト文字列へのポインタ |
| src | - | コピー元の文字配列へのポインタ |
| count | - | コピーする最大文字数 |
| destsz | - | コピー先バッファのサイズ |
戻り値
1)
dest のコピーを返す2) 成功した場合はゼロを返し、エラーの場合は非ゼロを返す。また、エラー時には
dest[0] にゼロを書き込む(dest が NULL ポインタであるか、destsz が 0 または RSIZE_MAX より大きい場合を除く)。ノート
strncat は呼び出しのたびに dest の末尾を探す必要があるため、strncat を使用して複数の文字列を 1 つに連結するのは非効率である。
コピー先バッファに収まるように切り詰めることはセキュリティ上のリスクであるため、strncat_s にとっては実行時制約違反となるが、count をコピー先配列のサイズより 1 少ない値に指定することで、切り詰め動作を実現することは可能である。その場合、最初の count バイトをコピーし、常にヌル終端子を追加する:strncat_s(dst, sizeof dst, src, (sizeof dst) - strnlen_s(dst, sizeof dst) - 1);
例
このコードを実行する
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char str[50] = "Hello ";
char str2[50] = "World!";
strcat(str, str2);
strncat(str, " Goodbye World!", 3);
puts(str);
#ifdef __STDC_LIB_EXT1__
set_constraint_handler_s(ignore_handler_s);
char s1[100] = "good";
char s5[1000] = "bye";
int r1 = strncat_s(s1, 100, s5, 1000); // r1 is 0, s1 holds "goodbye\0"
printf("s1 = %s, r1 = %d\n", s1, r1);
char s2[6] = "hello";
int r2 = strncat_s(s2, 6, "", 1); // r2 is 0, s2 holds "hello\0"
printf("s2 = %s, r2 = %d\n", s2, r2);
char s3[6] = "hello";
int r3 = strncat_s(s3, 6, "X", 2); // r3 is non-zero, s3 holds "\0"
printf("s3 = %s, r3 = %d\n", s3, r3);
// the strncat_s truncation idiom:
char s4[7] = "abc";
int r4 = strncat_s(s4, 7, "defghijklmn", 3); // r4 is 0, s4 holds "abcdef\0"
printf("s4 = %s, r4 = %d\n", s4, r4);
#endif
}
出力例:
Hello World! Go
s1 = goodbye, r1 = 0
s2 = hello, r2 = 0
s3 = , r3 = 22
s4 = abcdef, r4 = 0
参考文献
- C23 規格 (ISO/IEC 9899:2024):
- 7.26.3.2 strncat 関数 (p: 379)
- K.3.7.2.2 strncat_s 関数 (p: TBD)
- C17 規格 (ISO/IEC 9899:2018):
- 7.24.3.2 strncat 関数 (p: 265-266)
- K.3.7.2.2 strncat_s 関数 (p: 449-450)
- C11 規格 (ISO/IEC 9899:2011):
- 7.24.3.2 strncat 関数 (p: 364-365)
- K.3.7.2.2 strncat_s 関数 (p: 618-620)
- C99 規格 (ISO/IEC 9899:1999):
- 7.21.3.2 strncat 関数 (p: 327-328)
- C89/C90 規格 (ISO/IEC 9899:1990):
- 4.11.3.2 strncat 関数
関連項目
(C11) |
2 つの文字列を連結する (関数) |
(C11) |
一方の文字列を他方にコピーする (関数) |
(C23) |
指定された区切り文字の後で停止して、一方のバッファを他方にコピーする (関数) |
C++ ドキュメント for strncat
| |