The short answer is: DO NOT USE strncpy()
.
You can read why here: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
The semantics of strncpy
are obscure, widely misunderstood and error prone. The size argument is the size of the destination array, not some limit to the number of characters to copy from the source. If the source string length is size
or larger, the destination will not be null terminated and if it is shorter, the remainder of the destination will be filled with null bytes ('\0'
).
The reasons for these choices are historical: strncpy()
was used to copy filenames into a memory structure for an archaic file system where filenames were limited in length.
The lack of null termination is so error prone that this function should never be used in production code, because the programmer or the maintainer will all too easily misunderstand the code's actual behavior and create bugs by modifying it.
If you must re-implement it as an assignment, you must implement the exact semantics. It you just want to have a handy string function that copies a string with truncation, choose a different name and probably a different argument order.
Here are examples of both:
char *strncpy_reimplemented(char *dest, const char *src, size_t n) {
size_t i;
for (i = 0; i < n && src[i] != '\0'; i++) {
dest[i] = src[i];
}
while (i < n) {
dest[i++] = '\0';
}
return dest;
}
char *pstrcpy(char *dest, size_t size, const char *src) {
size_t i;
if (size > 0) {
for (i = 0; i < size - 1 && src[i] != '\0'; i++) {
dest[i] = src[i];
}
dest[i] = '\0';
}
return dest;
}
Your copywords
function has problems:
- You do not null terminate the destination if the source string is longer than
size-1
- You dereference the source string beyond its length if it is shorter than
size-1
- You do not check if the value typed by the user is within the proper bounds for the source and the destination arrays.
You have further problems:
(words == STOP)
does not check whether the string read by fgets()
is quit
. You must first remove the trailing newline from the buffer and use strcmp()
to compare strings:
words[strcspn(words, "\n")] = '\0';
if (!strcmp(words, "quit")) {
printf("Good bye!\n");
return 0;
}
Here is a corrected and simplified version of your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *copywords(char *dest, const char *source, size_t n);
int main(void) {
char words[50];
char newwords[50];
int num;
for (;;) {
printf("Type a word, or type 'quit' to quit: ");
if (scanf("%49s", words) != 1) {
printf("Invalid input!\n");
return 0;
}
if (!strcmp(words, "quit")) {
printf("Good bye!\n");
return 0;
}
printf("Type the # of chars to copy: ");
if (scanf("%d", &num) != 1) {
printf("Invalid input!\n");
return 0;
}
copywords(newwords, words, num);
printf("The word was %s\n", words);
printf("and the copied word is %s\n", newwords);
}
}
char *copywords(char *dest, const char *source, size_t n) {
size_t i;
for (i = 0; i < n && source[i] != '\0'; i++) {
dest[i] = source[i];
}
dest[i] = '\0';
return dest;
}