K&R C Programs Exercise 5-5

Exercise 5-5. Write versions of the library functions strncpy, strncat, and strncmp, which operate on at most the first n characters of their argument strings. For example, strncpy(s,t,n) copies at most n characters of t to s. See the description in Appendix B.

Three functions, each with a character count limit n. The non-obvious behaviour from the C standard is worth matching exactly: strncpy pads with null bytes if t is shorter than n, and does not null-terminate if t is exactly or longer than n. strncat always null-terminates. strncmp returns the difference of the first differing characters (or 0 if the first n match).

Solution

/* K&R Exercise 5-5 — strncpy, strncat, strncmp
 * Compile: gcc -ansi -Wall ex5-5.c -o ex5-5 */
#include <stdio.h>

/* Copy at most n chars of t into s; pad with '\0' if t is shorter than n.
 * Does NOT null-terminate if t has n or more characters. */
char *my_strncpy(char *s, const char *t, int n)
{
    char *start = s;
    while (n > 0 && *t != '\0') {
        *s++ = *t++;
        n--;
    }
    while (n-- > 0)     /* pad with nulls */
        *s++ = '\0';
    return start;
}

/* Append at most n chars of t to s; always null-terminates. */
char *my_strncat(char *s, const char *t, int n)
{
    char *start = s;
    while (*s) s++;     /* advance to end of s */
    while (n-- > 0 && *t != '\0')
        *s++ = *t++;
    *s = '\0';          /* always null-terminate */
    return start;
}

/* Compare at most n chars of s and t; return negative/0/positive. */
int my_strncmp(const char *s, const char *t, int n)
{
    for ( ; n > 0; n--, s++, t++) {
        if (*s != *t)
            return (unsigned char)*s - (unsigned char)*t;
        if (*s == '\0')
            return 0;   /* both ended before n chars */
    }
    return 0;
}

int main(void)
{
    char buf[20];

    /* strncpy */
    my_strncpy(buf, "hello", 3);
    buf[3] = '\0';                  /* must terminate manually when n <= strlen(t) */
    printf("strncpy 3: '%s'\n", buf);   /* hel */

    my_strncpy(buf, "hi", 8);
    printf("strncpy 8: '%s' (padded with nulls)\n", buf); /* hi + 6 nulls */

    /* strncat */
    char s[20] = "foo";
    my_strncat(s, "barbaz", 3);
    printf("strncat 3: '%s'\n", s);  /* foobar */

    /* strncmp */
    printf("strncmp abc/abd 2: %d\n", my_strncmp("abc","abd",2)); /* 0 */
    printf("strncmp abc/abd 3: %d\n", my_strncmp("abc","abd",3)); /* negative */
    printf("strncmp abc/abc 5: %d\n", my_strncmp("abc","abc",5)); /* 0 */
    return 0;
}

Compile and Run

gcc -ansi -Wall ex5-5.c -o ex5-5
./ex5-5

Sample Output

strncpy 3: 'hel'
strncpy 8: 'hi' (padded with nulls)
strncat 3: 'foobar'
strncmp abc/abd 2: 0
strncmp abc/abd 3: -1
strncmp abc/abc 5: 0

What This Exercise Teaches

  • strncpy null-padding rule — this is one of C’s famous gotchas: if t is shorter than n, the extra space is zeroed; but if t is longer, no null terminator is added — the caller must add it
  • strncat always terminates — unlike strncpy, strncat always writes a '\0' after the appended characters; the buffer needs room for strlen(s) + n + 1 bytes
  • Unsigned char in comparison(unsigned char)*s - (unsigned char)*t avoids sign-extension bugs when characters have values > 127

Set Up Your C Environment

← Exercise 5-4  | 
Chapter 5 Solutions  | 
Exercise 5-6 →

Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>