Should I use free() when realloc fails? - c

I wondered if I have a memory leak, I don't know whether I should use free() when realloc fails or it does that automatically.
for example, I have a dynamic array of chars, each time I'm reaching the end of it from input, I'm adding more memory using realloc.
I'm saving the last address just in case the realloc fails.
int cntan = 0;
char *p = (char *)malloc(MEM_SIZE * sizeof(char));
char *q = p; /*backup pointer*/
int c;
long current = 0, last = MEM_SIZE - 1;
while ((c = getchar()) != EOF)
{
if (current >= last) /* if reached last bit */
{
q = p;
p = (char *)realloc(q, last + (MEM_SIZE * sizeof(char))); /* attempting to add more bits */
if (p == NULL)
{
printf("Memory allocation failed, printing only stored values \n");
return q;
}
last += MEM_SIZE;
}
p[current] = c;
++current;
if (isalnum(c)) /* checking if current char is alpha-numeric */
cntan++;
}
in this example, I wonder I should free(p) if p==NULL
whole code if someone interested: Pastebin

You should free() the original pointer, not the one returned by realloc(). Passing NULL to free() won’t do anything.
To clarify: the pointer returned by realloc() on success may not be the same as the original pointer.

Other problems with code:
Length unknown
return q; returns the pointer, yet the caller lacks information about how big an allocation (current).
Conceptually wrong size calculation
Scaling by sizeof(char)applies to the sum. Makes no difference in this case as sizeof char is always 1.
// last + (MEM_SIZE * sizeof(char))
(last + MEM_SIZE) * sizeof(char)
Missing malloc() check
Code checks the return value of realloc(), but misses checking malloc().
Note: cast not needed. Consider:
// p = (char *)realloc(q, last + (MEM_SIZE * sizeof(char)));
p = realloc(q, sizeof q[0] * (last + MEM_SIZE));

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'

Dynamic array of structs in C using realloc

I'm trying to create a simple dynamic array of structures, but I'm unsure about how to go about doing it.
I have
n = 1;
typedef struct {
char wordName [50];
int counter;
}Words;
Words * array;
And now I want to reallocate it in this loop.
if(strlen(token) > 6){
array = (Words*)realloc(array, sizeof(Words)*n++);
strcpy(array->wordName, token);
//printf("%s ", array->wordName);
array++;
}
I can add data to the structure, it prints it all out in a loop just fine. But when I add array++, I get:
realloc(): invalid pointer
Am I accessing the data incorrectly? Or do I not even have any more pointers beyond the head?
array++; causes array to advance by sizeof (a_pointer) so it now no longer points to address first returned by realloc(). That means any future attempt to realloc() with the new address of array will result in the error "Invalid pointer, pointer was not initially allocated with call to malloc, calloc or realloc".
Further, if array is not initialized NULL, then you cannot use realloc() for the initial allocation. Initializing n = 1; mixes a count with and index. Always keep the count so it is the next index to be filled. For 0 you can use an if / else or ternary, e.g.
n = 0;
...
if (n) { /* if n > 0 (not 1st allocation, use realloc) */
/* always realloc using a temporary pointer */
void *tmp = realloc (array, (n + 1) * sizeof *array);
if (!tmp) { /* validate EVERY allocation */
/* handle error, return */
}
array = tmp; /* assign reallocated block to array */
n += 1; /* increment count */
}
else { /* initial allocation */
array = malloc (sizeof *array);
if (!array) { /* validate EVERY allocation */
/* handle error, return */
}
n += 1; /* increment count */
}
/* rest of your strcpy here
* VALIDATE strlen(token) > 0 AND < 50
*/
/* note: you can consolidate both n += 1; here. duplicated for clarity */
You always call realloc() with a temporary pointer because when (not if) realloc() fails it returns NULL which will overwrite the address pointing to the allocated block of memory creating a memory leak. e.g. never do:
pointer = realloc (pointer, size);
Instead, by using a temporary pointer, if realloc() fails, pointer (your array) will still point to the last allocated and still valid block of memory allowing you to use what is stored there AND allowing you to free() that block of memory when you are done with it.
Note, you can use a ternary, but it is much less readable:
Words *array = NULL;
size_t n = 0;
...
void *tmp = realloc (array, sizeof *array * n ? n + 1 : 1);
if (!tmp) { /* validate EVERY allocation */
/* handle error, return */
}
array = tmp;
n += 1;
/* rest of your strcpy here
* VALIDATE strlen(token) > 0 AND < 50
*/
In C, there is no need to cast the return of malloc (or calloc or realloc), it is unnecessary. See: Do I cast the result of malloc?

Adding space before the memory block rather than after it using realloc in C

I have allocated an array of chars and I want to add another char at the beginning of the array while maintaining the order.
Ex. If pointer points to the beginning of 4 char blocks: A,B,C,D -> pointer[0]==A . If I add E the block of memory should look: E,A,B,C,D -> pointer[0]==E.
Additionally I want to do it in one line, without manually copying elements to another block and erasing the first. All functions have to be from C standard library.
I have though of something like pointer = realloc(pointer-1, (n-1)*size), but I'm not guaranteed that pointer-1 is free.
Thankful for your answers in advance
Adding space before the memory block rather than after it using realloc
Re-allocate with realloc() and then shift the data with memove().
I want to do it in one line,
Either use a helper function like below or employ a long hard to read un-maintainable line.
char *realloc_one_more_in_front(char *ptr, size_t current_size) {
void *new_ptr = realloc(ptr, sizeof *ptr * (current_size + 1));
if (new_ptr == NULL) {
return NULL; // Failure to re-allocate.
}
ptr = new_ptr;
memmove(ptr + 1, ptr, sizeof *ptr * current_size);
return ptr;
}
Sample usage. For simplicity of example, error handling omitted.
size_t current_size = 4;
char *ptr = malloc(current_size);
for (size_t i = 0 ; i<current_size; i++) {
ptr[i] = 'A' + i;
}
ptr = realloc_one_more_in_front(ptr, current_size++);
ptr[0] = 'E';
printf("%.*s\n", (int) current_size, ptr);

How to improve the code for dynamically allocated strings in C?

So I'm practicing and learning C right now and came across a rather easy challenge from CodeWars that asked to print out a string of "Aa~", "Pa!", and "Aa!" depending on whether n was <= 6 or not. I know how we can do this with arrays but wanted to try out using dynamically allocated char arrays like with malloc for efficiency sake.
I want to make sure I get the basics down with these questions
So I know in other malloc examples with int we set up a pointer (of type int) to point to an allocated memory block. Is it still a pointer when I declare "char *ptr" to point to an allocated memory block because pardon me I thought "char *anything" meant it was a convention to represent a string. So not sure why the below is working somewhat if I'm not setting up a pointer like "char **ptr" as I thought.
why do I get a "malloc: *** error for object 0x100000fab: pointer being freed was not allocated" type of error when I try to return the answer especially when the "val" is 1 or 0? I read somewhere that changing the pointer(string?), answer, to NULL will solve the issue but not entirely sure why this works.
3 To continue in general with the above question, for freeing up space, what is the best method to do so if we dynamically allocate a memory block in a function but need to return a value from that function? As in, do we free up the space after or before?
Thank you all for your input.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define val 1
char *sc(int); // function declaration/prototype
int main(int argc, const char * argv[]) {
char *answer = sc(val);
printf("The answer is %s\n", answer);
answer = NULL; // why does this work
free(answer);
return 0;
}
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
char *ptr = (char*) malloc(n*4);
if (ptr == NULL){
printf("malloc failed");
}
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
ptr = "";
}
else {
printf("Error!");
exit(0);
}
return ptr;
}
The main issue you have with your code is that you're not zeroing out the buffer you get from malloc(), so the first strcat() is not necessarily going to write at the start of the string but at the end.
You can fix that with a strcpy() right after the malloc() call and check:
strcpy(ptr, "");
Or, equivalently, you can just set the first byte of the buffer to zero. Since C strings are zero-terminated strings, setting a character to zero will indicate it's at the end:
ptr[0] = 0;
You also seem to be allocating your buffer too short. If you write n-1 copies of Aa~ (4 bytes) plus one copy of Pa! Aa! (8 bytes when you include the terminating zero!) you'll actually need 4 * (n+1) as space. So either always allocate that or do so in case n < 6 which is where you need the additional bytes.
This is also a problem:
ptr = "";
Because now your ptr is no longer pointing to a buffer returned by malloc(), but to a static (empty) string in your binary. It's quite probable that this is the place where you're getting the trouble from free(), since calling it on a static string in your binary is definitely wrong.
Furthermore, after you set ptr = "" you no longer have any reference to the buffer you allocated, which means you most likely just created a memory leak!
In that case, you should simply use strcpy() or set the first byte to zero. But if you do that at the start of the program, you don't need to do it here.
Finally, free(NULL); works (as in, doesn't throw an error) because that's part of its specification, you can pass it a NULL pointer and it will do nothing. But note that it's not freeing the buffer you allocated, so you have a memory leak here too.
I'd further refactor the second part of your code so you don't have too much repetition appending the strings:
char *sc(int n) {
/* if n <= 6 then will have an extra "Aa!"
* after "Pa!" at the nth position.
*/
char *ptr;
if (n < 0) {
printf("Error!");
return NULL;
}
ptr = (char*) malloc(4 * (n+1));
if (ptr == NULL){
printf("malloc failed");
return NULL;
}
strcpy(ptr, "");
if (n > 1) {
for (int i = 1; i < n; i++){
strcat(ptr, "Aa~ ");
}
strcat(ptr, "Pa!");
if (n <= 6) {
strcat(ptr, " Ah!");
}
}
return ptr;
}
Also note you don't need to assign the result of strcat() back to ptr every time, since it always returns its first argument anyways, so assigning it there isn't changing anything really.
I thought "char *anything" meant it was a convention to represent a string. So not sure why the below is working somewhat if I'm not setting up a pointer like "char **ptr" as I thought.
You're allocating memory for a string, so it just needs to be char *. char ** would be used for an array of multiple strings, or for a pointer to a variable that contains a pointer to a string.
why do I get a "malloc: *** error for object 0x100000fab: pointer being freed was not allocated"
You get that when you do ptr = "";. After you do this, ptr no longer points to the memory that was allocated with malloc, it points to that string literal. If you want to set the allocated memory to an empty string, you can do
ptr[0] = '\0';
This puts a null terminator in the first element of the string.
You also need to do that before the code that uses strcat() to append to the string. Otherwise you're appending to uninitialized data. Simplest would be to do it immediately after allocating the memory (then you don't need it in the n <= 1 block.
The else block is not needed. There are no other possibilities than the 3 you test for unless the CPU is malfunctioning (in which case all bets are off). However, you should check for n < 1 before calling malloc(), as you can't allocate negative memory, and malloc(0) may return NULL.
When you allocate space for ptr, you need to add 1 byte for the terminating null of the string.
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
if (n >= 1) {
char *ptr = malloc(n*4 + 1);
} else {
char *ptr = malloc(1);
}
if (ptr == NULL){
printf("malloc failed");
exit(1);
}
ptr[0] = '\0'; // initialize empty string
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
// nothing to do
}
return ptr;
}

Reading file to char** using fgets and memcpy

I'm trying to read a set of lines from a file to an array. I'm doing this to learn malloc and realloc.
#define MAX_LINE 301
char** read_file_lines(char* filename) {
char** ptr = NULL;
int max = 5;
int i = 0;
FILE *fp = fopen(filename, "r");
if(fp != NULL) {
char line[MAX_LINE];
while(fgets(line, MAX_LINE, fp) != NULL) {
/* allocate some extra memory for some more lines */
if(i == max) {
int new_max = max * 2;
int nr_bytes = new_max * sizeof(char) * MAX_LINE;
char **ptr2 = realloc(ptr, nr_bytes);
if(ptr2 != NULL) {
ptr = ptr2;
ptr2 = NULL;
max = new_max;
}
}
// ptr[i] = line;
// strcpy(ptr[i], line);
memcpy(ptr[i], line, strlen(line));
i++;
}
fclose(fp);
}
else {
printf("Error opening file %s\n", filename);
}
return ptr;
}
The code compiles. However, when it is executed, an error occurs (the program crashes).
I did some debugging and determined that the problem is in up in the memcpy () instruction. I had previously tried using strcpy, which also gives a similar problem.
I went to check memcpy ()'s protocolo and it is as followS:
void * memcpy ( void * destination, const void * source, size_t num );
Now, if ptr is char**, isn't ptr[i] equivalent to a char* ?
Thanks for your comments.
It looks like ptr isn't initialized to point to any memory at all. Also, you're not allocating any memory for the individual lines.
To initialize ptr, change the declaration to:
int max = 5;
char** ptr = malloc(max * sizeof(char*));
Try adding this before the call to memcpy:
ptr[i] = malloc(strlen(line) + 1);
and change the calculation for the realloc call:
int nr_bytes = new_max * sizeof(char*);
EDIT: To explain in more detail: ptr is a pointer to an array of pointers. You have to allocate memory for ptr (that is, enough memory just to store individual pointers). In addition to this, you also have to allocate each individual array of characters that the individual elements of ptr will point to.
The first change I suggested ensures that ptr always points to enough memory to hold 5 pointers (or more, once it's been realloc'd.)
The second change ensures that each member of ptr always points to valid memory before you try to access it as a pointer.
And the third change is required because ptr points to elements that are pointers to char, not char.
Nah. Arrays are not pointers. Pointers to pointers are not arrays of arrays. If you want a two-dimensional dynamic array, then you have to allocate memory for 1. the array of pointers that point to the individual lines, and 2. for the lines themselves too.
Problem is that at first execution memory is not allocated: i is 0, max is 5, the if condition is false and the realloc is never executed.

Resources