Home > Enterprise >  How does strcat() fucntion actually works and its alternate
How does strcat() fucntion actually works and its alternate

Time:08-03

Let's take an example.

#include <stdio.h>
#include <string.h>
int main() {
    char str1[7] = "hello ";
    printf("Initial size of str1 is: %d\n", strlen(str1));
    char str2[] = "buddy";
    printf("%s\n", strcat(str1, str2));
    printf("Final size: %d\n", strlen(str1));
}

The output of the above program will be

Initial size of str1 is: 6
hello buddy
Final size: 11

--------------------------------
Process exited after 0.835 seconds with return value 0
Press any key to continue . . .

See? how the size of str1 changed from 7 to 11 (including null variable), regarding that what I think would have happened is :

  • Some function I do not know may have reallocated contiguous memory for str1 starting from same address as before i.e str1 with size strlen(str1) strlen(str2) 1 1 for null value, and then redefined it to get hello buddy.

If I am wrong please tell, if not then, what function is it and how does it work?

One more question: how can I write a code to do the above task without the use of strcat function.

I tried doing it using realloc() but didn't quite succeed may be that's because realloc() can only reallocate dynamically allocated memory, is it so?

CodePudding user response:

Buffer overflow

OP's code fails as strcat(str1,str2) attempts to write past the end of str1[] - result: undefined behavior (UB). @dimich

Instead use a larger destination buffer.

// char str1[7]="hello ";
char str1[7   5]="hello ";
char str2[]="buddy";
printf("%s\n",strcat(str1,str2));

Use correct print specifier

strlen() returns a size_t, not an int.

// printf("Initial size of str1 is: %d\n",strlen(str1));
printf("Initial size of str1 is: %zu\n",strlen(str1));

Tip: enable all warnings.

Alternative

One of many alternatives: copy str2 to the end of str1.

// printf("%s\n",strcat(str1,str2));
strcpy(str1   strlen(str1), strt2);
printf("%s\n",str1);

realloc()

realloc() can only reallocate dynamically allocated memory, is it so?

realloc() should not be used on pointers to non-allocated, non-NULL pointers.
In addition to re-allocating dynamically allocated memory, realloc() can start with no prior allocation.

char *p = realloc(NULL, size);
// just like
char *p = malloc(size);

Moral of the story

  • Be mindful of memory usage with string functions.
  • Enable all warnings.

CodePudding user response:

  1. You can only reallocate the memory you have dynamically allocated (ie using malloc family functions).

ee? how the size of str1 changed from 7 to 11 (including null variable), regarding that what I think would have happened is : A function idk which one, but it may have reallocated contiguous memory for str1 starting from same address as before i.e str1 with size strlen(str1) strlen(str2) 1 1 for null value, and then redefined it to get hello buddy. If i am wrong please tell, if not then , what function is it and how does it work?

  1. You are wrong. It is an example of an Undefined Behaviour. You have written some data outside array memory. Undefined Behavior means that your program behaviour from now is unpredictable. strcat does not reallocate any memory.

At last, how can i write a code to do the above task without the use of strcat function.

For example:

char *mystrcat(char *dest, const char *src, const int isDynamicMemory)
{
    size_t dlen = strlen(dest);
    size_t slen = strlen(src);

    if(isDynamicMemory)
    {
        dest = realloc(dest, slen   dlen   1);
        if(!dest) return NULL;
    }
    memcpy(dest   dlen, src, slen   1);
    return dest;
}

If dest was dynamically allocated you can reallocate it to the correct size by passing 1 as isDynamicMemory parameter

  1. Try to do not program in the main function. Use functions for such task like writing strcat like function.

CodePudding user response:

As i mentioned in the comment, overflow of str1 occures. strcat() doesn't know where and how strings are allocated. It searches end of destination string and appends source string overwriting null terminator. If you want dynamic allocation, it could be:

    char str1[]="hello ";
    printf("Initial size of str1 is: %d\n",(int)strlen(str1));
    char str2[]="buddy";
    char *str3 = malloc(strlen(str1)   strlen(str2)   2);
    if (!str3)
        return 1;
    strcpy(str3, str1);
    strcat(str3,str2);
    printf("%s\n",str3);
    printf("Final size: %d\n",(int)strlen(str3));
    free(str3);

This may be optimized little bit. strlen() calculates length of string at runtime and you have strings known at compile time. So we can use it:

    char str1[]="hello ";
    printf("Initial size of str1 is: %d\n",(int)strlen(str1));
    char str2[]="buddy";
    char *str3 = malloc(sizeof(str1)   sizeof(str2));
    if (!str3)
        return 1;
    strcpy(str3, str1);
    strcpy(str3   sizeof(str1), str2);
    printf("%s\n",str3);
    printf("Final size: %d\n",(int)strlen(str3));
    free(str3);

Also pay attention to strlen() return type: it is size_t. We should either cast it to int for printing with %d format or print with %z.

CodePudding user response:

As other answers (1, 2) mentioned, your code has a buffer overflow, a kind of undefined behaviour.

One more question: how can I write a code to do the above task without the use of strcat function.

Either:

  1. Use snprintf()
  2. Use strncpy()
  3. Implement your own version of strcat() (with some potential improvements)

Option 1

char str1[] = "hello ";
char str2[] = "buddy";
    
size_t total_size = sizeof(str1)   sizeof(str2) - 1; // sizeof counts \0 at the end
char res[total_size];

snprintf(res, total_size, "%s%s", str1, str2);

printf("sizeof(str1) = %zu\n", sizeof(str1));
printf("sizeof(str2) = %zu\n", sizeof(str2));
printf("total_size   = %zu\n", total_size);
printf("res = %s\n", res);
sizeof(str1) = 7
sizeof(str2) = 6
total_size   = 12
res = hello buddy

snprintf() lets you to control how many characters at most you want to print. This is comes handy in preventing a buffer overflow. For example, if you use sprintf(res, "%s%sxxx", str1, str2) in the above code, you'll get a BO. This is not the case with snprintf(res, total_size, "%s%sxxx", str1, str2).


Option 2

char str1[12] = "hello ";
char str2[] = "buddy";

strncpy(str1   strlen(str1), str2, strlen(str2));

printf("str1 = %s\n", str1);

Like in snprintf() abouve, strncpy() lets you to control how many characters at most you want to copy. strcpy() is very prone to BO: if str2 is larger than what str1 can hold, your code may behave in an undefined manner.


Option 3 (credit: Back to Basics)

char *concatenate(char *dest, char *src)
{
    while (*dest) dest  ;
    while ((*dest   = *src  ));
    return --dest;
}

Or:

char *concatenate_n(char *dest, char *src, int n)
{
    while (*dest) dest  ;
    for (int i = 0; i < n && (*dest   = *src  );   i) {}
    return --dest;
}

Example:

char str1[12] = "hello ";
char str2[] = "buddy";

concatenate(str1, str2);
// concatenate_n(str1, str2, strlen(str2)); // Safer

printf("str1 = %s\n", str1);
printf("p    = %s\n", p);

CodePudding user response:

The program has undefined behavior because in the call of strcat

printf("%s\n", strcat(str1, str2));

the memory beyond the character array str1 declared like

char str1[7] = "hello ";

is being overwritten.

There is no space in the array to accommodate the string "buddy" after the stored string "hello ".

The sizes of the array str1 and str2 themselves was not changed. The function strlen does not return the size of a character array. It returns the length of a string: a sequence of characters terminated by the zero-terminating character '\0'. To get the size of a character array you should use the operator sizeof.

Pay attention to that the return type of the function strlen is size_t. To output a value of this type you have to use the conversion specifier zu instead of d.

printf("Initial size of str1 is: %zu\n", strlen(str1));
                                 ^^^ 

To make the program correct you have to enlarge the character array str1. Here is a demonstration program.

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

int main( void )
{
    char str1[12] = "hello ";
    
    printf( "The size of str1 is: %zu\n", sizeof( str1 ) );
    printf( "The length of the stored string in str1 is: %zu\n", strlen( str1 ) );

    char str2[] = "buddy";

    printf("%s\n", strcat(str1, str2));

    printf( "The size of str1 is: %zu\n", sizeof( str1 ) );
    printf( "The length of the stored string in str1 is: %zu\n", strlen( str1 ) );
}

The program output is

The size of str1 is: 12
The length of the stored string in str1 is: 6
hello buddy
The size of str1 is: 12
The length of the stored string in str1 is: 11

As you can see from the output the size of the array str1 stays unchanged. What was changed is the length of the stored string in the array str1.

Pay attention to that the function strcat does not allocate or reallocate memory for arrays passed to the function as arguments. So if the destination array does not have enough memory to accommodate the appended string then the behavior is undefined because in this case the memory after the destination array will be overwritten.

  • Related