Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 5.05 on page 107

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 . Full descriptions are in Appendix B.



Solution by Lars Wirzenius

Note: Lars uses EXIT_FAILURE in his test code, but not in the actual solution code. As far as I can tell, then, this is a Category 0 solution.


/*
 * Solution to exercise 5-5 in K&R2, page 107:
 *
 *	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. Full descriptions are in
 *	Appendix B.
 *
 * Note that the description in the exercise is not precise. Here are
 * descriptions from Appendix B (though one should really follow the
 * descriptions in the standard):
 *
 *	char *strncpy(s,ct,n)	copy at most n characters of string ct
 *				to s, return s. Pad with '\0's is ct
 *				has fewer than n characters.
 *	char *strncat(s,ct,n)	concatenate at most n characters of
 *				string ct to string s, terminate s with
 *				'\0'; return s.
 *	int strncmp(cs,ct,n)	compare at most n characters of string
 *				cs to string ct; return <0 if cs<ct,
 *				0 if cs==ct, or >0 if cs>ct.
 *
 * Further note that the standard requires strncmp to compare the
 * characters using unsigned char internally.
 * 
 * Implementation note: since the function names are reserved by the
 * standard, I've used the prefix `liw_'. This also allows Richard Heathfield to check
 * the functions against the standard library versions. For each library
 * function, I've written a test function that tests a particular test
 * case. Where appropriate, the test functions use internal buffers that
 * are of size MAX_BUF; at least some of the test cases should be longer
 * to test all boundary conditions.
 *
 * Feel free to modify, copy, and use as you wish.
 *
 * Lars Wirzenius <liw@iki.fi>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_BUF 16

char *liw_strncpy(char *s, const char *ct, size_t n) {
	char *p;
	
	p = s;
	for (; n > 0 && *ct != '\0'; --n)
		*p++ = *ct++;
	for (; n > 0; --n)
		*p++ = '\0';
	return s;
}

char *liw_strncat(char *s, const char *ct, size_t n) {
	char *p;
	
	p = s;
	while (*p != '\0')
		++p;
	for (; n > 0 && *ct != '\0'; --n)
		*p++ = *ct++;
	*p = '\0';
	return s;
}

int liw_strncmp(const char *cs, const char *ct, size_t n) {
	while (n > 0 && *cs == *ct && *cs != '\0') {
		++cs;
		++ct;
		--n;
	}
	if (n == 0 || *cs == *ct)
		return 0;
	if (*(unsigned char *) cs < *(unsigned char *) ct)
		return -1;
	return 1;
}

void test_ncpy(const char *str) {
	char std_buf[MAX_BUF];
	char liw_buf[MAX_BUF];

	memset(std_buf, 0x42, sizeof(std_buf));
	strncpy(std_buf, str, sizeof(std_buf));

	memset(liw_buf, 0x42, sizeof(liw_buf));
	liw_strncpy(liw_buf, str, sizeof(liw_buf));

	if (memcmp(std_buf, liw_buf, sizeof(std_buf)) != 0) {
		fprintf(stderr, "liw_strncpy failed for <%s>\n", str);
		exit(EXIT_FAILURE);
	}
}

void test_ncat(const char *first, const char *second) {
	char std_buf[MAX_BUF];
	char liw_buf[MAX_BUF];

	memset(std_buf, 0x69, sizeof(std_buf));
	strcpy(std_buf, first);
	strncat(std_buf, second, sizeof(std_buf) - strlen(std_buf) - 1);

	memset(liw_buf, 0x69, sizeof(liw_buf));
	strcpy(liw_buf, first);
	liw_strncat(liw_buf, second, sizeof(liw_buf) - strlen(liw_buf) - 1);
	
	if (memcmp(std_buf, liw_buf, sizeof(std_buf)) != 0) {
		fprintf(stderr, "liw_strncat failed, <%s> and <%s>\n",
			first, second);
		exit(EXIT_FAILURE);
	}
}

void test_ncmp(const char *first, const char *second) {
	size_t len;
	int std_ret, liw_ret;

	if (strlen(first) < strlen(second))
		len = strlen(second);
	else
		len = strlen(first);
	std_ret = strncmp(first, second, len);
	liw_ret = liw_strncmp(first, second, len);
	if ((std_ret < 0 && liw_ret >= 0) || (std_ret > 0 && liw_ret <= 0) ||
	    (std_ret == 0 && liw_ret != 0)) {
		fprintf(stderr, "liw_strncmp failed, <%s> and <%s>\n",
			first, second);
		exit(EXIT_FAILURE);
	}
}

int main(void) {
	test_ncpy("");
	test_ncpy("a");
	test_ncpy("ab");
	test_ncpy("abcdefghijklmnopqrstuvwxyz");     /* longer than MAX_BUF */
	
	test_ncat("", "a");
	test_ncat("a", "bc");
	test_ncat("ab", "cde");
	test_ncat("ab", "cdefghijklmnopqrstuvwxyz"); /* longer than MAX_BUF */

	test_ncmp("", "");
	test_ncmp("", "a");
	test_ncmp("a", "a");
	test_ncmp("a", "ab");
	test_ncmp("abc", "ab");

	printf("All tests pass.\n");
	return 0;
}


Solution by Jose G. López (Category 0)

#include <stdio.h>

#define MAX 20

char *my_strncpy(char *s, const char *t, int n);
char *my_strncat(char *s, const char *t, int n);
int my_strncmp(const char *s, const char *t, int n);

int main(void)
{
	char s[MAX] = "hello";
	char *t = "he";

	/* test my_strncpy */
	my_strncpy(s, t, 5);
	printf("my_strncpy - s = \"%s\"\n", s);
	/* test my_strncat */
	my_strncat(s, t, 20);
	printf("my_strncat - s = \"%s\"\n", s);
	/* test my_strncmp */
	printf("my_strncmp - returns %d comparing \"%s\" with \"%s\"\n", 
			my_strncmp(s, t, 4), s, t);

	return 0;
}

char *my_strncpy(char *s, const char *t, int n)
{
	int i;

	for (i = 0; i < n; i++)
		*(s + i) = *(t + i) ? *(t + i) : '\0';

	return s;
}

char *my_strncat(char *s, const char *t, int n)
{
	int i;

	while (*++s)
		;
	for (i = 0; i < n && *(t + i); i++)
		*(s + i) = *(t + i);
	*(s + i) = '\0';

	return s;
}

int my_strncmp(const char *s, const char *t, int n)
{
	int i;

	for (i = 0; i < n && *(s + i) == *(t + i) && *(s + i); i++)
		;
	if (*(s + i) != *(t + i))
		 return (unsigned char)*(s + i) - (unsigned char)*(t + i);
	
	return 0;
}

Note from Amrgrn: Jose, you could simplify further since a match will yield a difference of 0.

int my_strncmp(const char *s, const char *t, int n)
{
    int i;

    for (i = 0; i < n && *(s + i) == *(t + i) && *(s + i); i++)
		;
    return (unsigned char)*(s + i) - (unsigned char)*(t + i);
}


Solution by Jesus Alvarez (Category 0)

#include <stdio.h>

#define STR_BUF    10000
#define STR_MATCH  7 /* Used as the base number of characters to match with. */

char *my_strncpy (char *, char *, int);
char *my_strncat (char *, char *, int);
int   my_strncmp (char *, char *, int);
int   my_strlen  (char *);

int main(int argc, char *argv[])
{
        int result;
        char str_s[STR_BUF] =  "All along the watchtower.";
        char buf_1[STR_BUF];
        char buf_2[STR_BUF] = "Bob Dylan: ";
        char buf_3[STR_BUF] = "All along the Watchposition.";

        printf ("----------------------------------------------------------\n");
        printf ("  Base String: %s\n", str_s);
        printf ("----------------------------------------------------------\n");

        my_strncpy (buf_1, str_s, STR_MATCH);
        printf ("buf_1 (my_strncpy, 7 chars): %s\n", buf_1);

        my_strncat (buf_2, str_s, STR_MATCH);
        printf ("buf_2 (my_strncat, 5 chars): %s\n", buf_2);

        result = my_strncmp(buf_3, str_s, STR_MATCH);

        printf ("buf_3 (my_strncmp, 6 chars): %s\n", buf_3);

        if ( result == 0 ) {
                printf ("my_strncmp result: Both strings match up to %d char(s).\n",
                        STR_MATCH );
        } else if ( result == -1 ) {
                printf ("my_strncmp result: Strings do not match, buf_3 string ");
                printf ("has a lesser value.\n");
        } else if ( result == 1 ) {
                printf ("my_strncmp result: Strings do not match, ");
                printf ("base string has a greater value than buf_3.\n");
        }

        return 0;
}

/*
 * Copy at most n characters of string ct to s; return s.
 */
char *my_strncpy (char *s, char *ct, int n)
{
        int count = 1;
        while ((*s++ = *ct++)) {
                if (count++ == n) {
                        break;
                }

        }
        return s;
}

/*
 * Concatenate at most n characters of string ct to string s, terminate s with
 * '\0'; return s.
 */
char *my_strncat (char *s, char *ct, int n)
{
        int i = 0;
        int len = my_strlen(s);
        for (i = 0; n > 0; i++, n--) {
                *(s + len + i) = *ct++;
        }
        *(s + len + i) = '\0';
        return s;
}

/*
 * Compare at most n characters of string cs to string ct; return < 0 if
 * cs < ct, 0 if cs == ct, or > 0 if cs > ct.
 */
int my_strncmp (char *cs, char *ct, int n)
{
        int i;

        for (i = 0; i < n; i++) {
                if (*(cs+i) < *(ct+i)) {
                        return -1;
                } else if (*(cs+i) > *(ct+i)) {
                        return 1;
                }
        }

        return 0;
}

int my_strlen (char *s)
{
        int count = 0;
        while (*s++ != '\0') {
                count++;
        }
        return count;
}


Solution by menonsahab

#include <stdio.h>
#include <string.h>
#define SIZE 100
char *my_strncpy(char *s, char *t, unsigned int n);
char *my_strncat(char *s, char *t, unsigned int n);
int   my_strncmp(char *s, char *t, unsigned int n);

int main()
{
	char s[SIZE] = "Aomine Daiki", t[SIZE] = "Kise Ryota";
	char *ps =  "Aomine Daiki", *pt = "Kise Ryota";
	printf("my_strncmp(%s, %s, %d) = %d\n", ps, pt, 10, my_strncmp(s, t, 10));
	printf("my_strncat(%s, %s, %d) = %s\n", ps, pt, 8,  my_strncat(s, t, 8));
	printf("my_strncpy(%s, %s, %d) = %s\n", ps, pt, 5,  my_strncpy(s, t, 6));	
	return 0;
}

// Function description: http://www.cplusplus.com/reference/cstring/strncpy/
char *my_strncpy(char *s, char *t, unsigned int n)
{
	int i, tlen = strlen(t);
	for(i = 0; i < n; i++)
	{
		if(i < tlen)
			s[i] = t[i];
		else
			s[i] = 0;
	}
	s[i] = 0;
	return s;
}

// Function description: http://www.cplusplus.com/reference/cstring/strncat/
char *my_strncat(char *s, char *t, unsigned int n)
{
	int i, tlen = strlen(t), slen = strlen(s);
	for(i = 0; i < n; i++)
	{
		if(i < tlen)
			s[slen + i] = t[i];
		else
			break;
	}
	s[slen + i] = '\0';
	return s;
}

// Function description: http://www.cplusplus.com/reference/cstring/strncmp/
int my_strncmp(char *s, char *t, unsigned int n)
{
	int i;
	for(i = 0; i < n; i++)
		if(s[i] != t[i] || s[i] == 0 || t[i] == 0)
			return s[i] - t[i];
	return 0;
}


Solution by Luke Panayi

/*
 * Write versions of the library functions strncpy, strncat, and
 * strncmp, which operate on at mot the first n characters of their argument
 * strings. For example, strncpy(s,t,n) copies at most n characters of t to s.
 * Full descriptions are in Appendix B.
*/

#include <stdio.h>
#define BUFFER 1000

char *strncpy2(char *s, char *t, int n)	{

	int i;
	char *sStart = s;

	for (i=0; i < n && (*s++ = *t++); ++i);
	
	if (i == n)
		*s = '\0';

	return sStart;
}

char *strncat2(char *s, char *t, int n)	{
	
	while (*++s);

	return strncpy2(s, t, n);
}

int strncmp2(char *s, char *t, int n)	{
	
	int i;

	for (i=0; i < n && *s == *t; ++i, ++s, ++t)	{
		if (i == n || !*s)
			return 0;
	}

	return *s - *t;
}

int main()	{
	int i;
	char s[BUFFER] = "hello";
	char t[BUFFER] = "hello world!";
	i = strncmp2(s,t,6);
	printf("%d\n", i);
	return 0;
}


Solution by DtxdF

#include <stdio.h>
#include <stdlib.h>

int my_strncmp(char *s, char *t, int l);
char *my_strncpy(char *s, char *t, int l);
char *my_strncat(char *s1, char *s2, int l);

int main(void) {
	char str2cmp[] = "hello";
	char buff[10];

	my_strncpy(buff, str2cmp, 3);
	my_strncat(buff, "lo", 2);

	printf("String to compare: %s\n", buff);

	printf("%d\n", my_strncmp(buff, "hello", 0)); /* 0: equals */
	/* 0: First character is equal to the first character in `str2cmp` */
	printf("%d\n", my_strncmp(buff, "hsllo", 1));
	/* But not this. */
	printf("%d\n", my_strncmp(buff, "hsllo", 2));

	return EXIT_SUCCESS;

}

/**
 * Description: Compare two strings
 *
 * s: The first string
 * t: The second string
 * l: The characters limit to compare. 0 is infinite or equal to `strcmp`.
 *
 * return: 0 if `s` and `l` are equals; non-zero if not.
 **/
int my_strncmp(char *s, char *t, int l) {
	int i;

	for (i = 0; *s == *t; i++, s++, t++)
		if ((l > 0 && i >= l-1) || *s == '\0')
			return 0;

	return *s-*t;

}

char *my_strncpy(char *s, char *t, int l) {
	int i;

	i = 0;
	while ((l > 0 && i++ < l) && (*s++ = *t++))
		;

	return s;

}

char *my_strncat(char *s1, char *s2, int l) {
	int c;
	int i;
	int copied;

	i = 0;
	while (*s1++)
		i++;

	s1--;

	copied = 0;
	while ((c = *s2++)) {
		if (copied >= l)
			break;

		*s1++ = c;
		i++;

	}

	return s1-i;

}

Solution by anonymous

Luke, I really like the idea of using your strncpy2 to help do most of the work for strncat2. The only thing that is an issue is if s == "", it will skip over the initial '\0'. We essentially came up with the same strncmp, you just wrote yours to find a match within the for loop when I was looking for a non-match in the for loop. DtxdF, you forgot to pad '\0' in your strncpy

#include <stdio.h>

/*
    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. Full descriptions are in Appendix B.
*/

char *strncpy(char *s, char *t, int n);
char *strncat(char *s, char *t, int n);
int strncmp(char *s, char *t, int n);

int main()
{
    char *str1 = "hello, world"; // const string pointer
    char str2[100];

    printf("%s\n", strncpy(str2, str1, 8));
    printf("%s\n", strncpy(str2, str1, -1));
    printf("%s\n", strncpy(str2, str1, 99));

    printf("%s\n", strncat(str2, str1, 2));
    printf("%s\n", strncat(str2, str1, -1));
    printf("%s\n", strncat(str2, "llo, world!", 1234567890));

    printf("%d\n", strncmp(str1, "hello, ", 2));
    printf("%d\n", strncmp(str1, "hello, ", -5)); 
    printf("%d\n", strncmp(str1, "hello, ", 1234567890));
    printf("%d\n", strncmp(str1, "hello,w", 6));
    printf("%d\n", strncmp(str1, "hello,w", 7));

    return 0;
}

// copy at most n characters of string t to s; return s. Pad with '\0' if t has fewer than n characters.
char *strncpy(char *s, char *t, int n)
{
    char *u = s; // keep track of original pointer location
    while (n-- > 0 && (*s++ = *t++)) // copy t to s until t == '\0' or until n chars are copied
        ;
    while (n-- > 0) // pad s with '\0' if t has fewer than n characters.
        *s++ = '\0';
    *s = '\0'; // terminates string, even if already terminated (this will be char n + 1)
    return u;
}

// concatenate at most n characters of string t to string s, terminate s with '\0'; return s. ASSUMES s is big enough!
char *strncat(char *s, char *t, int n)
{
    char *u = s; // keep track of original pointer location
    while (*s) // while *s is not zero ('\0')
        s++; // move s to point to '\0'
    while (n-- > 0 && (*s++ = *t++)) // append t to end of s or until n chars are copied
        ;
    *s = '\0'; // terminates string, even if already terminated (this will be char n + 1)
    return u;
}

// compare at most n characters of string s to string t; return <0 if s<t, 0 if s==t, or >0 if s>t
int strncmp(char *s, char *t, int n)
{
    if (n <= 0) // handle bad input
        return 1;
    for ( ; n-- > 0; s++, t++) // check the first n chars, increment s and t after loop finishes each iteration
        if (*s != *t || *s == 0 || *t == 0) // they are different or at end of string, so compare and return results
            return *s - *t; // if s < t, <0, s > t, >0
    return 0; // s == t
}

Solution by Harsh Sharma

This solution uses the automatic variable as counter so there is no need to keep a separate counter.

My strncat uses strncpy internally after incrementing the pointer to end of string.

void strncpy(char *s, char *t, int n){
  while ((*s++ = *t++) && --n);
  if (!n)
    *s = '\0';
}

void strncat(char *s, char *t, int n){
  while (*s){
    s++;
  }
  strncpy(s, t, n);
}

int strncmp(char *s, char *t, int n){
  for (; (*s == *t && --n); s++, t++){
    if (!*s){
      return 0;
    }
  }
  return *s - *t;
}

Personal tools