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;
}
comments powered by Disqus