Consider this program:
int main(void)
{
int* i = malloc(sizeof(int));
int* j = malloc(sizeof(int));
}
However this is a naive approach, because malloc may fail and the pointers are not free'd.
So you can do this:
int main(void)
{
int* i;
int* j;
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
if ((j = malloc(sizeof(int)) < 0)
{
free(i);
return -1;
}
free(i);
free(j);
}
However I consider this very error-prone. Consider having to assign 20 pointers, in the last malloc error case, you have to free 19 variables and then return -1.
I also know atexit, which can help me to write it like this:
int* i;
int* j;
void del_i(void)
{
free(i);
}
void del_j(void)
{
free(j);
}
int main(void)
{
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
else
{
atexit(del_i);
}
if ((j = malloc(sizeof(int)) < 0)
{
return -1;
}
else
{
atexit(del_j);
}
}
Which is better, but I dislike having to declare all pointers as global. Is there some way to combine these two approaches, basically:
Having destructors for pointers, which can be either executed directly or be used with atexit.
Having pointers local to functions.
free on NULL is defined to be a safe no-op. So a non-jumping variation could be:
int *i = malloc(sizeof(int));
int *j = malloc(sizeof(int));
if(i && j)
{
// do some work
}
free(i);
free(j);
First, this will not detect malloc failure:
if ((i = malloc(sizeof(int)) < 0)
{
return -1;
}
malloc returns NULL on failure, not a negative number.
Second, atexit is good for cleaning up static and global objects. It is not a good idea to make local objects global only to use them inside atexit.
A better approach is to make a struct for all related pointers that you need to allocate in an all-or-nothing unit, define a function for freeing them all at once, and write a function that allocates them one by one with memory checking on each allocation:
typedef struct AllOrNothing {
double *dPtr;
int *iPtr;
float *fPtr;
size_t n;
} AllOrNothing;
void freeAllOrNothing(AllOrNothing *ptr) {
free(ptr->dPtr);
free(ptr->iPtr);
free(ptr->fPtr);
free(ptr);
}
int allocateAllOrNothing(size_t n, AllOrNothing **res) {
*res = malloc(sizeof(AllOrNothing));
if (*res == NULL) {
return -1;
}
// Freeing NULL is allowed by the standard.
// Set all pointers to NULL upfront, so we can free them
// regardless of the stage at which the allocation fails
(*res)->dPtr = NULL;
(*res)->iPtr = NULL;
(*res)->fPtr = NULL;
(*res)->n = n;
(*res)->dPtr = malloc(n*sizeof(double));
if ((*res)->dPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
(*res)->fPtr = malloc(n*sizeof(float));
if ((*res)->fPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
(*res)->iPtr = malloc(n*sizeof(int));
if ((*res)->iPtr == NULL) {
free(*res);
*res = NULL;
return -1;
}
return 0;
}
int main(void)
{
int* i = NULL; // Init with NULL otherwise free on none NULL possible
int* j = NULLL;
if (!(i = malloc(sizeof(int)))
{
goto exit;
}
if (!(j = malloc(sizeof(int)))
{
goto exit;
}
...
exit:
free(i);
free(j);
...
return err;
}
This is something you can solve with goto statements.
int main(void)
{
int* i = NULL;
int* j = NULL;
bool success = false;
do {
i = malloc(sizeof(int));
if (NULL == i) break;
j = malloc(sizeof(int));
if (NULL == j) break;
success = true;
} while (0);
if (!success)
{
printf("Something failed!");
}
else
{
printf("All succeeded!");
// Do more work
}
free(i);
free(j);
return (success? 0 : 1);
}
Avoid multiple exit points. Avoid interlacing allocation and error handling. Follows a clean order of operation:
Declare, allocate and initialize resources..
If all successful, do the task.
Clean-up.
Return status.
// Do all allocations first, test their `NULL`-ness, then free them all.
int main(void) {
// Allocate resources
// declare and allocate in one step
int* i = malloc(sizeof *i);
double* j = malloc(sizeof *j);
// Test for acceptability
bool ok = i && j;
// Perform the main body of code
if (ok) {
; // do normal process in the code;
}
// free resources
free(i);
free(j);
// return status
return ok ? 0 : -1;
}
int *i=NULL,*j=NULL;
if(!(i=malloc(sizeof(int))))
goto EXIT;
if(!(j=malloc(sizeof(int))))
goto EXIT;
/* do some work */
return 0;
EXIT:
free(i);
free(j);
exit(EXIT_FAILURE);
Although goto is considered a bad programming practice
but here we can use it to get our task done with ease and simplicity
Related
My application has 3 layers -downlayer, middlelayer and upperlayer. downlayer and upperlayer are decoupled. Data passes either this way upperlayer -> middlelayer -> downlayer, or this way downlayer -> middlelayer->upperlayer.
My code look like this:
UpperLayer.c
#include <middlelayer.h>
int upperReaddownData()
{
DATA_ST* readFromdown_ptr = (DATA_ST*)malloc(sizeof(DATA_ST*));
if(readFromdown_ptr != NULL)
{
int retVal = GetFunction(readFromdown_ptr, UP);
if (retVal == 0) {
bool value;
memcpy(&value, readFromdown_ptr->continousData, sizeof(bool));
printf("Data read %s\t",
value ? "true" : "false");
printf("\n");
}
// I get error here
free(readFromdown_ptr);
return 0;
}
else {
return -1;
}
}
int UpperWritedownData()
{
bool random = rand() % 2;
DATA_ST* dumpTodown_ptr = (DATA_ST*)malloc(sizeof(DATA_ST*));
if (dumpTodown_ptr != NULL) {
size_t size_data = (size_t)sizeof(bool);
memcpy(dumpTodown_ptr->continousData, &random, size_data);
// size of data has to be mentioned while writing to memory
dumpTodown_ptr->packetSize = size_data;
int retVal = SetFunction(dumpTodown_ptr, DOWN);
// i get error here
free(dumpTodown_ptr);
return 0;
}
else {
return -1;
}
}
middlelayer.h
#define UP 0x00
#define DOWN 0x01
#define BUFFER_SIZE 2
typedef struct
{
size_t packetSize;
uint8_t continousData[80];
}DATA_ST;
typedef struct
{
bool isDataAlreadyRead;
DATA_ST Data;
}PACKET_HEADER_ST;
int Init();
int GetFunction(DATA_ST* packet, uint8_t target);
int SetFunction(DATA_ST* packet, uint8_t target);
int DeInit();
middlelayer.c
#include<middlelayer.h>
#define COPY_MEMORY(Dst,Src,Size) memcpy(Dst,Src,Size)
static PACKET_HEADER_ST* BUFFER[BUFFER_SIZE];
int Init()
{
uint8_t instance;
for (instance = 0; instance < BUFFER_SIZE; instance++) {
BUFFER[instance] = (PACKET_HEADER_ST*)calloc(1,sizeof(PACKET_HEADER_ST));
if (BUFFER[instance] != NULL)
{
BUFFER[instance]->isDataAlreadyRead = true;
}
else
{
return -1;
}
}
return 0;
}
int GetFunction(DATA_ST* packet,uint8_t target)
{
DATA_ST* currPacket = NULL;
if (BUFFER[target]->isDataAlreadyRead == false)
{
// not getting how to use memcpy properly
//currPacket = (DATA_ST*)&BUFFER[target]->Data;
//COPY_MEMORY(packet->continousData, currPacket->continousData, sizeof(BUFFER[target]->Data.packetSize));
//packet->packetSize = currPacket->packetSize;
//BUFFER[target]->isDataAlreadyRead = true;
*packet = BUFFER[target]->Data;
}
else
{
return -1;
}
return 0;
}
int SetFunction(DATA_ST* packet, uint8_t target)
{
//COPY_MEMORY(BUFFER[target]->Data.continousData, packet->continousData,sizeof(packet->packetSize));
//BUFFER[target]->Data.packetSize = packet->packetSize;
//BUFFER[target]->isDataAlreadyRead = false;
BUFFER[target]->Data = *packet;
BUFFER[target]->isDataAlreadyRead = false;
return 0;
}
int DeInit()
{
uint8_t instance;
for (instance = 0; instance < BUFFER_SIZE; instance++) {
free(BUFFER[instance]);
}
return 0;
}
lowerlayer.c
#include<middlelayer.h>
int downReadUpData(void)
{
DATA_ST* readFromDown_ptr = (DATA_ST*)malloc(sizeof(DATA_ST*));
if (readFromDown_ptr != NULL)
{
int retVal = GetFunction(readFromDown_ptr,DOWN);
if (retVal == 0) {
bool mybool;
memcpy(&mybool, readFromDown_ptr->continousData,readFromDown_ptr->packetSize);
// do something with data
}
// i get error here
free(readFromDown_ptr);
readFromDown_ptr = NULL;
return retVal;
}
else {
return -1;
}
}
//send dummy data to Up
int downWriteUpData(void)
{
static bool race_b = false;
DATA_ST* dumpToUp_ptr = (DATA_ST*)malloc(sizeof(DATA_ST*));
if (dumpToUp_ptr != NULL) {
size_t size_data = (size_t)sizeof(bool);
memcpy(dumpToUp_ptr->continousData, &race_b, size_data);
// size of data has to be mentioned while writing to memory
dumpToUp_ptr->packetSize = size_data;
int retVal = SetFunction(dumpToUp_ptr, UP);
// i get error here
free(dumpToUp_ptr);
dumpToUp_ptr = NULL;
race_b = !race_b;
return retVal;
}
else {
return -1;
}
}
I call UpperWritedownData function, it calls SetFunction, then i call downReadUpData Function and read the data passed by Upper layer.
I want to design the middlelayer like this : you pass the data of any datatype to the middlelayer(uint8_t array can hold any datatype), during Init ,the middlelayer is allocated some memory and i have the pointer of BUFFER[Up]->Data or BUFFER[DOWN]->Data and these pointers are constant and should not change throughout the program. Now upper/down layer gives data (SetFunction) , i dump it in my preallocated memory, then after some time, down/upper asks the data i copy the data from my preallocated memory and give it.Now I just need to store the recent data hence i have not implemented queues also i am overwriting and once the data is read , i dont care about it and I have to discard it.
What I tried:
I tried using memcpy and copy the pointer to BUFFER[x]->Data and I have commented that part of code as you can see.It did not work because memcpy copies the pointer to BUFFER[target]->Databut i wanted to copy just the contents.When I tried to use free(pointer), i get heap corruption error message.I am having trouble in using memory allocation. Kindly provide inputs.
There is a bug in this malloc:
DATA_ST* readFromdown_ptr = (DATA_ST*)malloc(sizeof(DATA_ST*));
You define readFromdown_ptr as a pointer to DATA_ST. So you want to make it point to a memory block that can hold a DATA_ST but you only allocate memory for a pointer to DATA_ST because you do sizeof(DATA_ST*).
Change it to:
DATA_ST* readFromdown_ptr = malloc(sizeof(DATA_ST));
or better.. change it to:
DATA_ST* readFromdown_ptr = malloc(sizeof *readFromdown_ptr);
The same applies to the line:
DATA_ST* dumpToUp_ptr = (DATA_ST*)malloc(sizeof(DATA_ST*));
I am creating a deque to store stings in C, and when I call the free() function, the program crashes. I have implemented a similar structure but only storing integers, and encountered no problems, but this seems to be causing me a few. I created a struct containing a multidimensional array or characters, and i think maybe I am not using the pointers correctly? I have searched far and wide and cannot solve it The main area of concern is when i call clear() from the ain body. That in turn calls free(), and the program just stalls. :-( Any help would be extremely useful.
#include <stdio.h>
#define MAX 20 // number of characters for word
typedef struct {
char **deque;
int size;
int pFront;
int pRear;
} deque;
typedef int bool;
enum { false, true };
void initDeque(deque *d, int initialSize)
{
d->size = initialSize;
d->pFront = -1;
d->pRear = -1;
d->deque = (char **)malloc(sizeof(char*)*initialSize);
int idx;
for(int idx = 0; idx < d->size; idx++)
{
d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));
d->deque[idx] = "";
}
printf("d->size: %zu\n", d->size);
}
void clear(deque *d) {
if(d->pFront == -1)
{
printf("Queue is empty\n");
}
else
{
printf("Attempting to clear...\n");
for(int idx = 0; idx < d->size; idx++)
{
printf("Attempting to clear columns...");
free(d->deque[idx]);
}
printf("Attempting to clear rows...");
free(d->deque);
printf("Freed!!!!\n");
d->deque = NULL;
d->size = 0;
d->pFront = -1;
d->pRear = -1;
}
}
bool isEmpty(deque *d)
{
if(d->pFront == -1){
return true;
}
else
{
return false;
}
}
bool isFull(deque *d)
{
if(d->size == d->pRear+1)
{
return true;
}
else
{
return false;
}
}
void display(deque *d)
{
if(isEmpty(d)){
printf("empty\n");
}
else{
printf("Deque Values:\n");
int idx;
for(int idx = 0; idx <= d->pRear; idx++)
{
printf("Index: %zu\tValue: %s\n", idx, d->deque[idx]);
}
printf("Size: %zu\n", d->size);
}
}
void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{
if(isFull(d))
{
printf("Is Full\n");
int idx;
deque dTemp;
initDeque(&dTemp, d->size);
printf("dTemp Initialised\n");
for(idx = 0; idx < d->size; idx++)
{
dTemp.deque[idx] = d->deque[idx];
}
printf("deque copied to dTemp:\n");
for(idx = 0; idx < d->size; idx++)
{
printf("dTemp[%zu]: %s\n", idx, dTemp.deque[idx]);
}
clear(&d);
printf("d cleared\n");
initDeque(&d, dTemp.size*2);
printf("New deque of double length initialised\n");
for(idx = 0; idx < dTemp.size; idx++)
{
d->deque[idx] = d->deque[idx];
}
printf("dTemp Copied to new deque\n");
clear(&dTemp);
printf("dTemp Cleared\n");
char **tmp = realloc( d->deque, sizeof (d->deque) * (d->size*2) );
if (tmp)
{
d->deque = tmp;
for (int i = 0; i < d->size; i++)
{
d->deque[d->size + i] = malloc( sizeof(char) * MAX );
}
}
}
printf("Appending to rear.. %s\n", item);
d->pRear++;
d->deque[d->pRear] = item;
if(d->pFront == -1)
d->pFront = 0;
}
int main(void)
{
deque d;
initDeque(&d, 5);
rAppend(&d, "when");
rAppend(&d, "will");
rAppend(&d, "wendy");
rAppend(&d, "walk");
rAppend(&d, "with");
display(&d);
clear(&d);
return 0;
}
The problem is your are calling free() on static chain "when", "will",...
You can replace insertion in the function void rAppend(deque *d, char item[]) :
d->deque[d->pRear] = item;
with:
d->deque[d->pRear] = strdup(item);
Doing like this chains are allocated in the heap and free from the heap.
After there is others problems in the code, but it run without crash.
The main problem seems to be that you don't appreciate the difference between copying / assigning pointers and copying / assigning the data to which they point. Secondarily, it seems you may not appreciate the utility of pointers that don't point to anything, especially null pointers. Some details follow.
You are dynamically allocating space for a bunch of strings ...
for(int idx = 0; idx < d->size; idx++)
{
d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));
... and then leaking all of that space by replacing the pointer to each with a pointer to an empty string literal:
d->deque[idx] = "";
}
As if the leak were not bad enough, you are not permitted to free a string literal or modify its content, which you nevertheless try to do to any of those pointers that remain in the dequeue whenever you clear() it. This is likely the cause of some of your errors.
If you want to set each allocated string to an empty one then modify its content instead of replacing the pointer to it. For example:
d->deque[idx][0] = '\0';
In fact, however, you probably don't need to do even that. You are already performing bookkeeping to know which arrays contain valid (string) data and which don't, and that should be sufficient to do the right thing. Supposing you maintain copies of the strings in the first place.
But that's not all. When you rAppend() elements to your deque you have a similar problem. You create a temporary deque, and then copy the string pointers from your original deque into the temporary:
dTemp.deque[idx] = d->deque[idx];
Not only does this leak the original (empty) data in the temporary deque, it aliases that deque's contents with the main deque's. When you later clear the temporary deque, therefore, you free all the string pointers in the original. Subsequently using or freeing them produces undefined behavior.
Perhaps you instead want to strcpy() all the elements of the main deque into the temp and back, but I suggest instead skipping the temp deque altogether with something along these lines:
void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{
if(isFull(d))
{
printf("Is Full\n");
char **tmp = realloc(d.deque, d->size * 2);
if (tmp)
{
d->deque = tmp;
for (int i = 0; i < d->size; i++)
{
// Copied from the original, but see below
d->deque[d->size + i] = malloc( sizeof(char) * MAX );
}
d->size * 2;
} // else?
}
printf("Appending to rear.. %s\n", item);
d->pRear++;
// Oops, this is another leak / aliasing issue:
d->deque[d->pRear] = item;
if(d->pFront == -1)
d->pFront = 0;
}
The whole point of the temporary deque is lost on me, since the realloc() you need to do preserves the original data anyway (as long as it succeeds, anyway).
Note too, however, that this still has an aliasing issue: you have aliased a deque element with the appended string, and leaked the memory allocated for that element. Furthermore, when you clear the deque, you free that string for everyone holding a pointer to it. Or at least you attempt to do so. You're not permitted to do that to string literals.
I suggest not allocating space in your deque for the individual strings at all, and not freeing it. Continue to use assignment to store elements in your deque, understanding and embracing that these are aliases. This will be more analogous to your implementation for ints.
#include<memory>
#include<iostream>
using namespace std;
struct S {
S() { cout << "make an S\n"; }
~S() { cout << "destroy an S\n"; }
S(const S&) { cout << "copy initialize an S\n"; }
S& operator=(const S&) { cout << "copy assign an S\n"; }
};
S* f()
{
return new S; // who is responsible for deleting this S?
};
unique_ptr<S> g()
{
return make_unique<S>(); // explicitly transfer responsibility for deleting this S
}
int main()
{
cout << "start main\n";
S* p = f();
cout << "after f() before g()\n";
// S* q = g(); // this error would be caught by the compiler
unique_ptr<S> q = g();
cout << "exit main\n";
// leaks *p
// implicitly deletes *q
}
I have an error using realloc to replace malloc.
This code below runs OK on my computer.
int vector_grow(Vector* vec) {
unsigned long newcap;
int * newarr;
if (0 == vec->cap) {
vec->arr = (int*)malloc(START_CAPACITY * sizeof(*vec->arr));
if (NULL == vec->arr)
return -1;
vec->cap = START_CAPACITY;
return 0;
}
newarr = malloc (newcap * sizeof(*vec->arr));
if (NULL == newarr)
return -1;
memcpy (newarr, vec->arr, vec->len * sizeof(*vec->arr));
free (vec->arr);
vec->arr = newarr;
vec->cap = newcap;
return 0;
}
I want to change the malloc to realloc, but the error occurs.
int vector_grow(Vector* vec) {
unsigned long newcap;
if (0 == vec->cap) {
vec->arr = (int*)malloc(START_CAPACITY * sizeof(*vec->arr));
if (NULL == vec->arr)
return -1;
vec->cap = START_CAPACITY;
return 0;
}
newcap = 2 * vec->cap;
if ((vec->arr = (int*)realloc(vec->arr, newcap * sizeof(int))) == NULL)
return -1;
return 0;
}
It says
malloc: *** error for object 0x7fca64c02598: incorrect checksum for freed object - object was probably modified after being freed.
I don't know any difference between those two snippets of code, if you know what causes the error, please tell me! Thank you very much!
Bug in missing vec->cap = in updated code certainly contribute to various calls to malloc() and calling code's misuse of data.
int vector_grow(Vector* vec) {
unsigned long newcap;
if (0 == vec->cap) {
... // not important to show the bug
}
newcap = 2 * vec->cap;
if ((vec->arr = (int*)realloc(vec->arr, newcap * sizeof(int))) == NULL)
return -1;
// Add missing update
vec->cap = newcap;
return 0;
}
Also better to test for allocation success
void *p = realloc(vec->arr, sizeof *(vec->arr) * newcap);
if (p == NULL) {
return -1;
}
vec->arr = p;
vec->cap = newcap;
The only scenario where I can imagine such error message is when you actually modify the pointer, for example
int *x = malloc(2 * sizeof *x);
if (x != NULL) {
x = x + 1;
free(x);
}
The pointer that MUST be passed to free() MUST had been returned by malloc()/calloc()/realloc(), passing any other pointer including a pointer to the same data but at a different position like x in the example above is undefined behavior.
I have problems with memory deallocation in C. Without the division of functions everything is OK, but unfortunately it does not work on the same functions. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
char *name;
enum {
summer,
winter
} index;
} student;
bool init(student *s) {
printf("The next stage in the allocation of memory\n");
s = (student*)malloc(sizeof(*s));
if (&s == NULL) {
printf("Allocation Failed\n");
return 0;
} else {
printf("Allocation completed successfully\n");
}
}
void delete(student *s) {
if (&s != NULL) {
printf("begin removal\n");
free(s);
printf("Released memory");
}
}
int main() {
student *s;
init(s);
delete(s);
return 0;
}
I do not know what I'm doing wrong. Please help.
First of all the function init has undefined bbehaviour because it returns nothing in the case of successful memory allocation.
You can check whether the memory was allocated or not by returning pointer to the allocated memory or NULL.
Also this statement
if(&s==NULL){
is wrong. The condition will always yield false because the address of the local variable s is not equal to NULL.
So the function can be rewritten either the following way
student * init()
{
printf("The next stage in the allocation of memory\n");
student *s = ( student* )malloc( sizeof( *s ) );
if ( s == NULL )
{
printf("Allocation Failed\n");
}
else
{
printf("Allocation completed successfully\n");
}
return s;
}
And called like
int main( void )
^^^^^
{
student *s = init();
//...
Or it can be defined the following way
int init( student **s )
{
printf("The next stage in the allocation of memory\n");
*s = ( student* )malloc( sizeof( **s ) );
int success = *s != NULL;
if ( !success )
{
printf("Allocation Failed\n");
}
else
{
printf("Allocation completed successfully\n");
}
return success;
}
and called like
int main( void )
^^^^^
{
student *s;
init( &s );
//...
The function delete should be defined at least like
void delete(student *s) {
if (s != NULL) {
^^^
printf("begin removal\n");
free(s);
printf("Released memory");
}
}
Firstly, free is NULL safe. If variable is NULL already, basically nothing happens. You do not have to check if it is NULL. (you can check page 313 ISO-IEC 9899 )
Also, when you initialize student->name and allocate, there will be memory leak. You have to free that too.
So, your delete function could be like this ;
void delete(student *s) {
printf("begin removal\n");
free(s->name);
free(s);
printf("Released memory");
}
And if (&s == NULL) is wrong. They must be changed with if (s == NULL).
Your allocation may cause really big troubles in the big codes. If you allocate s = (student*)malloc(sizeof(*s)); it means that "allocate s with size of *s". But pointer size is fixed memory block (mostly 8 bytes). It means that you blocks certain size of memory. If you have bigger struct than that, this kind of allocation will corrupt the memory and your executable will be killed by the OS(you can try add some more variables to your struct and initialize them). In small structs and very short runtimes, mostly this allocation works too. But i guarantee that this is not safe for run-time. And it will not give any warnings or errors in compile-time. True way is s = malloc(sizeof(student)). With this way, you exactly allocates all the memory blocks. And your memory stay safe in run-time.
Lastly, your init function should return the initialized variable. And your init function could be like this ;
#define NAME_LENGHT 128
...
student * init(student *s) {
printf("The next stage in the allocation of memory\n");
s = malloc(sizeof(student));
if (s == NULL) {
printf("Allocation Failed\n");
return NULL;
}
s->name = malloc(NAME_LENGHT);
if (s->name == NULL) {
printf("Allocation Failed\n");
return NULL;
} else {
printf("Allocation completed successfully\n");
}
//alternatively you can strdup directly without any allocation
// s->name = strdup("some name");
return s;
}
You never change the s in main.
Solution 1: Pass a pointer to the variable in main for init to populate.
void init_student(student** s_ptr) {
*s_ptr = (student*)malloc(sizeof(student));
if (*s_ptr == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
(*s_ptr)->name = malloc(MAX_NAME_SIZE + 1);
if ((*s_ptr)->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
(*s_ptr)->name[0] = 0;
(*s_ptr)->gpa = 0;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s;
init_student(&s);
delete_student(s);
return 0;
}
Solution 1b: Same, but a cleaner implementation.
void init_student(student** s_ptr) {
student* s = (student*)malloc(sizeof(student));
if (*s_ptr == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name = malloc(MAX_NAME_SIZE + 1);
if (s->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name[0] = 0;
s->gpa = 0;
*s_ptr = s;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s;
init_student(&s);
delete_student(s);
return 0;
}
Solution 2: Return the allocated point to main.
student* init_student() {
student* s = (student*)malloc(sizeof(student));
if (s == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name = malloc(MAX_NAME_SIZE + 1);
if (s->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name[0] = 0;
s->gpa = 0;
return s;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s = init_student();
delete_student(s);
return 0;
}
Note that &s == NULL will never be true because &s is the address of the variable itself. You want just s == NULL to check the value of the pointer in s.
I'm looking for most convenient/best/smartest way for multiple memory deallocation. Not quite sure if that is best explanation out there, but here is an example that will show what i want to do:
void func()
{
int* ptr1 = malloc(1);
if(ptr1 == NULL)
{
//...
}
int* ptr2 = malloc(1);
if(ptr2 == NULL)
{
//...
free(ptr1);
}
int* ptr3 = malloc(1);
if(ptr3 == NULL)
{
//...
free(ptr2);
free(ptr1);
}
//code...
}
Only thing that comes up to my mind is array filled with flags, if the flag is raised particular memory has to be freed. Is there any other more convenient way to do this? You can imagine how many times i need to repeat free() if there is need for more malloc()-ing.
What you posted is a common practice for error handling and resource release in a function, you acquire several resources and in case any error occurred, you need to release those that have been acquired prior, there is nothing wrong, just do it one by one.
void func(void) {
void *ptr1 = NULL;
void *ptr2 = NULL;
void *ptr3 = NULL;
ptr1 = malloc(SIZE);
if (!ptr1) goto end;
ptr2 = malloc(SIZE);
if (!ptr2) goto end;
ptr3 = malloc(SIZE);
if (!ptr3) goto end;
// do your work with the buffers
end:
free(ptr1);
free(ptr2);
free(ptr3);
}
You can use an array of pointers and keep count of the number of mallocs done. And use a common free function to free them all. Like,
void func()
{
char* ptr[10];
int n = 0, i;
for(i = 0; i < 10; i++)
ptr[i] = NULL;
ptr[n] = malloc(1);
if(ptr[n] == NULL)
{
//...
}
n++;
ptr[n] = malloc(1);
if(ptr[n] == NULL)
{
//...
custom_free(ptr1, n);
}
n++;
ptr[n] = malloc(1);
if(ptr[n] == NULL)
{
//...
custom_free(ptr, n);
}
n++;
//code...
}
And the custom_free() can be something like,
void custom_free(char* ptr[], int n)
{
int i;
for(i = 0; i <= n; i++)
free(ptr[i]);
}
An alternative way is to allocate all needed memory in one big chunk,
and treat parts of that as p0,p1,p2:
void worker(void)
{
#define N_ELEM 123
int *work_mem;
int *p0,*p1,*p2;
work_mem = malloc ( 3* N_ELEM * sizeof *work_mem);
if (!work_mem) { OMG(); return; }
p0 = work_mem, p1= work_mem + N_ELEM, p2 = work_mem + 2 * N_ELEM;
/* do useful stuff here with work_mem
** , possibly via p0,p1,p2
*/
free(work_mem);
}