C23標準ライブラリで追加されたmemccpy関数*1についてメモ。POSIX関数からの標準C入り。
コピー元s2からコピー先s1に対して、文字cの初回出現まで、もしくは最大n文字までコピーする関数。標準ヘッダ<string.h>にて宣言される。
void *memccpy(void * restrict s1, const void * restrict s2, int c, size_t n);
第3引数cにNUL文字('\0')を指定することで、有限長バッファへの安全な文字列コピーを実現できる。下記はOpenBSD strlcpy関数相当の実装例。*2
#include <string.h> void my_strlcpy(char* dst, const char* src, size_t size) { char *p = memccpy(dst, src, '\0', size); if ( !p ) { // コピー元範囲 [src, src+size) にNUL文字が存在しない場合、 // コピー先末尾 dst[size-1] へのNUL代入で文字列を終端させる。 dst[size-1] = '\0'; } } const char *str = "Hello C"; char buf1[10]; my_strlcpy(buf1, str, sizeof(buf1); // buf1 = "Hello C" char buf2[5]; my_strlcpy(buf2, str, sizeof(buf2); // buf2 = "Hell"
上記の文字列コピー処理は、古くからC標準ライブラリに存在するstrncpy関数(→id:yohhoy:20161102)よりも効率的に動作する。
void wasteful_strlcpy(char* dst, const char* src, size_t size) { strncpy(dst, src, size); // OK...だが非効率 // strlen(src) < size-1 の場合、コピー先の残り領域にもNUL文字が設定される。 // バッファサイズsizeが大きいほど無駄なメモリ書込処理が行われる。 // またコピー先バッファ末尾へのNUL代入も必要となる。 // strncpy関数の仕様上、戻り値からは代入の必要性を判定できない。 dst[size-1] = '\0'; // メモリ書込アクセスを嫌って分岐させるなら下記コードとなるが、 // strncpy内部にてバッファ全域への書込アクセスが行われている... // if (dst[size-1] != '\0') { dst[size-1] = '\0'; } }
C23 7.26.2.2/p2-3より引用。C++2c(C++26)はC23規格をNormative参照する。
Description
2 Thememccpyfunction copies characters from the object pointed to bys2into the object pointed to bys1, stopping after the first occurrence of characterc(converted to anunsigned char) is copied, or afterncharacters are copied, whichever comes first. If copying takes place between objects that overlap, the behavior is undefined.
Returns
3 Thememccpyfunction returns a pointer to the character after the copy ofcins1, or a null pointer ifcwas not found in the firstncharacters ofs2.
関連URL