strncpy(3)

C library function

Copies at most n bytes and pads with null bytes. If the source is too long, the destination is left unterminated: strncpy is not a safe strcpy.

Synopsis
#include <string.h>
char *strncpy(char *restrict dst, const char *restrict src, size_t n);

Description

strncpy always writes exactly n bytes to dst. It copies the bytes of src up to its '\0', without going past n. If the source is shorter than n, it fills the rest of the destination with null bytes. If the source is n characters long or longer, it copies the first n and stops there, without adding a '\0'.

That behavior comes from its original purpose: filling fixed-size fields padded with zeros, like those of the utmp login records on Unix. The Linux man page in fact presents it as a copy that produces a null-padded character sequence, not a string.

Return value

dst. Nothing tells you whether the source was truncated: to find out, compare strlen(src) with n before the call.

Pitfalls

The main trap is the missing null byte. After strncpy(dst, src, sizeof dst) with a source that is too long, dst is not a string: strlen(3) or a printf("%s") will read past the end of the array. The classic fix is to copy with n equal to the size of the array, then write the terminator yourself in the last slot, as the example does: the string is then truncated, but terminated.

GCC spots this case when it knows both sizes, and warns with -Wstringop-truncation as in the example. When the source is only known at run time, it cannot tell whether truncation will happen.

The padding has a cost: strncpy(buf, "a", 4096) writes 4,096 bytes, 4,095 of them zeros. To copy a short string into a large buffer, that is wasted work.

As with strcpy(3), src and dst must not overlap.

To truncate a string cleanly into an array, snprintf(dst, sizeof dst, "%s", src) always terminates the destination and returns the length it would have needed, which lets you detect the truncation.

Example

The program prints every byte written, with a dot in place of each '\0'.

ncopy.c
#include <stdio.h>#include <string.h>
static void dump(const char *buf, size_t n){    for (size_t i = 0; i < n; i++)        putchar(buf[i] == '\0' ? '.' : buf[i]);    putchar('\n');}
int main(void){    char short_[8];    char long_[4];
    strncpy(short_, "git", sizeof short_);    dump(short_, sizeof short_);    strncpy(long_, "docker", sizeof long_);    dump(long_, sizeof long_);    long_[sizeof long_ - 1] = '\0';    printf("%s\n", long_);    return 0;}
Terminal
$ gcc -Wall -Wextra ncopy.c -o ncopyncopy.c: In function 'main':ncopy.c:18:5: warning: 'strncpy' output truncated copying 4 bytes from a string of length 6 [-Wstringop-truncation]   18 |     strncpy(long_, "docker", sizeof long_);      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~$ ./ncopygit.....dockdoc

"git" is shorter than 8: three letters, then five null bytes of padding. "docker" is longer than 4: four letters, no terminator, and dock is not a string. Line 20 writes the '\0' into the last slot, which sacrifices the k but gives a valid string, doc.

C

strncpy

Write gm_strncpy, which pads dst with null bytes when src is short and adds no terminator when src fills all n bytes.

Open the exercise

Now write it yourself.