2

I'm trying to make an algorithm for strncpy
I read how it work in the manual and made my algorithm
then I made a test to see if there's any difference between my ft_strncpy and the real strncpy

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

char    *ft_strncpy(char *dest, char *src, unsigned int n)
{
    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);
}

int main(void)
{
    char src1[] = "123456789q00000";
    char dest1[] = "zzz";
    printf("my output\n");
    printf("src1: %s\n", src1);
    ft_strncpy(dest1, src1, 8);
    printf("dest1: %s\n", dest1);

    printf("\nhow it should be\n");
    char src2[] = "123456789q00000";
    char dest2[] = "zzz";
    printf("src2: %s\n", src2);
    strncpy(dest2, src2, 8);
    printf("dest2: %s\n", dest2);
}

the how it should be section supposed to be the right output of the real strncpy but I'm having weird outputs just like in the code I provided
I get this error

*** buffer overflow detected ***: terminated
signal: aborted (core dumped)

what I understood from the manual is
strncpy copies n first characters from *src to *dest
so a code like

    char src2[] = "123456789q00000";
    char dest2[] = "zzz";
    printf("src2: %s\n", src2);
    strncpy(dest2, src2, 10);
    printf("dest2: %s\n", dest2);

should output : 123456789q as the value of dest I'm I missing smth here?

AzerSD
  • 148
  • 1
  • 9
  • 1
    [The "real" `strncpy`](https://en.cppreference.com/w/c/string/byte/strncpy) have a corner-case where it might not add the terminating null character. – Some programmer dude Feb 07 '22 at 07:58
  • 5
    But the above doesn't matter as your example code have *undefined behavior*! Your arrays `dest1` and `dest2` have enough space for the string `"zzz"` (including null terminator) but no more (i.e. it will have four elements). By using it as a destination for a longer string you go out of bounds and have the mentioned undefined behavior. – Some programmer dude Feb 07 '22 at 07:59
  • C does not have a string class so you need to handle all allocation manually. This is why I recommend to study arrays, then pointers, then strings, in that order. – Lundin Feb 07 '22 at 08:05
  • @Someprogrammerdude so the size of `dest` has to allways be >= size of `src`? – AzerSD Feb 07 '22 at 08:06
  • 1
    Yes. As any beginners book, tutorial or class should have taught you: C doesn't have dynamic arrays. – Some programmer dude Feb 07 '22 at 08:08
  • @Someprogrammerdude I think I got it so how can set this question as closed or idk should I Just leave it? – AzerSD Feb 07 '22 at 08:13
  • Write `strncpy(dest, src, sizeof(dest))` and make sure `dest` is an array (not a pointer). If you think you need something else, think again. Then bear in mind that you **cannot** printf `dest` as is because **it is not a string** (it may or may not have the terminated null character). Add `dest[sizeof(dest)-1] = '\0';` if you want to make it a string. – n. m. could be an AI Feb 07 '22 at 08:14
  • You can write your own answer and mark it as accepted. It is the best way to close questions when you learn the answer before it is actually answered by someone else, because you have to think about the answer before writing it down. – n. m. could be an AI Feb 07 '22 at 08:17
  • If the size of `dest` >= size of `src` (including the null terminator) then you don't need `strncpy`. `strcpy` works better in this case. You only need `strncpy` when you may have to *truncate* the source (and then you need to write your own null terminator). – n. m. could be an AI Feb 07 '22 at 08:20
  • 2
    Unrelated, props to your understanding and implementation of at least the `strncpy` algorithm. You'd be amazed how many seasoned C programmers do not know that function pads the destination array with zeroes in the case where the source string is shorter than `n`. It is fairly well known it does not terminate if the source meets/exceeds the target size, but the pad with zeros action is surprisingly elusive. `strncpy` *always* writes `n` slots; no more, no less. – WhozCraig Feb 07 '22 at 08:35
  • 2
    For details regarding why `strncpy` is dangerous, see [Is strcpy dangerous and what should be used instead?](https://software.codidact.com/posts/281518) – Lundin Feb 07 '22 at 11:13
  • The 3rd argument to strncpy must never be greater than the size of dest. – stark Feb 07 '22 at 12:45

2 Answers2

2

Arrays in c are not dynamic so
I can't just initialize it with a string then try changing it to a bigger one
that will lead to an undefined behavior

besides there's a corner-case where it might not add the terminating null character so I won't be able to print it with printf because it's not a string anymore, it's an array.

conclusion: Be more cautious when modifying strings.
(answer by the help of @Someprogrammerdude and @n. 1.8e9-where's-my share m.)

AzerSD
  • 148
  • 1
  • 9
0

strncpy(dest2, src2, 10); has undefined behavior because it will modify 10 bytes starting with dest2[0], yet dest2 is only 4 bytes long.

You have the same problem in ft_strncpy(dest1, src1, 8); for the same reason.

Nothing can be concluded from tests with code that has undefined behavior. The code must be corrected first.

You code for ft_strncpy() seems correct, except for the type unsigned int for the length argument which should be size_t. Using unsigned int is probably a requirement for your school project, but a debatable one, among other counter-productive requirements such as banning for loops and mandating parentheses around the return value.

Here is a modified version:

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

char    *ft_strncpy(char *dest, char *src, unsigned int n)
{
    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);
}

void dump(const char *prefix, const char *p, size_t size)
{
    size_t i;

    printf("%s: %s  [", prefix, p);
    i = 0;
    while (i < size) {
        printf(" %.2X", (unsigned char)p[i]);
        i++;
    }
    printf(" ]\n");
}
    
int main(void)
{
    char src1[] = "zzz";
    char dest1[] = "123456789q00000";

    printf("my output\n");
    printf("src1: %s\n", src1);
    ft_strncpy(dest1, src1, 8);
    dump("dest1: ", dest1, sizeof dest1);

    printf("\nhow it should be\n");
    char src2[] = "zzz";
    char dest2[] = "123456789q00000";
    printf("src2: %s\n", src2);
    strncpy(dest2, src2, 8);
    dump("dest1: ", dest1, sizeof dest1);

    return 0;
}

Output:

my output
src1: zzz
dest1: : zzz  [ 7A 7A 7A 00 00 00 00 00 39 71 30 30 30 30 30 00 ]

how it should be
src2: zzz
dest1: : zzz  [ 7A 7A 7A 00 00 00 00 00 39 71 30 30 30 30 30 00 ]
chqrlie
  • 131,814
  • 10
  • 121
  • 189