memory leak when concatenating strings into array - c

I have one function, alloc_str, which takes a string pointer and an array of pointers. It dynamically increases the size of the array by one and appends the string into the array. I have run a GDB debugger and highlighted my memory leak and const error below.
My expected input/output:
array = alloc_str(array, "test_1");
array = alloc_str(array, "test_2");
array = alloc_str(array, "test_3");
--> ["test_1", "test_2", "test_3"]
My alloc_str function:
char **alloc_str(char **existing, const char *add)
{
int length = 0; //find the length of the array
for (; existing[length]; length++)
{
}
//allocate memory to copy array array
char **existing_c = (char **)calloc(length + 2, sizeof(char *));
for (int i = 0; i < length; i++) //copy original array into new array
{
existing_c[i] = existing[i];
}
//possible memory leak error
strncat(existing_c, add, sizeof(existing_c) - strlen(existing_c) - 1);
existing_c[sizeof(existing_c)-1] = '\0';
//possible memory leak error
strncpy(existing, existing_c, sizeof(existing - 1));
s_copy[sizeof(destsize)-1] = '\0'; //error here
free(existing);
return existing_c;
}
void free_array(char **strings) //free's data in array, should be fine
{
int length = 0;
for (; strings[length]; length++)
{
}
strings = (char **)calloc(length + 2, sizeof(char *));
}
My main function:
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main(){ //should be fine
char **array = NULL;
char **test;
array = (char **)calloc(1, sizeof(char *)); //array has no strings yet
array = alloc_str(array, "test_1");
array = alloc_str(array, "test_2");
array = alloc_str(array, "test_3");
for (test = array; *test; test++)
{
printf("%s\n", *test);
}
free_array(array);
}
My error:
Subscript of pointer to function type 'void (const void *, void *, size_t)' (aka 'void (const void *, void *, unsigned long)')

There are multiple problems:
char **alloc_str(char **existing, const char *add)
{
int length = 0; //find the length of the array
for (; existing[length]; length++)
{
}
//allocate memory to copy array array
char **existing_c = (char **)calloc(length + 2, sizeof(char *));
for (int i = 0; i < length; i++) //copy original array into new array
{
existing_c[i] = existing[i];
}
////////////////////////////////////
//possible memory leak error
strncat(existing_c, add, sizeof(existing_c) - strlen(existing_c) - 1);
existing_c[sizeof(existing_c)-1] = '\0';
//possible memory leak error
strncpy(existing, existing_c, sizeof(existing - 1));
s_copy[sizeof(destsize)-1] = '\0'; //error here
////////////////////////////////////
free(existing);
return existing_c;
}
The part marked with //////////////////////////////////// does not make much sense.
You allocated an array of pointers. Don't treat it like a string. It is no string.
Instead simply assign the new pointer to the end of the array and add terminator again.
existing_c[length] = add;
existing_c[length+1] = NULL;
With that terminater you could use normal malloc instead of calloc because you assign all elements of the array anyway.
Besides the problem with allocation, you have another memory leak:
void free_array(char **strings) //free's data in array, should be fine
{
int length = 0;
for (; strings[length]; length++)
{
}
strings = (char **)calloc(length + 2, sizeof(char *));
}
You pass a pointer to an array of pointers. This array takes some memory that you allocated with calloc earlier.
Then you allocate a bit more memory and assign the address to local variable string.
This has two problems:
The memory that was allocated earlier is not freed.
The memory you allocate in this function is not accessible outside of that function.
In the end, your free_array function does not free anything but consumes more memory.
Another problem might be present with the strings that you store in that array.
In your example you use string literals. These are static objects and there is no need to free them.
If you will use your functions to store pointers to dynamically allocated string as well, you will need to take care about allocating and freeing the strings as well.

strncat() works on a memory buffer containing a NUL-terminated (aka "C") string:
char buf[10] = {'a', 'b', 'c', '\0'};
strncat(buf, "def", sizeof(buf) - strlen(buf) - 1);
assert(strcmp(buf, "abcdef") == 0); // buf now equals to "abcdef"
https://ideone.com/fWXk8C
(Well, the use of strlen() kinda killed the benefit of strncat() over good ol' strcat() but that's another story...)
So it's very different from what you want to do in your exercise. You actually don't need either of strncat() or strncpy().

Related

Attempting to free address that was not malloced , error on realloc

Question : https://leetcode.com/problems/find-and-replace-in-string/
"""
char * findReplaceString(char * s, int* indices, int indicesSize, char ** sources, int
sourcesSize, char ** targets, int targetsSize){
int len = strlen(s);
char *copyS;
char *copy = (char*) malloc(sizeof(char)*len);
memcpy(copy, s, sizeof(char)*len);
copyS = copy;
int x = indicesSize-1;
int indexArr[1001] = {0};
int y;
for(int j=0; j<indicesSize; j++)
{
indexArr[indices[j]] = j;
}
qsort(indices, indicesSize, sizeof(int), cmp);
while((x >= 0))
{
y = indexArr[indices[x]];
copy = copyS+(indices[x]);
if(!(strncmp(copy, sources[y], strlen(sources[y]))))
{
copy = (char *)realloc(copy, sizeof(char)*(sizeof(copy) + sizeof(targets[y])));
strcpy(copy, targets[y]);
}
x--;
}
return copyS;
}
I am getting a runtime error due to the use of realloc. I was trying to modify the input string 's'. Got a runtime error due to realloc: Trying to free memory that was not malloced.
So I malloced new string pointer , *copy. Still getting same error when I use realloc on copy
There are several problems with the code.
For starters it is unclear whether the dynamically allocated array pointed to by the pointer copy shall contain a string or not.
If it shall contain a string then instead of
char *copy = (char*) malloc(sizeof(char)*len);
memcpy(copy, s, sizeof(char)*len);
you need to write
char *copy = (char*) malloc(sizeof(char)*( len + 1 ));
memcpy(copy, s, sizeof(char)*( len + 1 ));
Also it is unclear why there is used the magic number 1001 in this declaration
int indexArr[1001] = {0};
The pointer copyS was assigned with the address of the initially allocated memory
char *copyS;
char *copy = (char*) malloc(sizeof(char)*len);
memcpy(copy, s, sizeof(char)*len);
copyS = copy;
but then you are trying to reallocate the memory
copy = (char *)realloc(copy, sizeof(char)*(sizeof(copy) + sizeof(targets[y])));
As a result the pointer copyS can have an invalid value. And this pointer with an invalid value is returned from the function
return copyS
In turn the pointer copy is changed within the while loop
while((x >= 0))
{
y = indexArr[indices[x]];
copy = copyS+(indices[x]);
//..
So after such an assignment it does not point to the previously allocated memory extent. Hence using the pointer in the call of realloc
copy = (char *)realloc(copy, sizeof(char)*(sizeof(copy) + sizeof(targets[y])));
invokes undefined behavior.
And again this statement
copy = copyS+(indices[x]);
also invokes undefined behavior because after the memory reallocation the pointer copyS can be invalid.
Once you do this
copy = copyS+(indices[x]);
you can no longer use 'copy' as an argument to realloc or free. The pointer you pass to these functions must be the value returned by a prior malloc or realloc (or calloc)
Save the original 'copy' in a variable like 'originalCopy'

Why am I getting a "segmentation fault 11" when I have assigned enough memory?

I have created double pointer char to be used as a 2d array to store strings. The append function is meant to add the string provided to the end of the array, the num_strings pointer is provided to keep track of the elements in the array (since I can't use sizeof). It seems that at some point, the function isn't allocating enough memory but I can't seem to figure out where and can't find any other issues.
I have already tried giving both the outer array and the inner array large amounts of memory, much more than they need. The issue persists. I have also tried copying the string to the array after the function had run.
int main(int argc, char *argv[]) {
char **strings = NULL;
int num_strings = 0;
append(&strings, &num_strings, "Alex");
append(&strings, &num_strings, "Edward");
// Do things with array
for (int i = 0; i < num_strings; i++) {;
printf("%s\n", strings[i]);
}
// Free memory after use
for (int i = 0; i < num_strings; i++) {
free(strings[i]);
}
free(strings);
strings = NULL;
return 0;
}
void append(char ***array, int * num_strings, char *string) {
if (*array == NULL) {
*array = malloc(sizeof(*array)); // start with enough room for 1 item (pointer)
} else {
// reallocate memory for new item
*array = realloc(*array, (((*num_strings) + 1) * sizeof(*array)));
}
printf("Char Size: %lu\n", sizeof(char));
printf("Given Size: %lu\n", sizeof(***(array)));
*(array[*num_strings]) = malloc((strlen(string) + 1) * sizeof(***(array + 0)));
strcpy(*(array[*num_strings]), string);
(*num_strings)++; // increment the number of strings
}
The output of the program should be the two strings, at the moment it only prints the first and then crashs due to the segmentation fault.
The problem is there are a couple instances of *(array[*num_strings]) that should be (*array)[*num_strings].
The difference is that the first form tries to index through the pointer passed to the function, as if the passed strings were an array, corrupting the caller's stack. The corrected version first derefernces the pointer, then indexed through the target as desired.
There are also a few places where sizeof(*array) is used where it should be sizeof(**array). x = malloc(sizeof(x)) is never correct. But this isn't causing a visible problem.

String Arrays: Delete original array and then return copy

I am new with C and I am trying to understand allocating strings.
I am trying to create a function called adding_string. It takes in an array of zero or more strings that has a null in the final location. Next, it makes a shallow copy of the array that is + 1 location bigger, then appends a copy of the string str onto the array. Finally, it deletes the original array and returns the new copy
This is what I have so far:
char **adding_string(char **array, const char *str)
{
size_t num = strlen(str) + 1;
char *final= (char *)malloc(num);
strncpy(final, str, num);
free(array);
//The above code would create a copy of the string "str".
//Then it puts that into the array.
//Not sure if free(array); would be the right method
//Having issues with returning final too
return final;
}
In the main function, you would have something like:
char **array = NULL;
char **lines;
array = (char **)calloc(1, sizeof(char *));
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
}
I'm not sure if free(array) would be the right method to use to delete the original array, and I'm having issues with returning the new copy.
When I try returning the new copy, I get:
warning: return from incompatible pointer type
which is because of:
return final;
Your adding_string makes no sense, you make a copy of str, free the memory
from array and return the new copy. The function should return a double pointer to char,
you are passing a single-pointer to char. All other values are lost, you are
leaking memory like crazy.
I'd rewrite your adding_string like this:
char **adding_string(char **array, const char *str)
{
char **tmp;
if(str == NULL)
return NULL;
// first make copy
size_t len = strlen(str);
char *strcopy = malloc(len+1);
if(strcopy == NULL)
return NULL;
// you've allocated enough memory for the copy
// no need of strncpy here
strcpy(strcopy, str);
// get the number of strings saved
size_t size = 0; // number of strings saved
if(array)
{
tmp = array;
while(*(tmp++))
size++;
}
// reallocate memory for array of strings
tmp = realloc(array, (size+2) * sizeof *tmp);
if(tmp == NULL)
{
// something went wrong, free the copy
free(strcopy);
return NULL;
}
tmp[size] = strcopy;
tmp[size+1] = NULL;
return tmp;
}
Note that in this version, if array is NULL, the function allocates the memory for the
array of strings. That's only a design choice, you could as well check that
array is not NULL and pass to adding_string a pre-allocated array of
strings. I think (and that's only my opinion) that is more elegant that
adding_string will create the first array. In this way, the code that
allocates memory is in one place only.
Now in your main
char **array = NULL;
char **lines;
// adding_string will allocate the memory for array when it's NULL
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
}
Note that I do
tmp = realloc(array, (size+2) * sizeof *tmp);
size has the number of strings saved, that means that array
holds size+1 spaces, because the last one points to NULL. You are appending
one more strings, so you have to reallocate size+1+1 spaces, which is
size+2.
Please don't forget to free the memory afterwards.
The program below strictly follows your needs and intentions.
The array array is resized every time a new string is added. At the end of the program the proper cleanup of all allocated memory is done.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char ** adding_string(char **array, const char *str)
{
size_t num = strlen(str) + 1;
char *final = (char *)malloc(num); // allocate memory for the string `str`
strncpy(final, str, num); // create the copy of the `str`
int i=0;
for(i=0; array[i] !=NULL; i++) {} // find how many elements do we have in the array
array[i] = final; // add final to the first empty spot in the `array`
i++;
char ** new_array = calloc(1+i, sizeof(char *)); // allocate a new array 1 size bigger
memcpy(new_array, array, sizeof(char*)*i); // copy all the pointers
free (array); // no need for the old array
return new_array; // return a pointer to the new bigger array
}
int main(void)
{
char **array = NULL;
char **lines;
array = (char **)calloc(1, sizeof(char *)); // allocate array for 4 poiters if type (char *)
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
free(*lines);
}
free (array);
return 0;
}
Output:
help
plz
thanks
This is different approach where
char *adding_string(const char *str)
returns a pointer (char *) to the copy of the string. The array has already preallocated memory to accommodate all string pointers.
A small program to demonstrate the concept:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *adding_string(const char *str)
{
size_t num = strlen(str) + 1;
char *final= (char *)malloc(num); // allocate memory for the string str
strncpy(final, str, num); // crreate the copy
return final; // return a pointer to created copy
}
int main(void)
{
char **array = NULL;
array = (char **)calloc(4, sizeof(char *)); // allocate array for 4 pointers if type (char *)
array[0] = adding_string("help");
array[1] = adding_string("plz");
array[2] = adding_string("thanks");
for (int i=0; i<3; i++ )
{
printf("%s\n", array[i]);
free(array[i]);
}
free (array);
return 0;
}
Output:
help
plz
thanks

Trying to use realloc(), getting core dumped

I'm trying to write a little program which uses realloc(), getchar() and some pointer arithmetic to store an array of characters in the memory.
I have a function called "inputArray" (in convert.c) which receives a pointer to a char (which is NULL to begin with, declared in main.c), then gets reallocated with one char until getchar() gets a '\n' char. the functions seems to work ok, but then when I try to print the string back in main.c, i get a "segmentation fault (core dumped)" error. I've been looking for hours, can't find where the problem is. Thanks!
main.c:
# include "convert.h"
int main()
{
char * string = NULL;
inputArray(string);
printf("%s", string);
free(string);
return 0;
}
convert.c:
#include "convert.h"
void inputArray(char * array)
{
/*pointer to the array*/
char * ptr = NULL;
/*stores the char*/
char c = 0;
/*counter used for pointer arithmetic*/
int count = 0;
/*loop for getting chars in array*/
while ((c = getchar()) != '\n')
{
array = realloc(array, sizeof(char));
ptr = array + count;
*ptr = c;
++count;
}
/*add the null char to the end of the string*/
array = realloc(array, sizeof(char));
ptr += count;
*ptr = '\0';
}
convert.h:
#include <stdio.h>
#include <stdlib.h>
void inputArray(char * array);
The size of the new allocated array is incorrect. You have to allocate count + 1 characters.
array = realloc(array, ( count + 1 ) * sizeof(char));
Take into account that it is more safe to use a temporary pointer to reallocate the memory. Otherwise the original address of the previously allocated memory will be lost.
Also these statements
array = realloc(array, sizeof(char));
ptr += count;
are wrong. You should at least write
array = realloc(array, count * sizeof(char));
ptr = array + count - 1;
Also the function should be declared like
char * inputArray(char * array);
and it must to return the new pointer to the caller.
And in main you have to write
string = inputArray(string);
Otherwise the function should accept the argument by reference that is the parameter should be declared like
void inputArray(char ** array);
and be processed correspondingly in the function.
You are missing one level of indirection in the inputArray function. It should be declared like
void inputArray(char **array)
and it should be realloc'd like this (you also need to increase the size of the array by multiplying with count + 1)
*array = realloc(*array, (count + 1) * sizeof(char));
Call it like this:
inputArray(&string);

pointer of a matrix in C

tPeca* criarPecas(FILE *pFile, int tam){
int i = 0,linhaV,colunaV,j = 0;
char ***elemento = (char***)malloc(tam*sizeof(char**));;
tPeca *pecaJogo = (tPeca*)malloc(tam*sizeof(tPeca));
if(pecaJogo==NULL)
return NULL;
for(i=0;i<tam;i++){
j=0;
fscanf (pFile, "%[^;]", pecaJogo[i].nome);
fscanf (pFile, ";%d", &pecaJogo[i].qtd);
fscanf (pFile, ";%d", &linhaV);
pecaJogo[i].linha = linhaV;
fscanf (pFile, ";%d", &colunaV);
pecaJogo[i].coluna = colunaV;
**elemento[i] = (char**)malloc(linhaV * sizeof(char*));
*elemento[i][j] = (char*)malloc(colunaV * sizeof(char));
j++;
}
return pecaJogo;
}
*** elemento is a pointer of a matriz, i think that i have problem with malloc... I received Segmentation Fault
These two statements are where I guess you ran into your problem:
**elemento[i] = (char**)malloc(linhaV * sizeof(char*));
*elemento[i][j] = (char*)malloc(colunaV * sizeof(char));
You created char *** above, and attempted to create an array of pointers:
char ***elemento = (char***)malloc(tam*sizeof(char**));;
Should be:
//this step creates an array of pointers
char ***elemento = malloc(tam*sizeof(char*));
//Note: there is no need to cast the return of [m][c][re]alloc in C
// The rules are different in C++ however.
Now you can put elemento in a loop to allocate pointer space for each of the pointers you created:
//this step creates an array pointers for each element of the array created above:
for(i=0;i<tam;i++) //assuming size is also tam (you specified nothing else)
{
elemento[i] = malloc(tam*sizeof(char *));//each ith element will
//now have tam elements of its own.
}
Next, you allocate memory at each location:
for(i=0;i<tam;i++)
{
for(j=0;j<tam;j++)
{
elemento[i][j] = malloc(someValue*sizeof(char));
//Note: sizeof(char) == 1, so could be:
//elemento[i][j] = malloc(someValue);
}
}
Now you have a fully allocated 3D array.
Putting it all together, (A simple 2D example)
When you create memory for a multi-dimensional array, you must create a combination of array of pointers, and memory for each. For 2D example, (used for an array of strings perhaps) you could do this:
char ** allocMemory(char ** a, int numStrings, int maxStrLen)
{
int i;
a = calloc(sizeof(char*)*(numStrings), sizeof(char*));//create array of pointers
for(i=0;i<numStrings; i++)
{
a[i] = calloc(sizeof(char)*maxStrLen + 1, sizeof(char));//create memory at each location
}
return a;
}
You must also create method to free memory:
void freeMemory(char ** a, int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(a[i]) free(a[i]);
free(a);
}
Usage:
char **array = {0};
...
array = allocMemory(array, 10, 80);
...
freeMemory(array, 10);
Will create memory, and addresses sufficient to contain 10 arrays of 80 character strings (arrays of char), then free it.
This could be expanded to 3D by adding another layer (for loop) of pointer creation, as shown at top of post). In this implementation, the inner most loop always creates the actual memory for each of the address locations you create.

Resources