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.estr1
with sizestrlen(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:
- 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?
- 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
- Try to do not program in the
main
function. Use functions for such task like writingstrcat
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:
- Use
snprintf()
- Use
strncpy()
- 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.