0

I am currently learning C. One of the things I want to do in order to get better is to mimic the "strcat" function from the header. In order to make a more precise copy I compare the output of my own function with the output of the original one. They use identical, but different char arrays. However, the output of my own function changes if it is called after the original one. Here are the two code variations:

In the first one the output of the original is "1234567890123456", as expected, and the output of my own is "123456789123456". I have found after several dozens of various checks: the '0' char disappears from the src[0] once it is passed to the function as a parameter:

int main()
{

    char dest[10] = "123456789";
    char src[70] = "0123456";
    printf ("Concatenate |%s| and |%s|\n", dest, src); 


    char dest1[10] = "123456789";
    char src1[70] = "0123456";
    printf ("Normal people do: %s\n", strcat(dest1, src1));    
    printf ("Your filthy code: %s\n",  ft_strcat(dest, src));


    return 0;
}

However, if I simply relocate the printf of my function like this:

int main()
{

    char dest[10] = "123456789";
    char src[70] = "0123456";
    printf ("Concatenate |%s| and |%s|\n", dest, src);     
    printf ("Your filthy code: %s\n",  ft_strcat(dest, src));


    char dest1[10] = "123456789";
    char src1[70] = "0123456";
    printf ("Normal people do: %s\n", strcat(dest1, src1));    
    return 0;
}

both functions will return "1234567890123456" as an output.

The question is: how is this possible? I am curious because these functions address two different sets of arrays and therefore should not affect one another. For some reason my function's behaviour varies depending on when it is called in the body of the int main().

Please notice, that I have intentedly made the dest[] array 10 chars big, as I want to mimic the behaviour of original "strcat" in unexpected situations as well. Although that is probably the core of the problem, and if I change it, the code works fine in both aforementioned cases. As I said, I am interested in the nature of the problem, not in the way to solve it.

I am not sure whether the text of my function is relevant for this issue, but here it is, just in case:

char *ft_strcat(char *dest, char *src)
{
    int dest_end;
    int src_count;

    dest_end = 0;
    src_count = 0;
    while (dest[dest_end])
        dest_end++;
    while (src[src_count])
    {
        dest[dest_end] = src[src_count];        
        src_count++;
        dest_end++;
    }
    dest[dest_end + 1] = '\0';
    return dest;
}

Thank you for any answers.

  • 3
    Welcome to SO! If you check the [man page](https://linux.die.net/man/3/strcat): _...the dest string must have enough space for the result. If dest is not large enough, program behavior is unpredictable; buffer overruns are a favorite avenue for attacking secure programs._. – ggorlen May 01 '20 at 07:19
  • @ggorlen Thanks you for the answer. I am aware of the fact that such a code can and will lead to overflow and unpredictable behaviour, Does that mean, however, that I cannot reproduce the behaviour of the original function in such circumstances at any means? I intended to mimic the "strcat" function even in cases such as overflows and other unexpected situations, therefore I intentedly made *dest[]* array smaller than needed. And thank you for the welcoming word, it is very heart-warming. – Emil Nadimanov May 01 '20 at 07:28
  • `unpredictable behaviour` yes, that means it's unpredictable. It's called that [the behavior of the program is undefined](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior), and yes, you can't expect a program with undefined behavior to behave in any way. Because it's not defined what will happen. So anything can happen. – KamilCuk May 01 '20 at 07:30
  • 1
    `how is this possible?` - to answer such question, inspect the assembly code of your program. `because these functions address two different sets of arrays` - your compiler could have optimized them to one array. – KamilCuk May 01 '20 at 07:37
  • @KamilCuk I deeply appreciate your help. Thanks! – Emil Nadimanov May 01 '20 at 07:38

2 Answers2

0

This is due to the memory overlapping .... because you are declaring the size of dest1[10] and dest[10] instead of declare these two array with larger length like this

char dest1[100];
char dest[100];

your problem will be solved.

0
char dest[10] = "123456789";
char src[70] = "0123456";
printf ("Your filthy code: %s\n",  ft_strcat(dest, src));

char dest1[10] = "123456789";
char src1[70] = "0123456";
printf ("Normal people do: %s\n", strcat(dest1, src1)); 

In both cases, using your own function ft_strcat(dest, src)) or strcat() either, you invoke undefined behavior because the char array´s pointed to by destination are not capable to hold the initialized string plus the appended strings pointed to by source. When writing beyond the bounds of an array, the behavior of the program is undefined.


Solution:

The arrays dest and dest1 need to have at least 18 elements in total, 10 for holding "0123456789", 7 for the appending "0123456" + 1 element for the terminating null character \0:

char dest[18] = "123456789";
char dest1[18] = "123456789";

char src[70] = "0123456";
char src1[70] = "0123456";

printf ("Your filthy code: %s\n",  ft_strcat(dest, src));
printf ("Normal people do: %s\n", strcat(dest1, src1));

Side notes:

  1. The array scr1 is redundant, as it only serves for providing the appending string and it exactly equals with its content to the string in src. You can use src for the second appending process, too:
printf ("Normal people do: %s\n", strcat(dest1, src));
  1. src and src1 hold each 70 elements, which is unnecessary. They both need only to have 8 elements, not 70:
src[8] = "0123456";
src1[8] = "0123456";

A more handy approach is to omit the amount of elements and let the compiler automatically detect the amount of elements required, which also ensures that you wan´t forget the null character in counting:

src[] = "0123456";
src1[] = "0123456";
  1. As the pointer src nor its pointed object shall get modified inside of ft_strcat() declare src as char const * const src.

dest can be made const too, but without making it a pointer to const char: char * const dest.

You can also adapt the return type of ft_strcat(), regarding the pointer dest.

The result is:

char * const ft_strcat(char * const dest, char const * const src)
Community
  • 1
  • 1
  • @EmilNadimanov I saw that you temporary accepted my answer but then rejected the vote. I corrected the code regarding the placement of the `const` qualifier. If you got any issues with it or want to ask for more information, just let me know. – RobertS supports Monica Cellio May 02 '20 at 07:39