Memory leak in a join string function - c

I'm trying to make a function that join 2 strings (str1 & str2) into a new one (str3) seperated by a char seperator. Unfortunately I have a memory leak from this function and I don't really know why since I free str3 at the end.
Example: str_join_string("ABC","DEF",'|') ---> "ABC|DEF"
Here's the code:
char *str_join_string(const char *str1, const char *str2, char separator) {
char *str3;
size_t len = str_length(str1)+ str_length(str2)+1;
size_t i = 0;
size_t j = 0;
str3 = (char * )calloc(len, sizeof(char));
if(str3 == NULL){
printf("Impossible d'allouer la mémoire");
return NULL;
}
while(str1[i] != '\0' && str1 != NULL){
str3[i] = str1[i];
i++;
}
str3[i] = separator;
i+=1;
while(str2[j] != '\0' && str2 != NULL){
str3[i+j] = str2[j];
j++;
}
str3[len] = '\0';
return str3;
}
I will add that I can't use any function like strcat() or anything that comes from string.h.
What Valgrind shows:
==4300== Searching for pointers to 3 not-freed blocks
==4300== Checked 131,560 bytes
==4300==
==4300== 4 bytes in 1 blocks are definitely lost in loss record 1 of 3
==4300== at 0x4C31B25: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4300== by 0x13E3B2: str_join_string (stringslib.c:238)
==4300== by 0x13E545: str_join_array (stringslib.c:283)
==4300== by 0x137065: JoinArrayTest_OneEmpty_Test::TestBody() (stringslib_test.cc:779)
==4300== by 0x1652A9: HandleSehExceptionsInMethodIfSupported<testing::Test, void> (gtest.cc:2611)
==4300== by 0x1652A9: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (gtest.cc:2647)
==4300== by 0x15A9DE: testing::Test::Run() [clone .part.658] (gtest.cc:2686)
==4300== by 0x15AC61: Run (gtest.cc:2677)
==4300== by 0x15AC61: testing::TestInfo::Run() [clone .part.659] (gtest.cc:2863)
==4300== by 0x15B350: Run (gtest.cc:2837)
==4300== by 0x15B350: testing::TestSuite::Run() [clone .part.660] (gtest.cc:3017)
==4300== by 0x15BAF4: Run (gtest.cc:2997)
==4300== by 0x15BAF4: testing::internal::UnitTestImpl::RunAllTests() (gtest.cc:5709)
==4300== by 0x165769: HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool> (gtest.cc:2611)
==4300== by 0x165769: bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) (gtest.cc:2647)
==4300== by 0x15AD82: testing::UnitTest::Run() (gtest.cc:5292)
==4300== by 0x11C08E: RUN_ALL_TESTS (gtest.h:2485)
==4300== by 0x11C08E: main (stringslib_test.cc:799)
==4300==
I hope that you can help me because I'm really lost right now.
------EDIT------
Yes I completely forgot to add the caller which is where I free the memory:
TEST(JoinStringTest, Simple) {
char *buf = str_join_string("ABC", "XYZ", '|');
ASSERT_TRUE(buf != NULL);
EXPECT_EQ(buf[0], 'A');
EXPECT_EQ(buf[1], 'B');
EXPECT_EQ(buf[2], 'C');
EXPECT_EQ(buf[3], '|');
EXPECT_EQ(buf[4], 'X');
EXPECT_EQ(buf[5], 'Y');
EXPECT_EQ(buf[6], 'Z');
EXPECT_EQ(buf[7], '\0');
free(buf);
}

For starters the function invokes undefined behavior because there is not enough memory allocated for the result string.
Instead of
size_t len = str_length(str1)+ str_length(str2)+1;
you have to write
size_t len = str_length(str1)+ str_length(str2)+2;
Moreover this statement
str3[len] = '\0';
also tries to write to the memory outside the allocated array.
It seems you mean
str3[i + j] = '\0';
Though you could remove this statement because you are using the function calloc that sets the allocated memory with zeroes. On the other hand, using calloc is inefficient in the function context.
And statements after the return statement
//...
return str3;
free(str3);
str3 = NULL;
are never executed.
Pay attention to that conditions like in this for loop
while(str1[i] != '\0' && str1 != NULL){
do not make a sense. At least the operands of the logical AND operator shall be exchanged like
while( str1 != NULL && str1[i] != '\0' ){
Though in any case the condition str1 != NULL is redundant or you could check the condition before the loop in an if statement.
Here is a demonstrative program that shows how the function can be defined (without using standard string functions) and called.
#include <stdio.h>
#include <stdlib.h>
size_t str_length( const char *s )
{
size_t n = 0;
while ( *s++ ) ++n;
return n;
}
char * str_join_string( const char *s1, const char *s2, char separator )
{
size_t n = str_length( s1 ) + str_length( s2 ) + sizeof( separator ) + 1;
char *s3 = malloc( n );
if ( s3 )
{
char *p = s3;
for ( ; *s1; ++s1 ) *p++ = *s1;
*p++ = separator;
for ( ; *s2; ++s2 ) *p++ = *s2;
*p = '\0';
}
return s3;
}
int main(void)
{
char *s = str_join_string( "ABC", "DEF", '|' );
if ( s ) puts( s );
free( s );
return 0;
}
The program output is
ABC|DEF
It is the user of the function shall provide arguments not equal to NULL.

return str3;
free(str3);
Looks at this snippet, do you think free() is ever going to get called?

Maybe because you return your function first and THEN free your buffer!?
https://learn.microsoft.com/en-us/cpp/c-language/return-statement-c?view=vs-2019
A return statement ends the execution of a function, and returns
control to the calling function

Related

C program to concatenate two pointer strings with functions

#include <stdio.h>
#include <stdlib.h>
char concaten(const char *str1,const char *str2);
int main()
{
printf("%s",concaten("Code","blocks"));
return 0;
}
char concaten(const char *str1,const char *str2) {
int i=0,j=0;
char *result;
while(*str1){
result[i++]=str1[i++];
}
while(*str2){
result[i+j++]=str2[j++];
}
return result;
}
I wrote this function to get two strings and add them to another third string; I don't understand where I am going wrong, as it doesn't print anything.
There are a number of problems with your concaten function.
First, it should be returning a char* pointer, not a char; thus, the declaration should look like this:
char* concaten(const char* str1, const char* str2);
Next, the function will need to allocate memory in which to store the concatenated strings; this can be done with the malloc() function, and the number of characters required will be the sum of the lengths of the two input strings plus one, for the required nul-terminator.
Third, the logic of your two loops is wrong. You are incrementing i and j twice per loop but not incrementing either of the source pointers.
Finally, you must add a nul-terminator at the end of your new string.
Here's a version with the above fixes applied:
char* concaten(const char* str1, const char* str2)
{
int i = 0, j = 0;
char* result = malloc(strlen(str1) + strlen(str2) + 1); // allow space for nul-terminator
while (*str1) {
result[i++] = *str1++; // Only increment i once and "str1" once
}
while (*str2) {
result[i + j++] = *str2++; // Only increment j once and "str2" once
}
result[i + j] = '\0'; // Add required nul-terminator
return result;
}
Also, as you have allocated memory (with the malloc call), you should release that when you're done with the data, using a call to free. Here's how your main might work:
int main(void)
{
char* answer = concaten("Code", "blocks");
printf("%s", answer);
free(answer);
return 0;
}
Note: You can also remove the j variable entirely, and just re-use the result[i++] expression in the second loop. I've left it in so that you can more easily relate my code to your own.
Your function has the return type char
char concaten(const char *str1,const char *str2);
but within the function you are returning the variable result
return result;
declared like a pointer of the type char *
char *result;
So the compiler will issue a message that you are trying to convert a pointer to an integer.
The function must be declared like
char * concaten(const char *str1,const char *str2);
The pointer result is not initialized and has an indeterminate value. You need to allocate memory where you will write concatenated strings.
The while loops in the function will be infinite if str1 and/or str2 are not empty strings due to conditions
while(*str1){
and
while(*str2){
These statements
result[i++]=str1[i++];
and
result[i+j++]=str2[j++];
invoke undefined behavior not only because the pointer result is not initialized but also because there is no sequence point between left and write operands where there is used the postfix increment operator ++.
Also the result string must be zero terminated.
If you are not allowed to use standard C string functions then your function can be implemented for example the following way
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = 0;
size_t n2 = 0;
while ( str1[n1] ) ++n1;
while ( str2[n2] ) ++n2;
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
char *p = result;
while ( *str1 ) *p++ = *str1++;
do
{
*p++ = *str2;
} while ( *str2++ );
}
return result;
}
Also you should not forget to free the allocated memory when the result string is not needed any more.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = 0;
size_t n2 = 0;
while ( str1[n1] ) ++n1;
while ( str2[n2] ) ++n2;
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
char *p = result;
while ( *str1 ) *p++ = *str1++;
do
{
*p++ = *str2;
} while ( *str2++ );
}
return result;
}
int main(void)
{
char *result = concatenate( "Code ", "blocks" );
if ( result != NULL ) puts( result );
free( result );
return 0;
}
The program output is
Code blocks
If you may use standard C string functions then the function concatenate can look as it is shown in the demonstrative program below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = strlen( str1 );
size_t n2 = strlen( str2 );
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
memcpy( result, str1, n1 );
memcpy( result + n1, str2, n2 + 1 );
}
return result;
}
int main(void)
{
char *result = concatenate( "Code ", "blocks" );
if ( result != NULL ) puts( result );
free( result );
return 0;
}
The program output is the same as shown above that is
Code blocks
Aside from the fact that your function should not return char but char*, the expression result[i++] = str1[i++]; is not correct it lacks a sequence point. Furthermore result is an unitialized pointer, it cannot hold any data, you would need to make it point to some valid memory location.
You could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* concatenate(const char* str1, const char* str2 ){
char* result = malloc(strlen(str1) + strlen(str2) + 1);
if (result){
char* temp = result;
while (*str1 != '\0'){
*temp++ = *str1++;
}
while (*str2 != '\0'){
*temp++ = *str2++;
}
*temp = '\0'; // don't forget to null terminate the string
}
return result; // if memory allocation fails a null pointer is returned
}
The direct usage of the function in the printf statement will not allow you to free the memory and you would have a memory leak if the program didn't finish immediately, in these cases it's best to have the returned pointer assigned as to not lose track of the allocated memory:
int main(void){
char *result = concatenate("Code", "blocks");
if(result){
printf("%s", result);
free(result);
}
return EXIT_SUCCESS;
}

How to copy char array in C without inner function

This is my code
char function(char *dst)
{
int i;
char *arr;
i = 0;
while(dst[i] != '\0')
{
arr[i] = dst[i];
i++;
}
dst[i] != '\0'
return(arr);
}
int main(void)
{
char a[] ="asdf"
printf("%s", function(a);
}
I want to copy *dst to empty *arr but my code didn't work.
I can't understand.
How can I copy array without inner function in C(ex_strcpy, memspy....)
Thank you
Apart from missing ; and making sure that the string being passed to the function is always a '\0' terminated one ( else the program will run into side effects strcpy causes ). and returning char* instead of char, you missed allocating memory for arr
// return char * instead of char
char* function(char *dst)
{
// Note - sizeof(dst) wont work
// Neither does sizeof(dst)/sizeof(char)
// allocate one extra for '\0'
size_t size_to_alloc = (strlen(dst) + 1) * (sizeof *arr);
char *arr = malloc( size_to_alloc );
char *p = arr;
for ( ; *dst ; p++, dst++)
*p = *dst;
*p = '\0';
return(arr);
}
If you want to dynamically copy an array, you'll need to allocate memory for the char array using malloc or other equivalent. Make sure you free the memory once you're done with it. I would suggest reading some posts on malloc and allocating memory in c.
This is probably a good place to start.
https://www.geeksforgeeks.org/dynamic-memory-allocation-in-c-using-malloc-calloc-free-and-realloc/
#include <stdio.h>
#include <stdlib.h>
char* function(char *dst, size_t length) {
int i;
// Allocating the memory needed for the char array.
char *arr = (char*) malloc (sizeof(char) * length);
i = 0;
while(dst[i] != '\0') {
arr[i] = dst[i];
i++;
}
arr[length - 1] = '\0';
return(arr);
}
int main(void) {
char a[] ="asdf";
// Getting length of the array
size_t length = sizeof(a) / sizeof(a[0]);
char* val = function(a, length);
printf("%s", val);
free(val);
}
You are missing the memory allocation and basically attempting to recode strdup. See below:
char *ft_strdup(const char *src)
{
char *dst;
int len;
len = 0;
while (src[len]) // no inner function
++len;
if (!(dst = malloc(sizeof(char) * (len + 1)))) // need 1 extra char to NULL terminate.
return NULL;
dst[len] = '\0';
while (--len > -1)
dst[len] = src[len];
return dst;
}
Note that it makes sense to code your own version of strdup and include it in your program library as this function is not part of the C Standard.
If there is a possibility of copying strings without using c functions, perhaps it can be done by doing what c functions do.
it may be interesting to see what strcpy does:
https://code.woboq.org/userspace/glibc/string/strcpy.c.html
char *
STRCPY (char *dest, const char *src)
{
return memcpy (dest, src, strlen (src) + 1);
}
infact it uses memcpy: https://code.woboq.org/gcc/libgcc/memcpy.c.html
and here the magic...
void *
memcpy (void *dest, const void *src, size_t len)
{
char *d = dest;
const char *s = src;
while (len--)
*d++ = *s++;
return dest;
}
and strlen: https://code.woboq.org/userspace/glibc/string/strlen.c.html
You can use memcpy() to copy memory directly, like in Memcpy, string and terminator and https://www.gnu.org/software/libc/manual/html_node/Copying-Strings-and-Arrays.html In C any string has to be terminated by \0 (sentinel value)
#include<stdio.h>
#include<string.h>
int main()
{
char source[] = "World";
char destination[] = "Hello ";
/* Printing destination string before memcpy */
printf("Original String: %s\n", destination);
/* Copies contents of source to destination */
memcpy (destination, source, sizeof(source));
/* Printing destination string after memcpy */
printf("Modified String: %s\n", destination);
return 0;
}
source : https://www.educative.io/edpresso/c-copying-data-using-the-memcpy-function-in-c

wrong redimension of a string in c

I am trying to make a function that receives a dynamic string and removes from it all occurrences of the character also passed as a parameter.
The string should finally contain just enough space to contain characters not deleted
void delete(char *cad, char c){
int i, cont = 0;
char *aux = NULL;
i = 0;
while(cad[i] != '\0'){
if(cad[i] != c){
aux = (char*)realloc(aux, sizeof(char) * cont + 1);
aux[cont] = cad[i];
cont++;
}
i++;
}
cad = (char*)realloc(cad, sizeof(char) * cont);
i = 0;
while(aux[i] != '\0'){
cad[i] = aux[i];
i++;
}
}
Now I have a segmentation fault
You do not check the result of the realloc.
IMO it will be better to return the pointer to the new string instead of using double pointer. Double pointer may cause hard to track memory leaks, and function will not work with the const strings - for example string literals
You do not null character terminate the string.
In this example, I did not change your allocation algorithm but in real life more efficient will be first to count how much memory you need to allocate, allocate it and then process the string again:
char *delete(const char *cad, char c){
size_t nchars = 0;
char *aux = NULL;
char *temp;
while(*cad)
{
if(*cad != c)
{
temp = realloc(aux, sizeof(*temp) * nchars + 1);
if(temp)
{
aux = temp;
aux[nchars++] = *cad;
}
else
{
/* handle allocation error */
free(aux);
aux = NULL;
break;
}
}
cad++;
}
if(aux) aux[nchars] = 0;
return aux;
}
Some minor changes: use objects instead of types in sizeof and do not cast result of malloc. You can also add NULL pointer parameter check.
Every time you are reallocing inside the while loop, you are essentially giving the variable aux a new address each time.
I advise you to not do that and allocate the memory you want to allocate at the start of the function.
You will need to calculate how much memory you would need before allocating the memory. That is, count how much element you would delete.
If you want me to further elucidate or add a code fragment, please feel free to ask it in the comments.
Instead of many calls to realloc() I would just perform an in-place substitution of the characters; this substitution leaves unused allocated characters at the end of the string and is illustrated by the delete_no_realloc() function below.
If you want to get rid of these unused ending characters in the allocated string, then only one call to realloc() is needed as illustrated by the delete() function below.
Note that when a function uses realloc() on a parameter which is a pointer, it must obtain the address of this pointer to adjust it with the result of realloc().
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t // new length
delete_no_realloc(char *cad,
char c)
{
size_t w=0;
for(size_t r=0; cad[r]; ++r)
{
char ch=cad[r];
if(ch!=c)
{
cad[w++]=ch; // store and advance write index
}
}
cad[w]='\0'; // ensure string termination
return w;
}
void
delete(char **cad_ptr,
char c)
{
char *cad=*cad_ptr; // forget this embarrassing indirection
size_t new_length=delete_no_realloc(cad, c);
cad=realloc(cad, new_length+1);
if(cad==NULL)
{
abort();
}
*cad_ptr=cad; // don't forget to adjust the string
}
int
main(void)
{
const char *msg="this is a message";
char *cad=malloc(strlen(msg)+1);
if(cad==NULL)
{
abort();
}
strcpy(cad, msg);
printf("before: <%s>\n", cad);
delete(&cad, 's'); // pass the address of the string
printf("after: <%s>\n", cad);
free(cad);
return 0;
}
You can simplify your delete() function by simply using a read and write index within the original string, removing all c characters found, and then make a single call to realloc() to reallocate storage to exactly fit the remaining characters.
You can do something like:
void delete (char **cad, char c)
{
if (!*cad || !**cad) /* check if cad is NULL or empty-string */
return;
size_t write = 0; /* write index */
for (size_t read = 0; (*cad)[read]; read++) { /* loop over each char in cad */
if ((*cad)[read] != c) /* if char not c */
(*cad)[write++] = (*cad)[read]; /* copy incrementing write */
}
(*cad)[write] = 0; /* nul-terminate */
void *tmp = realloc (*cad, write + 1); /* realloc to exact size */
if (!tmp) { /* validate realloc */
perror ("realloc-cad");
return;
}
*cad = tmp; /* assign reallocated block to *cad */
}
A full example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void delete (char **cad, char c)
{
if (!*cad || !**cad) /* check if cad is NULL or empty-string */
return;
size_t write = 0; /* write index */
for (size_t read = 0; (*cad)[read]; read++) { /* loop over each char in cad */
if ((*cad)[read] != c) /* if char not c */
(*cad)[write++] = (*cad)[read]; /* copy incrementing write */
}
(*cad)[write] = 0; /* nul-terminate */
void *tmp = realloc (*cad, write + 1); /* realloc to exact size */
if (!tmp) { /* validate realloc */
perror ("realloc-cad");
return;
}
*cad = tmp; /* assign reallocated block to *cad */
}
int main (int argc, char **argv) {
if (argc < 3) {
fputs ("usage: ./prog \"string with c\" c\n", stderr);
return 1;
}
size_t len = strlen (argv[1]);
char *s = malloc (len + 1);
if (!s) {
perror ("malloc-s");
return 1;
}
memcpy (s, argv[1], len + 1);
printf ("%s (%zu chars)\n", s, len);
delete (&s, *argv[2]);
printf ("%s (%zu chars)\n", s, strlen(s));
free (s);
}
Example Use/Output
$ ./bin/delete_c_realloc "nmyn ndogn nhasnn nnfleasnnn" n
nmyn ndogn nhasnn nnfleasnnn (28 chars)
my dog has fleas (16 chars)
Look things over and let me know if you have questions.
There are four main problems with your function implementation.
The first one is that the function accepts the pointer to the source string by value. That is the parameter cad is initialized by the value of the pointer used as an argument. As a result changing the variable cad does not influence on the original pointer.
The second one is that you are not checking whether a call of realloc was successful. As a result the function can invoke undefined behavior.
The third one is that it is inefficient to reallocate the string each time when a new character is appended.
And at last the fourth one is that the result dynamically allocated array does not contain a string because you forgot to append the terminating zero character '\0'.
If you want to change within the function a value of the original pointer you should either to return from the function the result pointer obtained in the function and assign it to the original pointer in the caller. Or you should pass the original pointer to the function by reference. In C passing by reference means passing an object (that can be a pointer) indirectly through a pointer to it.
Here is a demonstrative program that shows the function implementation when the original pointer is accepted by the function by reference.
The function also returns a pointer to the result string that can be checked in the caller whether the reallocation of dynamic memory within the function was successful.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * remove_char( char **s, char c )
{
char * result = *s;
if ( c != '\0' )
{
char *dsn = *s;
const char *src = *s;
do
{
if ( *src != c )
{
if ( dsn != src )
{
*dsn = *src;
}
++dsn;
}
} while ( *src++ );
char *tmp = realloc( *s, ( dsn - *s ) * sizeof( char ) );
if( tmp != NULL ) *s = tmp;
result = tmp;
}
return result;
}
int main(void)
{
char *s = malloc( 12 );
strcpy( s, "H#e#l#l#o!" );
puts( s );
if ( remove_char( &s, '#' ) ) puts( s );
free( s );
return 0;
}
The program output is
H#e#l#l#o!
Hello!
Another approach is to write a function that does not change the source string but creates dynamically a new string that contains the source string excluding the specified character. Such a function is more flexible because you can call it with string literals. If the source string also was dynamically allocated then the caller of the function after a successful call it can just free the source string.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * remove_copy( const char *s, char c )
{
size_t src_len = strlen( s );
size_t dsn_len = src_len;
if ( c != '\0' )
{
for ( const char *p = s; ( p = strchr( p, c ) ) != NULL; ++p )
{
--dsn_len;
}
}
char *result = malloc( ( dsn_len + 1 ) * sizeof( char ) );
if ( result != NULL )
{
const char *src_s = s;
char *dsn_s = result;
if ( dsn_len != src_len )
{
for ( const char *p = src_s;
( p = strchr( src_s, c ) ) != NULL;
src_s = p + 1 )
{
if ( p - src_s != 0 )
{
memcpy( dsn_s, src_s, p - src_s );
dsn_s += p - src_s;
}
}
}
strcpy( dsn_s, src_s );
}
return result;
}
int main(void)
{
char s[] = "H#e#l#l#o!";
puts( s );
char *p = remove_copy( s, '#' );
if ( p != NULL ) puts( p );
free( p );
return 0;
}
The program output is the same as shown for the preceding demonstrative program that is
H#e#l#l#o!
Hello!

string replace using dynamically allocated memory

I am using the below function to replace a sub-string in a given string
void ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *result = NULL;
int i, cnt = 0;
int tolen = strlen(to);
int fromlen = strlen(from);
if (*inputString == NULL)
return;
// Counting the number of times old word
// occur in the string
for (i = 0; (*inputString)[i] != '\0'; i++)
{
if (strstr((&(*inputString)[i]), from) == &(*inputString)[i])
{
cnt++;
// Jumping to index after the old word.
i += fromlen - 1;
}
}
// Making new string of enough length
result = (char *)malloc(i + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return;
memset(result, 0, i + cnt * (tolen - fromlen) + 1);
i = 0;
while (&(*inputString))
{
// compare the substring with the result
if (strstr(*inputString, from) == *inputString)
{
strncpy(&result[i], to, strlen(to));
i += tolen;
*inputString += fromlen;
}
else
{
result[i++] = (*inputString)[0];
if ((*inputString)[1] == '\0')
break;
*inputString += 1;
}
}
result[i] = '\0';
*inputString = result;
return;
}
The problem with the above function is memory leak. Whatever memory is allocated for inputString will be lost after this line.
*inputString = result;
since I am using strstr and moving pointer of inputString *inputString += fromlen; inputString is pointing to NULL before the above line. So how to handle memory leak here.
Note: I dont want to return the new memory allocated inside the function. I need to alter the inputString memory based on new length.
You should use a local variable to iterate over the input string and avoid modifying *inputString before the final step where you free the previous string and replace it with the newly allocated pointer.
With the current API, ReplaceSubStr must be called with the address of a pointer to a block allocated with malloc() or similar. Passing a pointer to local storage or a string literal will have undefined behavior.
Here are a few ideas for improvement:
you could return the new string and leave it to the caller to free the previous one. In this case, you would take the input string by value instead of by address:
char *ReplaceSubStr(const char *inputString, const char *from, const char *to);
If the from string is empty, you should either insert the to string between each character of the input string or do nothing. As posted, your code has undefined behavior for this border case.
To check if the from string is present at offset i, use memcmp instead of strstr.
If cnt is 0, there is nothing to do.
You should return an error status for the caller to determine if memory could be allocated or not.
There is no need to initialize the result array.
avoid using strncpy(). This function has counter-intuitive semantics and is very often misused. Read this: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
Here is an improved version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ReplaceSubStr(char **inputString, const char *from, const char *to) {
char *input = *inputString;
char *p, *q, *result;
size_t cnt;
size_t tolen = strlen(to);
size_t fromlen = strlen(from);
if (input == NULL || fromlen == 0)
return 0;
// Counting the number of times old word occurs in the string
for (cnt = 0, p = input; (p = strstr(p, from)) != NULL; cnt++) {
p += fromlen;
}
if (cnt == 0) // no occurrence, nothing to do.
return 0;
// Making new string of enough length
result = (char *)malloc(strlen(input) + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return -1;
for (p = input, q = result;;) {
char *p0 = p;
p = strstr(p, from);
if (p == NULL) {
strcpy(q, p0);
break;
}
memcpy(q, p0, p - p0);
q += p - p0;
memcpy(q, to, tolen);
q += tolen;
p += fromlen;
}
free(*inputString);
*inputString = result;
return 0;
}
int main() {
char *p = strdup("Hello world!");
ReplaceSubStr(&p, "l", "");
printf("%s\n", p); // prints Heo word!
free(p);
return 0;
}
You cannot obviously free the input as it can be a literal, some memory you don't control. That would cripple your function even more than now.
You could return the old value of inputString so you'd be able to free it if needed.
char *ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *old_string = *inputString;
...
return old_string;
}
The caller is responsible to free the contents of old_string if needed.
If not needed (we have to workaround the char ** input by assigning a valid writable array to a pointer to be able to pass this pointer:
char input[]="hello world";
char *ptr = input;
ReplaceSubStr(&ptr, "hello", "hi");
// input is now "hi world" in a different location
free(ptr); // when replaced string isn't needed
if needed:
char *input = strdup("hello world");
char *old_input = ReplaceSubStr(&input, "hello", "hi");
free(old_input);
or just
free(ReplaceSubStr(&input, "hello", "hi"));
then always (when replaced string isn't needed):
free(input);
The only constraint is that you cannot use a constant string literal as input (const char *input = "hello world") because of the prototype & the possible return of a char * to pass to free.

Custom concat function in C with pointer

I try to code my own concatenation function in C without library, but I have issue and I don't know where it comes from.
To do my function I use pointers of char.
This is my Code :
#include <stdio.h>
#include <stdlib.h>
int longueur(char *str)
{
int i =0;
while(str[i] != '\0')
{
i++;
}
return i;
}
void concat(char* source, char* dest)
{
int longStr1 = (longueur(source));
int longStr2 = (longueur(dest));
int i=0, j=0;
char* temp = dest;
free(dest);
dest = (char*) realloc(dest, ((longStr1 + longStr2)* sizeof(char)));
/*dest[0] = temp[0]; <------ If I do this it will generate issue, so the bellow code too*/
while(temp[i] != '\0')
{
dest[i] = temp[i];
i++;
}
while(source[j] != '\0')
{
dest[i] = source[j];
i++;
j++;
}
dest[i] = '\0';
}
int main()
{
char *str1 = "World";
char *str2 = "Hello";
concat(str1, str2);
printf("-------------\n%s", str2);
return 0;
}
EDIT
I read all your answer, so I changed my concat function to :
void concat(char* source, char* dest)
{
int longStr1 = (longueur(source));
int longStr2 = (longueur(dest));
int i=0, j=0;
dest = (char*) malloc((longStr1 + longStr2)* sizeof(char) + sizeof(char));
while(dest[i] != '\0')
{
dest[i] = dest[i];
i++;
}
while(source[j] != '\0')
{
dest[i] = source[j];
i++;
j++;
}
dest[i] = '\0';
}
Now I don't have issue but my code only display "Hello"
In addition to all the good comments and solutions: realloc can give you a different pointer and you must return that pointer. So your function signature should be:
void concat(char* source, char** dest)
{
int longStr1 = (longueur(source));
int longStr2 = (longueur(dest));
int i=0, j=0;
char* temp = *dest, *temp2;
if ((temp2 = realloc(dest, ((longStr1 + longStr2)+1))==NULL) return;
*dest= temp2;
while(temp[i] != '\0')
{
*dest[i] = temp[i];
i++;
}
while(source[j] != '\0')
{
*dest[i] = source[j];
i++;
j++;
}
*dest[i] = '\0';
}
..and this assumes the function will only be called with a dest that was allocated with malloc. And sizeof(char) is always 1. (This resulting function is not optimal.)
--EDIT--
Below the correct, optimized version:
void concat(char* source, char** dest)
{
int longSrc = longueur(source);
int longDst = longueur(dest);
char *pDst, *pSrc;
if ((pDst = realloc(*dest, longSrc + longDst + 1))==NULL) return;
if (pDst != *dest) *dest= pDst;
pDst += longSrc;
pSrc= source;
while(pSrc)
*pDst++ = *pSrc++;
*pDst = '\0';
}
In your code
free(dest);
and
dest = (char*) realloc(dest, ((longStr1 + longStr2)* sizeof(char)));
invokes undefined behavior as none of them use a pointer previously allocated by malloc() or family.
Mostly aligned with your approach, you need to make use of another pointer, allocate dynamic memory and return that pointer. Do not try to alter the pointers received as parameters as you've passed string literals.
That said, you need to have some basic concepts clear first.
You need not free() a memory unless it is allocated through malloc() family.
You need to have a char extra allocated to hold the terminating null.
Please see this discussion on why not to cast the return value of malloc() and family in C..
If your concatenation function allocates memory, then, the caller needs to take care of free()-ing the memory, otherwise it will result in memory leak.
After you have freed dest here:
free(dest);
You cannot use this pointer in following call to realloc:
dest = (char*) realloc(dest, ((longStr1 + longStr2)* sizeof(char)));
/*dest[0] = temp[0]; <------ If I do this it will generate issue, so the bellow code too*/
man realloc
void *realloc(void *ptr, size_t size);
The realloc() function changes the size of the memory block
pointed to by ptr to size bytes. (...)
But this pointer is invalid now and you cannot use it anymore. When you call free(dest), the memory dest points to is being freed, but the value of dest stays untouched, making the dest a dangling pointer. Accessing the memory that has already been freed produces undefined behavior.
NOTE:
Even if free(dest) is technically valid when called on pointer to memory allocated by malloc (it is not an error in your function to call free(dest) then), it is incorrect to use this on pointer to literal string as you do in your example (because str2 points to string literal it is an error to pass this pointer to function calling free on it).
Given your original use, perhaps you would find a variant like this useful
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
size_t longueur ( const char * str ) { /* correct type for string lengths */
size_t len = 0;
while (*str++ != '\0') ++len;
return len;
}
char * concat ( const char * first, const char * second ) {
const char * s1 = first ? first : ""; /* allow NULL input(s) to be */
const char * s2 = second ? second : ""; /* treated as empty strings */
size_t ls1 = longueur(s1);
size_t ls2 = longueur(s2);
char * result = malloc( ls1 + ls2 + 1 ); /* +1 for NUL at the end */
char * dst = result;
if (dst != NULL) {
while ((*dst = *s1++) != '\0') ++dst; /* copy s1\0 */
while ((*dst = *s2++) != '\0') ++dst; /* copy s2\0 starting on s1's \0 */
}
return result;
}
int main ( void ) {
const char *str1 = "Hello";
const char *str2 = " World";
char * greeting = concat(str1, str2);
printf("-------------\n%s\n-------------\n", greeting);
free(greeting);
return 0;
}
In this variant, the two inputs are concatenated and the result of the concatenation is returned. The two inputs are left untouched.

Resources