CheatSheet: char* copying & concatenation in C

Related articles:

Just memcpy and add NUL by hand, how hard it can be?

char *s1 = "ab", *s2 = "cd";

size_t dstsize = strlen(s1) + strlen(s2) + 1;
char *dst = malloc(dstsize);

memcpy(dst, s1, strlen(s1));
memcpy(dst + strlen(s1), s2, strlen(s2));
dst[strlen(s1) + strlen(s2)] = '\0';

classic

char *strcpy(char *restrict dst, const char *restrict src);
char *stpcpy(char *restrict dst, const char *restrict src);
char *strcat(char *restrict dst, const char *restrict src);

strcpy returns its 1st arg dst. This is a trick to use it together with strcat

// both s1 & s2 will be copied into dst
strcat(strcpy(dst,s1),s2);

stpcpy returns a pointer to the NUL of the copied string, which can be used to chain copying:

char *dst = malloc(maxlen);
char *p = dst;
p = stpcpy(p, s1);
p = stpcpy(p, s2);
size_t len = p - dst;

! Inefficiency:

The strcpy returns dst the position of NUL is lost. Every time, the NUL have to be found again. https://en.wikipedia.org/wiki/Joel_Spolsky#Schlemiel_the_Painter's_algorithm

strcat(dst,s1);
strcat(dst,s2);

! Unsafe:

strcpy requires knowing dst’s length > src’s length before the strcpy call.

Classic with truncation

char *strncpy(char *restrict s1, const char *restrict s2, size_t n);
char *stpncpy(char *restrict s1, const char *restrict s2, size_t n);

Copy s2 into s1, but no more than n bytes.

stpncpy returns the last NUL.

In cast s1 is not big enough, need to consider something like

if (strlen(s1) >= sizeof(s2)){
    // handle overlong s2 error.
}

!Quirks

If s2 is smaller than s1, the s1’s end will be filled with NUL.

If s2 is larger n, then the result won’t be NUL terminated.

char s1[2];
char s2[] = "abcd";

strncpy(s1, s2, sizeof(s1));

/*
s1 = {char [2]} 
    [0] = {char} 97 'a'
    [1] = {char} 98 'b'
No NUL!
*/

BSD strlcpy

Safer strncpy, usable via libbsd or glibc >3.28

size_t strlcpy(char *dst, const char *src, size_t dstsize);
size_t strlcat(char *dst, const char *src, size_t dstsize);

Return total length of the string they tried to create

If return value >= datsize, then the output string is truncated.

To copy two str into dst:

char *s1 = "ab", *s2 = "cd";

size_t dstsize = strlen(s1) + strlen(s2) + 1;
char *dst = malloc(dstsize);

char *p = dst;
size_t n = strlcpy(p, s1, dstsize);
// n bytes copied, move the pointer forward
// and subtract that number from capacity
dstsize -= n;
p += n;
strlcpy(p, s2, dstsize);

memccpy from POSIX

void *memccpy(void dest[restrict .n], const void src[restrict .n], int c, size_t n);

Copies no more than n bytes from src to dest, stopping when char c is found.

Returns a pointer to be next char in dest after c, or NULL if c was not found in the first n characters of src.

Without considering buffer overflow, chain copying

// Note the -1 because the return value points to the next char after '\0'
memccpy(memccpy(dst,s1,'\0',dstsize) -1, s2,'\0', dstsize);