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; }