CheatSheet: char* copying & concatenation in C
Related articles:
- (2023) Better string handling for the kernel https://lwn.net/Articles/948408/
- (2022) Ushering out strlcpy() https://lwn.net/Articles/905777/
- (2015) Improving kernel string handling https://lwn.net/Articles/643376/
- strlcpy and strlcat – consistent, safe, string copy and concatenation. https://www.millert.dev/papers/strlcpy/
- Efficient string copying and concatenation in C https://developers.redhat.com/blog/2019/08/12/efficient-string-copying-and-concatenation-in-c
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);