Memory leak with string arrays in c - c

char **add_string(char **existing, const char *string){
size_t size = 0;
while (NULL != existing[size])
{
++size;
}
char **arr = realloc(existing, (size + 2) * sizeof *arr);
arr[size] = malloc(strlen(string) + 1);
strcpy(arr[size], string);
arr[size+1] = '\0';
return arr;
}
void free_strings(char **strings)
{
size_t size = 0;
while (NULL != strings[size])
{
free(strings[size]);
++size;
}
}
I am having a memory leak at line
char **arr = realloc(existing, (size + 2) * sizeof *arr);
I thought existing memory was suppose to be free'd when using realloc? How do I fix this memory leak?
edit: added my free_string function and the main I am using to run the program.

You did not append the array pointed to by the pointer existing with null pointer. Thus in this loop
while (NULL != existing[size])
{
++size;
}
the function has undefined behavior.
It seems you mean the following
char ** add_string( char **existing, const char *string )
{
size_t size = 0;
while ( NULL != existing[size] ) ++size;
char **arr = realloc( existing, ( size + 2 ) * sizeof *arr );
if ( arr != NULL )
{
arr[size] = malloc( strlen( string ) + 1 );
strcpy( arr[size], string );
arr[size+1] = NULL;
}
return arr;
}
Also the function free_strings is incorrect. It should look like
void free_strings( char **strings )
{
size_t size` = 0;
do
{
free( strings[size] );
} while ( strings[size++] != NULL );
free( strings );
}

Related

Create a 2d array of strings using dynamically allocation in c

I have to store some strings given in the args of a c code. I iterate over them but I can't store them properly because I don't know the length of those and neither their number. The better method should be a 2d array of pointers, so I can dynamically allocate memory for every new string. The problem is that I'm new on c and I have a lot of confusion about that technique. I tried to initialize a double pointer and use a function to insert elements, it allocates space for another column(new string) and set the length(size of string).
char** files;
int files_i=0;
void insert(char** root,char[] str)
{
root=(char **)malloc(sizeof(char *));
root[files_i]=(char *)malloc(sizeof(char)*sizeof(str));
root[files_i*sizeof(str)]=str;
i++;
}
I pass to the function the double pointer and the string I need to "append". It's not working and I have also really big doubts on how to iterate over that...
What you need is the following
char **files = NULL;
size_t files_i = 0;
//...
int insert( char ***root, const char str[], size_t i )
{
char *p = malloc( strlen( str ) + 1 );
int success = p != NULL;
if ( success )
{
char **tmp = realloc( *root, ( i + 1 ) * sizeof( char * ) );
if ( success )
{
strcpy( p, str );
tmp[i] = p;
*root = tmp;
}
else
{
free( p );
}
}
return success;
}
and then in the caller you can write for example
if ( insert( &files, some_string, files_i ) ) ++files_i;
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int insert( char ***root, const char str[], size_t i )
{
char *p = malloc( strlen( str ) + 1 );
int success = p != NULL;
if ( success )
{
char **tmp = realloc( *root, ( i + 1 ) * sizeof( char * ) );
if ( success )
{
strcpy( p, str );
tmp[i] = p;
*root = tmp;
}
else
{
free( p );
}
}
return success;
}
int main(void)
{
char **files = NULL;
size_t files_i = 0;
if ( insert( &files, "Hello", files_i ) ) ++files_i;
if ( insert( &files, "World", files_i ) ) ++files_i;
for ( size_t i = 0; i < files_i; i++ )
{
puts( files[i] );
}
for ( size_t i = 0; i < files_i; i++ )
{
free( files[i] );
}
free( files );
return 0;
}
Its output is
Hello
World
use strlen(str) instead of sizeof(str) for calculating the string length.
root[files_i]= malloc(strlen(str) + 1); // +1 for null character at the end of the string
if(!root[file_i]) {return;}
if you want to copy string, use strcpy instead of using = operator. Or use strdup (if you use strdup, you do not need to allocate memory for character pointer).
strcpy(root[files_i],str); // copy string str to "file_i" position of array root
if you use the global counter file_i, you should use realloc for root, because the size of root has to be vary (i think it's typo, the i++ should change to file_i++ ?).
root= realloc(root, sizeof(char *) * (file_i + 1));
// do not forget to check the return value of malloc or realloc function.
if(!root) {return;}
Do not cast malloc or realloc function. See at Do I cast the result of malloc?

Passing 2D array of char as argument

I made a function that loads lines and save it to array which dinamically alocates everytime the getline function load a new line but It gives me segmentation fault when i want to print all saved lines from array in the end of my code. Could you please tell me where the problem is ? When i remove the function and put everything to main it works fine.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void load(char **array,int *index) {
int riadok;
size_t len = 0;
char *buffer = NULL;
printf("Slova:\n");
while( ( riadok = getline(&buffer,&len,stdin) ) != -1 ) {
if (riadok == 1){
break;
}
array = (char**) realloc ( array, ( *index + 1 ) * sizeof( char* ) );
array[*index] = (char*) malloc ( (riadok + 1) * sizeof(char*) );
strcpy( array[*index], buffer );
*index = *index + 1;
}
}
int main()
{
int index = 0;
int i;
char **array = NULL;
load(array,&index);
for (i = 0; i < index; i++ )
printf("%s",array[i]);
return 0;
}
In C arguments are pass by value. When you pass array in main to the function then it just copied to the parameter array in function load. The statement
array = (char**) realloc ( array, ( *index + 1 ) * sizeof( char* ) );
modify the pointer array to new allocated memory.
Pass the address of array from main with some changes to load function
void load(char ***array, int *index) { // Use pointer to pointer to pointer
int riadok;
size_t len = 0;
char *buffer = NULL;
printf("Slova:\n");
while( ( riadok = getline(&buffer,&len,stdin) ) != -1 ) {
//printf("%d\n", riadok);
//printf("%s\n", buffer);
if (riadok == 1){
break;
}
*array = realloc ( *array, ( *index + 1 ) * sizeof( char* ) );
if(*array == NULL)
break;
array[0][*index] = malloc(riadok + 1);
if(array[0][*index])
strcpy( array[0][*index], buffer );
//else
// printf("Memory Not allocated\n");
*index = *index + 1;
}
free(buffer);
}
and call it from main as
load(&array,&index);

C - Array of Strings & Mysterious Valgrind Error

I'm trying to allocate a two-dimensional array of strings, where the last member is always a NULL pointer, i.e. an empty array consists of a single NULL pointer. I keep getting Valgrind errors but I have no idea why.
/*Initializes the string array to contain the initial
* NULL pointer, but nothing else.
* Returns: pointer to the array of strings that has one element
* (that contains NULL)
*/
char **init_array(void)
{
char **array = malloc(sizeof(char *));
array[0] = NULL;
return array;
}
/* Releases the memory used by the strings.
*/
void free_strings(char **array)
{
int i = 0;
while(array[i] != NULL){
free(array[i]);
i++;
}
//free(array[i]);
free(array);
}
/* Add <string> to the end of array <array>.
* Returns: pointer to the array after the string has been added.
*/
char **add_string(char **array, const char *string)
{
int i = 0;
while(array[i] != NULL){
i++;
}
array = realloc(array, (i+1) * sizeof(char *));
char *a = malloc(strlen(string)+1);
array[i] = malloc(strlen(string)+1);
strcpy(a, string);
strcpy(array[i], a);
free(a);
return array;
}
Here's the Valgrind error:
==375== Invalid read of size 8 ==375==    at 0x402FCE: add_string (strarray.c:40) ==375==    by 0x401855: test_add_string (test_source.c:58) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  Address 0x518df08 is 0 bytes after a block of size 8 alloc'd ==375==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==375==    by 0x402FF4: add_string (strarray.c:43) ==375==    by 0x401855: test_add_string (test_source.c:58) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  ==375== Invalid read of size 8 ==375==    at 0x4018F7: test_add_string (test_source.c:70) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  Address 0x518e308 is 0 bytes after a block of size 40 alloc'd ==375==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==375==    by 0x402FF4: add_string (strarray.c:43) ==375==    by 0x401855: test_add_string (test_source.c:58) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  ==375== Invalid read of size 8 ==375==    at 0x402F8D: free_strings (strarray.c:25) ==375==    by 0x401AA6: test_add_string (test_source.c:91) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  Address 0x518e308 is 0 bytes after a block of size 40 alloc'd ==375==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==375==    by 0x402FF4: add_string (strarray.c:43) ==375==    by 0x401855: test_add_string (test_source.c:58) ==375==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==375==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==375==    by 0x40256A: main (test_source.c:194) ==375==  ==376== Invalid read of size 8 ==376==    at 0x402FCE: add_string (strarray.c:40) ==376==    by 0x401DCD: test_make_lower (test_source.c:111) ==376==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==376==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==376==    by 0x40256A: main (test_source.c:194) ==376==  Address 0x518e6d8 is 0 bytes after a block of size 8 alloc'd ==376==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==376==    by 0x402FF4: add_string (strarray.c:43) ==376==    by 0x401DCD: test_make_lower (test_source.c:111) ==376==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==376==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==376==    by 0x40256A: main (test_source.c:194) ==376==  ==376== Invalid read of size 8 ==376==    at 0x402F8D: free_strings (strarray.c:25) ==376==    by 0x401F5C: test_make_lower (test_source.c:130) ==376==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==376==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==376==    by 0x40256A: main (test_source.c:194) ==376==  Address 0x518e9d0 is 0 bytes after a block of size 32 alloc'd ==376==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==376==    by 0x402FF4: add_string (strarray.c:43) ==376==    by 0x401DCD: test_make_lower (test_source.c:111) ==376==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==376==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==376==    by 0x40256A: main (test_source.c:194) ==376==  ==377== Invalid read of size 8 ==377==    at 0x402FCE: add_string (strarray.c:40) ==377==    by 0x4022DB: test_sort_strings (test_source.c:155) ==377==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==377==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==377==    by 0x40256A: main (test_source.c:194) ==377==  Address 0x518f3e8 is 0 bytes after a block of size 8 alloc'd ==377==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==377==    by 0x402FF4: add_string (strarray.c:43) ==377==    by 0x4022DB: test_sort_strings (test_source.c:155) ==377==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==377==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==377==    by 0x40256A: main (test_source.c:194) ==377==  ==377== Invalid read of size 8 ==377==    at 0x402F8D: free_strings (strarray.c:25) ==377==    by 0x40246A: test_sort_strings (test_source.c:174) ==377==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==377==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==377==    by 0x40256A: main (test_source.c:194) ==377==  Address 0x518f6e0 is 0 bytes after a block of size 32 alloc'd ==377==    at 0x4C245E2: realloc (vg_replace_malloc.c:525) ==377==    by 0x402FF4: add_string (strarray.c:43) ==377==    by 0x4022DB: test_sort_strings (test_source.c:155) ==377==    by 0x405F90: srunner_run_all (in /tmc/test/test) ==377==    by 0x4028B2: tmc_run_tests (tmc-check.c:122) ==377==    by 0x40256A: main (test_source.c:194) ==377==
That's because you didn't add new NULL-terminator to the array add_string(). So subsequent calls of add_array() fail to find end of array without going out of bounds.
I think you need realloc with larger length:
array = realloc(array, (i + 2) * sizeof(char *));
And then save NULL-terminator to array[i + 1]:
array[i + 1] = NULL;
Why didn't you try using linked lists for that? I feel bad for realloc() per each add_string()
This function
char **add_string(char **array, const char *string)
{
int i = 0;
while(array[i] != NULL){
i++;
}
array = realloc(array, (i+1) * sizeof(char *));
char *a = malloc(strlen(string)+1);
array[i] = malloc(strlen(string)+1);
strcpy(a, string);
strcpy(array[i], a);
free(a);
return array;
}
is wrong. It does not add a new slot into the array and you do not set the last element to NULL.
The valid function can look like
char **add_string( char **array, const char *string )
{
int i = 0;
while ( array[i++] != NULL );
array = realloc( array, ( i + 1 ) * sizeof( char * ) );
array[i] = NULL;
array[i-1] = malloc( strlen( string ) + 1 );
strcpy( array[i-1], string );
return array;
}

Dynamic array of pointers

I have an array of pointers to strings.
char **array;
I declare it this way and not char *array[N] because this array won't have a static number of elements.
Declaring the array this way, I will probably have to realloc the sizeof it every time I add a new element (pointer to string).
int main(void)
{
char **array;
char *word = "lolol";
char *word2 = "blabla";
return 0;
}
Could you give me an example on how I should "create space" in the array in order to store pointers to these strings?
The best way of doing it is probably by making a struct
This way, you can resize it, and add as many strings as you want without needing to choose a specific size.
Note: setting the string_array's capacity and size to 0 is necessary for it to work.
You could do it by a function like this instead:
void load_array(string_array *array)
{
array->size = 0;
array->capacity = 0;
}
And call it like this:
load_array(&my_array);
Note, when getting the value from one of these arrays using [], you must call it like this:
my_array.arr[index]
This is because you must refer to the pointer in the array struct, which is as arr (char **)
I have tested the below, and it works perfectly.
# include <stdio.h>
typedef struct string_array
{
char **arr;
unsigned capacity, size;
} string_array;
void add_to_array(string_array *array, char *str)
{
if(array->capacity == 0)
{
array->arr = (char **)malloc((array->capacity = 3) * sizeof(char *));
array->arr[array->size++] = str;
}
else if(array->capacity == array->size)
{
array->arr = (char **)realloc(array->arr, (array->capacity *= 1.5) * sizeof(char *));
array->arr[array->size++] = str;
}
else
{
array->arr[array->size++] = str;
}
}
int main(void)
{
char *str1 = "Hello World";
char *str2 = "Hello World2";
char *str3 = "Hello World3";
char *str4 = "Hello World4";
char *str5 = "Hello World5";
string_array my_array;
my_array.capacity = 0;
my_array.size = 0;
add_to_array(&my_array, str1);
add_to_array(&my_array, str2);
add_to_array(&my_array, str3);
add_to_array(&my_array, str4);
add_to_array(&my_array, str5);
// and so on
for (int i = 0; i < my_array.size; ++i)
{
printf(my_array.arr[i]);
printf("\n");
}
free(my_array.arr);
getchar(); // this means pressing enter closes the console
return (0);
}
Here is a demonstrative program
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
size_t N = 2;
char **array = malloc( N * sizeof( char * ) );
if ( !array ) return 1;
char *word = "lolol";
char *word2 = "blabla";
array[0] = word;
array[1] = word2;
char *word3 = "Hello";
++N;
array = realloc( array, N * sizeof( char * ) );
if ( !array ) return 2;
array[2] = word3;
for ( size_t i = 0; i < N; i++ ) puts( array[i] );
free( array );
return 0;
}
The program output is
lolol
blabla
Hello
Simply allocate some room for pointers in your array, and free it when you're done.
char *word = "lolol";
char *word2 = "blabla";
char** array = malloc(2*sizeof(char*));
array[0] = word;
array[1] = word2;
free(array);
You can change that 2*sizeof(char*) to a N*sizeof(char*) if you want more elements.

What is the correct way to allocate and pass a string?

I have a string, for which i allocate memory. After that, i pass this string to another function, and then from the second function i pass it to a third function. The problem is, that after the tird function completes, i can access/use the variable. But after the second function completes, i can't access it's value in the first function. So obviously i'm doing somethin wrong, but i don't what.
Could someone please help me?
Of course if anyone has better idee, how i should do what i want, i would also appreciate it.
My code so far:
#define MAX_SIZE 100
void Func1()
{
char *Test = NULL;
Test = ( char * )malloc( MAX_SIZE*sizeof( char ) );
if ( Test == NULL )
{
return;
}
Func2( Test );
if ( Test!= NULL ) free( Test);
}
void Func2(char *string)
{
Func3( &string);
}
void Func3( char **string)
{
if (Len > MAX_SZIE )
{
char *tmp = NULL;
tmp = ( char * )realloc( *string, Len + 1 );
if ( !tmp )
{
return;
}
*string = tmp;
memset( *string, 0, sizeof( *string ) );
}
memcpy( *string, SomeOtherString, Len );
free( SomeOtherString );
}
Thanks in advance!
Update:
Updated the function, with the current code i have now:
bool __stdcall Function( DataToSendBack *DataArray )
{
char *Result = NULL;
char InString[ MAX_SIZE ] = { 0 };
int DataLen = 0;
bool bRetVal = false;
__try
{
Result = ( char * )malloc( MAX_SIZE );
if ( Result == NULL )
{
__leave;
}
memset( InString, 0, sizeof( InString ) );
memset( Result, 0, sizeof( Result ) );
memcpy( InString, DataArray->StringIn, strlen( DataArray->StringIn ) );
Result = GetInfo( InString, Result, &DataLen );
if ( Result == NULL )
{
MessageBoxA(NULL,"error",NULL,NULL);
__leave;
}
if ( strlen( ErrorMsg ) > 0 ) strcpy( DataArray->ErrorMessage, ErrorMsg );
if ( DataArray->RequiredSize < DataLen )
{
DataArray->RequiredSize = DataLen;
strcpy( DataArray->ErrorMessage, ErrorMsg );
__leave;
}
DataArray->Result = _strdup( Result );
bRetVal = true;
}
__finally
{
if ( Result != NULL ) free( Result );
}
return bRetVal;
}
char * GetInfo( char *String, char *Result, int *DataLen )
{
bool bRetVal = false;
__try
{
if ( DoStuff( String, &Result, DataLen ) )
{
bRetVal = true;
}
}
__finally
{
// free other things
}
return Result;
}
bool DoStuff( char *MyString, char **Result, int *DataLen )
{
__try
{
//Call function which returns dwsize
*DataLen = dwSize * 2;
OtherString = SomFunction();
if ( strlen( OtherString ) > MAX_SIZE )
{
char *tmp = NULL;
tmp = ( char * )realloc( *Result, strlen( OtherString ) );
if ( !tmp )
{
__leave;
}
*Result = tmp;
memset( *Result, 0, sizeof( *Result ) );
}
memcpy( *Result, OtherString, strlen( OtherString ) );
free( OtherString );
bretVal = true;
}
__finally
{
// free other things
}
return bretVal;
}
Func3() modifies the pointer that Func1() allocated. You need to pass it back to Func1():
void Func1()
{
char *Test = NULL;
if (NULL == (Test = malloc(MAX_SIZE) ) {
// Handle case of OOM error
return;
}
// Func2 may modify Test
Test = Func2( Test );
if (NULL == Test) {
// Handle case of error in Func3
}
free(Test); // Test = NULL;
}
/**
* Reads and modifies (through a call to Func3) a pointer to string
*/
char * Func2(char *string)
{
Func3( &string);
return string;
}
Also, in Func3(), you need to:
memset( *string, 0, Len + 1 );
to zero the whole string.
But actually you're writing OtherString into *string, so zeroing all those bytes, as WhozCraig points out, is unnecessary. What you should really do is make sure you have enough space, and zero the one byte immediately after the string, if it is necessary. So that would be
strncpy(*string, OtherString, Len);
(*string)[Len] = 0x0;
or more efficiently (since strncpy will zero whatever comes after the copy of OtherString, up to Len bytes)
size_t new_len = strlen(OtherString);
if (new_len <= Len) {
// Copy, including last zero
memcpy(*string, OtherString, new_len+1);
} else {
// Copy Len bytes, from 0 to Len-1
memcpy(*string, OtherString, Len);
// Zero last byte to make it a valid C string
(*string)[Len] = 0x0;
}
or
size_t new_len = min(Len, strlen(OtherString));
memcpy(*string, OtherString, new_len);
// Zero last byte to make it a valid C string
(*string)[new_len] = 0x0;
Update
For testing purposes, this is the Func3() I used:
#define OtherString "To be or not to be, that is the question\n"
#define Len 10240
#define min(a,b) (((a)<(b))?(a):(b))
void Func3( char **string)
{
char *tmp = realloc( *string, Len+1);
if (NULL == tmp)
{
return;
}
*string = tmp;
size_t new_len = strlen(OtherString);
new_len = min(Len, new_len);
memcpy(*string, OtherString, new_len);
// Zero last byte to make it a valid C string
(*string)[new_len] = 0x0;
}
In one case it returns "To be or not to be", in the other (Len=16) it returns "To be or not to ", as expected.
You're realloc()ating the string in Func3(). However, the new, potentially modified pointer is never returned to Func1(), because Func2() takes the pointer by value, not by pointer. Hence, you will access a potentially deallocated memory region.
The solution: depending on what you need, you might possibly want to pass the original string by pointer to Func2() (and perhaps to Func1()) as well.

Resources