各関数の概要
C89 で追加された strncpy、strncat 及び、snprintf 関数では、バッファサイズを指定するパラメータがありますが、ここのサイズ指定を間違えがちなのでメモしておきます。
それぞれの関数のシグネチャと用途は下記のようになっています。
| 関数 | 説明 |
|---|---|
char *strncpy(char *dest, const char *src, size_t n) | 文字列 src から最大 n バイトを dest にコピーする |
char *strncat(char *dest, const char *src, size_t n) | 文字列 src から最大 n バイトを dest の末尾に追加する |
int snprintf(char *str, size_t n, const char *format, ...) | フォーマットした文字列から最大 n バイトを str に書き込む |
strncpy の使い方
strncpy の man page を見ると、次のように説明されています。
char *strncpy(char *dest, const char *src, size_t n);
If there is no null byte among the first n bytes of src,
the result will not be null terminated.
In the case where the length of src is less than that of n,
the remainder of dest will be padded with null bytes.
srcの長さがn以上だったら NULL 終端されないよ~ (>_<)srcの長さがnより小さければ NULL 終端してあげるよ~ (^o^)
と言っているので、src の長さによらず dest を NULL 終端させるには、下記のように最後に \0 を設定しておく必要があります。
strncpy(dst, src, dst_size);
dst[dst_size - 1] = '\0';
strncat の使い方
strncat の man page を見ると、次のように説明されています。
char *strncat(char *dest, const char *src, size_t n);
Since the result is always terminated with '\0',
at most n+1 characters are written.
引数 n で指定するのは、dest の末尾に最大何文字付け加えることができるかであって、dest のバッファサイズではないことに注意してください。
恐ろしいことに、最大 n 文字を加えた後に、末尾に必ず \0 を加えようとするので、最大で n+1 byte 分の文字が dest に書き込まれます。
つまり、第 3 引数の値としては、
'\0' からバッファの末尾までの空きサイズから、'\0' 追加分の 1 を引いた値
を指定するのが正解で、コードとしては以下のようにしなければいけません。
char dst[N] = "hoge"; // ※
strncat(dst, src, sizeof(dst) - strlen(dst) - 1);
※上記では、strlen でコピー先の現在の文字列長を調べているので、dst は NULL 終端している必要があります。
snprintf の使い方
snprintf の man page には下記のように記載されています。
int snprintf(char *str, size_t size, const char *format, ...);
The functions snprintf() and vsnprintf() write at most size bytes
(including the terminating null byte ('\0')) to str.
C99 準拠の snprintf は、size > 0 であれば、出力が切り詰められても常に NULL 終端を行います。
つまり、最大で size - 1 文字がバッファに書き込まれ、末尾に \0 が付加されます。
strncpy とは異なり、末尾に \0 を手動で追加する必要はありません。
char buf[N] = "";
snprintf(buf, sizeof(buf), "......", a, b, c);
// buf は常に NULL 終端される(N > 0 の場合)