Recreating strncpy()

90 Views Asked by At

I have an assignment where I have to recreate the strlcpy function from C without any libraries. I wrote the code and submitted it, but it was marked as wrong. Do you know why?

I've attached the diff text I was given that represents why I got the question wrong (it looks like gibberish to me) and a solution two of my peers put that was marked as correct. Can you tell when the two functions might ever have different outputs?

I've asked many of my peers in addition to ChatGPT 3.5 and 4.0 and ClaudeAI. The AI's initially said that the functions were different, but were all unable to give any input that would result in a different outcome. My Code:

char    *ft_strncpy(char *dest, char *src, unsigned int n)
{
    unsigned int    i;

    i = 0;
    while (i < n)
    {
        if (src[i])
            dest[i] = src[i];
        else
            dest[i] = '\0';
        i++;
    }
    return (dest);
}

Peers' Code:

{
        unsigned int    i;
        i = 0;
        while (i < n && src[i] !='\0')
        {
                dest[i] = src[i];
                i++;
        }
        while (i < n)
        {
                dest[i] = '\0';
                i++;
        }
        return (dest);
}

I'll out the diff text in a comment so it doesn't take up so much space

$> diff -U 3 user_output_test1 test1.output | cat -e
--- user_output_test1 2024-03-20 15:16:50.490408519 +0000$
+++ test1.output 2024-03-20 15:16:50.470408242 +0000$
@@ -2,69 +2,69 @@$
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d6b4c6156524a4e4f64505a6d62724d$
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004d6b4c61524256466c575441675a444f$
2

There are 2 best solutions below

1
tofro On

Your code has at least three flaws (the first two are clearly bugs, the last one is just unnecessary code):

  1. It reads beyond the source string end (because it always reads at least n bytes from there, even if the source string is shorter)
  2. It doesn't (even if the man page clearly says it should) pad the destination buffer with \000 bytes if the source string is shorter than the destination buffer. Instead, it writes random garbage from beyond the source string end.
  3. The "if-else"-clause is entirely useless. In the one case where it triggers the else (at the terminating NUL) the else branch does exactly what the if branch would have done. It basically boils down to dst [i] = src[i];

Otherwise, your code does something like strncpy(), but doesn't handle the corner cases as it should. With a bit more experience, you might learn that half-working code (like yours) is even more dangerous than code which is not working at all, because the problems are typically found very late and will only occur spuriously. Just a bit more into that:

Your 'strncpy' might seem to work work for a long time when your user (who calls the function) is supplying reasonable buffer sizes (n) in comparison with the average string length of the source string. Imagine a very generous user decides to supply 10000 bytes of destination buffer, and your source string is only 60 characters long in a buffer space of 80. That would mean you're reading 9920 bytes beyond your source buffer into potentially unallocated memory, which is very likely to cause a segmentation fault, which will be hard to find (because "it used to work all the time!").

0
chux - Reinstate Monica On

In addition to @tofro good answer,

OP's (and Peers') code has 4 type issues.

char *ft_strncpy(char *dest, char *src, unsigned int n);
// vs.
char *strncpy(char * restrict s1, const char * restrict s2, size_t n);
  • Use size_t n. unsigned int n is too narrow for long strings. String length may exceed UINT_MAX, but not SIZE_MAX.

  • Calling const char *ref = "abc"; ft_strncpy(dest,ref, 3); leads to a compiler error, but strncpy(dest,ref, 3) does not.

  • Calling char s[9] = "abc"; buft_strncpy(s+1, s, 8); should lead to a compiler warning as the buffers overlap and using restrict indicates to a compiler to flag that.

  • Pedantic: src[i] !='\0' is a problem on exceedingly rare non-2's complement encoding and char is signed as that stops the loop on +0 and -0. String functions should perform as if the characters were unsigned char. "For all functions in this subclause, each character shall be interpreted as if it had the type unsigned char (and therefore every possible object representation is valid and has a different value)."

Candidate alternative (akin to "Peers' Code"):

char *alt_strncpy(char * restrict s1, const char * restrict s2, size_t n) {
  unsigned char *u1 = (unsigned char *) s1;
  const unsigned char *u2 = (const unsigned char *) s2;

  while (n > 0 && *u2) {
    n--;
    *u1++ = *u2++;
  }

  while (n > 0) {
    n--;
    *u1++ = '\0';
  }

  return s1;
}