strncat
tags: memmove, string.h, strlen, strncat
The strncat
function allows you to join two strings by appending the second
string to the first, modifying the first string rather than creating a new one.
In addition, you may specify a maximum number of characters to be appended,
allowing you to control the amount of data being appended.
char *
strncat(char *s1, const char *s2, size_t n);
It will return s1
which will now hold the concatenation of the two strings.
Implementation
Much like strcat
, we must first determine the length of the two strings to
determine where to begin copying and how much to copy. We should copy the lesser
value of n
and the length of the second string.
size_t len1 = 0;
size_t len2 = 0;
/* Error checking */
len1 = strlen(s1);
len2 = strlen(s2);
if (len2 < n)
{
n = len2;
}
Now that we know how much to copy, we know that the first character to be
overwritten will be the null-character at the end of s1
, which is located at
s1 + len1
. Next, we add a null-character at the very end in case one was
not found within the first n
(our modified one) characters of s2
. Finally,
return the value of s1
.
memmove(s1 + len1, s2, n);
s1[len1 + n] = '\0';
return s1;
This is nice because it works whether n
is positive or zero. When n
is zero
no characters will be copied and the null-character at the end of s1
will be
overwritten with another null-character.
Testing
We need to test concatenation to/from NULL
pointers, to/from empty strings,
partial concatenation, and finally normal concatenation. In the case of NULL
pointers and an empty source string, we expect the destination to be unmodified.
Otherwise, we verify that the result is n
characters from the second string
appended to the first.
int
strncatTest(void)
{
int ret = 1;
char *pStr1 = NULL;
char pStr2[15] = "hello, world";
char pStr3[15] = "hello, world";
char pStr4[15] = { 0 };
char pStr5[15] = "hello,";
char pStr6[15] = " world";
do
{
/* Append to NULL, may crash */
strncat(pStr1, pStr2, strlen(pStr2));
/* Concatenate from NULL, may crash */
strncat(pStr2, pStr1, 10);
/* Concatenate to an empty string */
strncat(pStr4, pStr2, strlen(pStr2));
if (0 != memcmp(pStr4, pStr2, strlen(pStr2) + 1))
{
break;
}
/* Concatenate from an empty string */
memset(pStr4, '\0', sizeof(pStr4));
strncat(pStr2, pStr4, 10);
if (0 != memcmp(pStr2, pStr3, sizeof(pStr3)))
{
break;
}
/* Append partial string */
memset(pStr4, '\0', sizeof(pStr4));
strncat(pStr4, pStr5, 4);
if ((4 != strlen(pStr4)) ||
(0 != memcmp(pStr4, pStr5, 4)))
{
break;
}
/* Normal concatenation */
strncat(pStr5, pStr6, sizeof(pStr5) - strlen(pStr5));
if (0 != memcmp(pStr5, pStr3, sizeof(pStr3)))
{
break;
}
ret = 0;
} while (0);
return ret;
}
Conclusion
Leveraging the functionality of strlen
allows us to pass the major work to
memmove
, which consequently works as expected if n
is zero. The only tricks
to this function are figuring out how much data to copy and ensuring the
resulting string is null-terminated.
char *
strncat(char *s1, const char *s2, size_t n)
{
size_t len1 = 0;
size_t len2 = 0;
if (!s1 || !s2)
{
return s1;
}
len1 = strlen(s1);
len2 = strlen(s2);
if (len2 < n)
{
n = len2;
}
memmove(s1 + len1, s2, n);
s1[len1 + n] = '\0';
return s1;
}