I'm just learning to use valgrind and c, and I have an invalid free() output when trying to free data from a struct. I believe it's because data is not being freed from the struct correctly.
This is my struct:
typedef struct song_
{
char *artist;
char *title;
mtime *lastPlayed;
} song;
And this is the function which tries to free it:
void songDelete(song *s)
{
//artist
free(s->artist) ;
//title
free(s->title) ;
//time
if(NULL != s->lastPlayed)
mtimeDelete(s->lastPlayed) ;
//song
free(s);
}
mtime and mtimeDelete are some user-defined variables and methods, but I feel like they're not relevant to my question. I know it's wrong to ask someone to do my homework for me, I'd just like a push in the right direction if possible.
No, that's definitely the right way to do it.
So, if valgrind is complaining, it's probably because the values in artist, title or lastPlayed are not actually valid pointers.
That's the first thing I'd be checking.
In other words, make sure what you put in there are valid pointers. Simply creating a song with:
song *AchyBreakyHeart = malloc (sizeof (song));
won't populate the fields (they'll be set to arbitrary values). Similarly,
AchyBreakyHeart->artist = "Bill Ray Cyrus";
will populate it with a string constant rather than a valid pointer in the heap.
The ideal thing would be to have a "constructor", similar to the destructor you've provided, something like:
song *songCreate (char *artist, char *title, mtime *lastPlayed) {
song *s = malloc (sizeof (song));
if (s == NULL) return NULL;
s->artist = strdup (artist);
if (s->artist == NULL) {
free (s);
return NULL;
}
s->title = strdup (title);
if (s->title == NULL) {
free (s->artist);
free (s);
return NULL;
}
s->lastPlayed = mtimeDup (lastPlayed);
if (s->lastPlayed == NULL) {
free (s->title);
free (s->artist);
free (s);
return NULL;
}
return s;
}
This guarantees that the object is either fully constructed or not constructed at all (ie, no half-states).
Better yet would be to adapt the constructor/destructor pair to handle NULLs in conjuction with each other, to simplify the pair. First, a slightly modified destructor, the only change being that it can accept NULL and ignore it:
void songDelete (song *s) {
// Allow for 'songDelete (NULL)'.
if (s != NULL) {
free (s->artist); // 'free (NULL)' is valid, does nothing.
free (s->title);
if (s->lastPlayed != NULL) {
mtimeDelete (s->lastPlayed) ;
}
free (s);
}
}
Next, the constructor which, rather than trying to remember what has been allocated, instead sets them all to NULL initially and just calls the destructor if something goes wrong:
song *songCreate (char *artist, char *title, mtime *lastPlayed) {
// Create song, null all fields to ease destruction,
// then only return it if ALL allocations work.
song *s = malloc (sizeof (song));
if (s != NULL) {
s->artist = s->title = s->lastPlayed = NULL;
s->artist = strdup (artist);
if (s->artist != NULL) {
s->title = strdup (title);
if (s->title != NULL) {
s->lastPlayed = mtimeDup (lastPlayed);
if (s->lastPlayed != NULL) {
return s;
}
}
}
}
// If ANY allocation failed, destruct the song and return NULL.
songDelete (s);
return NULL;
}
Your code appears correct.
Make sure that your struct is initialized correctly (with pointers set to NULL or a valid value).
int main() {
song_ *s = calloc(sizeof(song_));
free(s);
return 0;
}
Did you create an object/pointer to your Struct using malloc (heap allocation)? or just song s; (stack allocation) ??
You can't free it if it hasn't been malloc'd, or in other words you can't free a variable on stack, it has to be on the heap.
Related
I just started learning C recently, and am having issues figuring out memory allocation. I have spent about the last 2~3 days in my extra time trying to figure this out, but have not found a solution yet. So first, I have two structs:
struct _list {
// arr is an array of string arrays
char **arr;
// recs tracks how many records are in the list
size_t recs;
// arrSizes records the size of each string array in arr
size_t *arrSizes;
};
typedef struct _list list_t;
and
struct _string {
char *string;
// size is used to store strlen
size_t size;
};
typedef struct _string string_t;
I initialize the above structs respectively in the following ways.
list_t:
list_t *NewList() {
list_t *List = NULL;
List = malloc(sizeof(*List));
if (List == NULL) {
fprintf(stderr, "Failed to allocate memory to list structure.\n");
return NULL;
}
List->arr = malloc(sizeof(List->arr));
if (List->arr == NULL) {
free(List);
fprintf(stderr, "Failed to allocate memory to list array.\n");
return NULL;
}
List->arrSizes = malloc(sizeof(List->arrSizes));
if (List->arr == NULL) {
free(List);
fprintf(stderr, "Failed to allocate memory to size array.\n");
return NULL;
}
List->recs = 0;
return List;
}
string_t:
// a string array read in by the program is passed with "char* record"
string_t *NewString(char *record)
{
string_t *String = NULL;
String = malloc(sizeof * String);
if (String == NULL) {
fprintf(stderr, "Failed to allocate memory to string structure.\n");
return NULL;
}
String->size = strlen(record) + 1;
String->string = malloc(String->size);
if (String->string == NULL) {
free(String);
fprintf(stderr, "Failed to allocate memory to string array.\n");
return NULL;
}
strcpy(String->string, record);
return String;
}
I read lines from a file and load them into a "matching results" buffer using something like the following code. Please ignore exits and the fact that I don't have null handling after struct initialization is complete; I will add something more useful later. Also, sorry about the length. I edited quite a bit to produce the smallest example I could think of that reproduces the issue.
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
// Check if File exists
void FileExists(FILE *FilePath) {
if (FilePath == NULL) {
fprintf(stderr, "Error: File not found.\n");
exit(1);
}
}
// Delete a string_t struct
int delString(string_t *Structure)
{
if (Structure != NULL) {
free(Structure->string);
free(Structure);
return 0;
}
return 1;
}
// Allocate memory for additional elements added to members of list_t struct
void AllocList(list_t *List, size_t StrLen)
{
char **ArrStrArr_tmp;
size_t *SizeArr_tmp;
char *StrArr_tmp;
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
StrArr_tmp = malloc(sizeof(*StrArr_tmp) * StrLen);
if ((ArrStrArr_tmp == NULL) || (SizeArr_tmp == NULL)
|| (StrArr_tmp == NULL)) {
fprintf(stderr, "Failed to allocate memory.\n");
exit(1);
}
else {
List->arr = ArrStrArr_tmp;
List->arrSizes = SizeArr_tmp;
(List->arr)[List->recs-1]= StrArr_tmp;
}
}
// Add a record to a buffer
int AddRecord(list_t *List, char *AppendRecord)
{
string_t *line = NewString(AppendRecord);
List->recs++;
AllocList(List, line->size);
(List->arr)[List->recs - 1] = line->string;
(List->arrSizes)[List->recs - 1] = line->size;
delString(line);
return 0;
}
// Sends entire string array to lowercase
void tolowerString(char *UpperString, size_t StrLen)
{
int i;
for (i = 0; i < (int)StrLen; i++) {
UpperString[i] = (char)tolower(UpperString[i]);
}
}
// Attempt to match string in lines from a file; lines with matches are read into a buffer
int main()
{
char line[80];
int PrintedLines = 0;
list_t *ResultList = NewList();
char *MyString = "theme";
char *Filename = "List.txt";
FILE *in = fopen(Filename, "r");
// Check if file exists
FileExists(in);
while (fscanf(in, "%79[^\n]\n", line) == 1)
{
char LookString[80];
strcpy(LookString, line);
LookString[strlen(LookString) - 1] = '\0';
// send lookstring to lowercase
tolowerString(LookString, strlen(LookString));
// add line to buffer ResultList if it contains MyString
if (strstr(LookString, MyString)) {
AddRecord(ResultList, line);
PrintedLines++;
}
}
// If PrintedLines is at zero after the while statement terminates, return in abnormal state
if (PrintedLines == 0) {
fprintf(stderr, "No matches found. Please check your input if you are sure there is a match.\n");
return 1;
}
fclose(in);
return 0;
}
When trying to read the 5th matching record into my buffer, my program crashes at this line in the AllocList function:
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
I get the following message on the version I have posted above:
realloc(): invalid old size
aborted (core dumped)
My guess is that I'm running into an error after some default amount of memory from my initial malloc is used, but I have no clue what is actually causing this. In my actual code I'm printing all sorts of things (pointer sizes, etc.), but I still can't spot anything. What's strange is, before writing this post, I was actually seeing the error:
realloc(): invalid next size
aborted (core dumped)
But I can't reproduce it now for some reason...
I have also read that I should reallocating memory for my list_t struct whenever I add an element to one of it's members, but reallocating it actually doesn't change where or how this program crashes. In any case, I'm not sure how I should be reallocating memory for my struct. To clarify, my questions are:
What is causing this memory issue?
Should I be reallocating memory for my list struct, and how much should I be reallocating given that I'm adding an extra element to the arr and arrSizes members?
As the crash suggests, the line
ArrStrArr_tmp = realloc(*List->arr, sizeof(**ArrStrArr_tmp) * List->recs);
is wrong.
This have it read an uninitialized buffer allocated via malloc(), whose value is indeterminate.
The intension of this line is to re-allocate the array pointed at by List->arr, which is an array of char*.
Therefore, the line should be
ArrStrArr_tmp = realloc(List->arr, sizeof(*ArrStrArr_tmp) * List->recs);
just like the following line, which is re-allocating an array of size_t.
SizeArr_tmp = realloc(List->arrSizes, sizeof(*SizeArr_tmp) * List->recs);
Also I found 2 more points for improvement:
Firstly, the usage of some malloc() in the function NewList are not good.
The function is creating zero-element array, so you won't need space for List->arr and List->arrSizes.
Also note that realloc() accepts NULL as the buffer to re-allocate.
list_t *NewList() {
list_t *List = NULL;
List = malloc(sizeof(*List));
if (List == NULL) {
fprintf(stderr, "Failed to allocate memory to list structure.\n");
return NULL;
}
List->arr = NULL;
List->arrSizes = NULL;
List->recs = 0;
return List;
}
Secondly, you are copying pointer instead of string in AddRecord,
so you have problems of memory leak and potential use-after-free.
It seems the string should be copied:
(List->arr)[List->recs - 1] = line->string;
should be
strcpy((List->arr)[List->recs - 1], line->string);
Suppose this is a part of my code:
int foo()
{
char *p, *q ;
if((p = malloc(BUFSIZ)) == NULL) {
return ERROR_CODE;
}
if((q = malloc(BUFSIZ)) == NULL) {
free(p)
return ERROR_CODE;
}
/* Do some other work... */
free(p);
free(q);
}
Since it's possible that the first malloc is successful but the second one fails, I use free(p) in the second "error handler". But what if there are more malloc's and what if I want to modify the code (adjusting their orders, adding or deleting some malloc)?
I know in C++ there are things like RAII and exception safe, etc. But in general, what is the correct way to handle malloc failure in C? (maybe using some goto?)
Your code is fine, but for lots of variables, I'd prefer:
int
foo()
{
char *p = NULL;
char *q = NULL;
int ret = 0;
if (NULL == (p = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
goto error;
}
// possibly do something here
if (NULL == (q = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
goto error;
}
// insert similar repetitions
// hopefully do something here
error:
free (p);
free (q);
return ret;
}
Note that freeing NULL is defined as a no-op.
This avoids n levels of indent for n variables. You can clean up filehandles etc. similarly (though you'll have to put a condition around the close()).
Now, if you know you can allocate them all at once, then dasblinkenlight has a good answer, but here's another way:
int
foo()
{
int ret = 0;
char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (!p || !q || !r)
{
ret = ERROR_CODE;
goto exit;
}
// do something
exit:
free(p);
free(q);
free(r);
return ret;
}
Final possibility: if you actually want to exit the program on malloc fail, consider using mallopt's M_CHECK_ACTION option. This makes malloc() faults get checked, and calls abort(), possibly printing a helpful message.
From the man page:
NAME
mallopt - set memory allocation parameters
SYNOPSIS
#include <malloc.h>
int mallopt(int param, int value);
DESCRIPTION
The mallopt() function adjusts parameters that control the behavior of the memory-allocation functions (see malloc(3)). The param argument specifies the parameter to be modified, and value specifies the new value for that parameter.
The following values can be specified for param:
M_CHECK_ACTION
Setting this parameter controls how glibc responds when various kinds of programming errors are detected (e.g., freeing the same pointer twice). The 3 least significant bits (2, 1, and 0) of the value assigned to this parameter determine the glibc behavior, as follows:
Bit 0: If this bit is set, then print a one-line message on stderr that provides details about the error. The message starts with the string "*** glibc detected ***", followed by the program name, the name of the memory-allocation function in which the error was detected, a brief description of the error, and the memory address where the error was detected.
Bit 1: If this bit is set, then, after printing any error message specified by bit 0, the program is terminated by calling abort(3). In glibc versions since 2.4, if bit 0 is also set, then, between printing the error message and aborting, the program also prints a stack trace in the manner of backtrace(3), and prints the process's memory mapping in the style of /proc/[pid]/maps (see proc(5)).
Bit 2: (since glibc 2.4) This bit has an effect only if bit 0 is also set. If this bit is set, then the one-line message describing the error is simplified to contain just the name of the function where the error was detected and the brief description of the error.
Since it is perfectly OK to pass NULL to free(), you could allocate everything that you need in a "straight line", check everything in a single shot, and then free everything once you are done, regardless of whether or not you have actually done any work:
char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (p && q && r) {
/* Do some other work... */
}
free(p);
free(q);
free(r);
This works as long as there are no intermediate dependencies, i.e. you do not have structures with multi-level dependencies. When you do, it is a good idea to define a function for freeing such a structure, without assuming that all memory blocks are non-NULL.
For large numbers of allocations, I would invest the time in creating a memory manager that keeps track of the allocations. That way, you never have to worry about leaks, regardless of whether or not the function succeeds.
The general idea is to create a wrapper for malloc that records successful allocations, and then frees them on request. To free memory, you simply pass a special size to the wrapper function. Using a size of 0 to free memory is appropriate if you know that none of your actual allocations will be for 0 sized blocks. Otherwise, you may want to use ~0ULL as the request-to-free size.
Here's a simple example that allows up to 100 allocations between frees.
#define FREE_ALL_MEM 0
void *getmem( size_t size )
{
static void *blocks[100];
static int count = 0;
// special size is a request to free all memory blocks
if ( size == FREE_ALL_MEM )
{
for ( int i = 0; i < count; i++ )
free( blocks[i] );
count = 0;
return NULL;
}
// using a linked list of blocks would allow an unlimited number of blocks
// or we could use an array that can be expanded with 'realloc'
// but for this example, we have a fixed size array
if ( count == 100 )
return NULL;
// allocate some memory, and save the pointer in the array
void *result = malloc( size );
if ( result )
blocks[count++] = result;
return result;
}
int foo( void )
{
char *p, *q;
if ( (p = getmem(BUFSIZ)) == NULL ) {
return ERROR_CODE;
}
if ( (q = getmem(BUFSIZ)) == NULL ) {
getmem( FREE_ALL_MEM );
return ERROR_CODE;
}
/* Do some other work... */
getmem( FREE_ALL_MEM );
return SUCCESS_CODE;
}
it is matter of habit, but I prefer:
int returnFlag = FAILURE;
if ((p = malloc...) != NULL)
{
if ((q = malloc..) != NULL)
{
// do some work
returnFlag = SUCCESS; // success only if it is actually success
free(q);
}
free(p);
}
return returnFlag; // all other variants are failure
IF you are expecting to allocate a large number of items, it Can get messy. Try to avoid the 'goto' approach. Not because of the old 'goto is bad' ethic, but because that way really can lie madness and memory leaks.
It's a little overkill for small numbers of malloc, but you can consider something like this approach:
void free_mem(void **ptrs, size_t len)
{
for (size_t i = 0; i < len; ++i)
{
free(ptrs[i]);
ptrs[i] = NULL;
}
}
int foo(...)
{
void *to_be_freed[N];
int next_ptr = 0;
for (size_t i = 0; i < N; ++i) to_be_freed[i] = NULL;
p = malloc(..);
if (!p)
{
free_mem(to_be_freed,N);
return ERROR_CODE;
}
to_be_freed[next_ptr++] = p;
// Wash, rinse, repeat, with other mallocs
free_mem(to_be_freed,N)
return SUCCESS;
}
In reality, you can probably wrap malloc with something which tracks this. Put the array and array size in a structure and pass that in with the desired allocation size.
I think the first answer is the most general purpose as it can be used for errors other than those caused by malloc. However I would remove the gotos and use a single pass while loop like so.
int foo()
{
char *p = NULL;
char *q = NULL;
int ret = 0;
do {
if (NULL == (p = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
break;
}
// possibly do something here
if (NULL == (q = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
break;
}
// insert similar repetitions
// hopefully do something here
} while(0);
free (p);
free (q);
return ret;
}
Giving this example function:
And giving that I can only change the else statement in the function, how should I handle a memory allocation error? I can't return some error code since the function returns void, I can't throw exceptions in C, I can't just stop the execution in a function for an error that could be minor relatively to the other things that the program is doing... should I just let the pointer be NULL and let the coder that is using my function to handle the errors after calling my function?
The only functions I can use are the function in these libraries: stdio.h, stdlib.h, string.h, stdarg.h
Thank you!
should I just let the pointer be NULL and let the coder that is using my function to handle the errors after calling my function?
Probably yes.
But not only "after" but also before, that is the coder needs to store a copy of the address the pointer being passed into your function points to. Because if the coder didn't and your function failed the program leaked memory, namly such which the pointed to address referenced.
char * p = malloc(42);
{
char * p_save = p;
f(&p_save, "");
if (NULL == p_save)
{
/* log error or what ever is needed, including freeing p and return, exit or abort */
}
else
{
p = p_save;
}
}
free(p);
Or just wrap that sick function into something that is more easy to handle:
int fex(char ** pp, const char * t)
{
char * p = *pp; /* Store a backup, just in case. */
f(pp, t);
if (NULL == *pp)
{
*pp = p; /* Opps, restore ... */
return -1; /* ... and indicate error. errno should already have been set by realloc in f. */
}
return 0; /* Good. */
}
or (a bit dirty):
char * fex(char * p, const char * t)
{
f(&p, t);
if (NULL == p)
{
return (char *) -1; /* ... and indicate error. errno should already have been set by realloc in f. */
}
return p; /* Good. */
}
Call the latter like:
char * p = malloc(42);
{
char * tmp = fex(p, "");
if ((char *) -1) == tmp)
{
perror("fex() failed");
}
else
{
p = tmp;
}
}
free(p);
*p = NULL;
syslog(LOG_ERR, "Memory allocation error");
In class we do
printf("Error allocating memory.");
exit(1);
or simply exit(1). If your memory is full, there's not a lot you can do from there.
Usually this is handled like:
abort();
in many implementation of programs and libraries.
My app is use in stlinux (sh4) and unfortunately valgrind does not support sh4 cpu.
since I saw memory leak with my app, I had used mtrace, and it confirmed that some memory is not free. The problem is, variable of malloc used in the return, therefore I do not have any idea, how could I free it (since if it would be free, then returning in the functions is meaningless)?
I had written cs_malloc (put bellow code from oscam-simple.c in above link), mtrace log says, that in line:
*tmp = malloc (size);
memory is not free
/* This function encapsulates malloc. It automatically adds an error message to the log if it failed and calls cs_exit(quiterror) if quiterror > -1.
result will be automatically filled with the new memory position or NULL on failure. */
void *cs_malloc(void *result, size_t size, int32_t quiterror){
void **tmp = result;
*tmp = malloc (size);
if(*tmp == NULL){
cs_log("Couldn't allocate memory (errno=%d %s)!", errno, strerror(errno));
if(quiterror > -1) cs_exit(quiterror);
} else {
memset(*tmp, 0, size);
}
return *tmp;
}
And then for malloc, I call it, like this:
// create the AES key entry for the linked list
if(!cs_malloc(&new_entry, sizeof(AES_ENTRY), -1)) return;
Please take a look at these 3 functions (which malloc is not free , and as other users said, valgrind claim that these codes cause memory leaks module-datastruct-llist.c
The memory leaks cause by 3 different parts:
in below codes "new" would never free , but since it use in return of that function, I don't have idea, how could I free it:
LL_NODE* ll_append_nolock(LLIST *l, void *obj)
{
if (l && obj) {
LL_NODE *new;
if(!cs_malloc(&new,sizeof(LL_NODE), -1)) return NULL;
new->obj = obj;
if (l->last)
l->last->nxt = new;
else
l->initial = new;
l->last = new;
l->count++;
return new;
}
}
also "l" use in below function, again since it use in return function, I have no idea how to free it. :
LLIST *ll_create()
{
LLIST *l = cs_malloc(&l, sizeof(LLIST), 0);
pthread_mutex_init(&l->lock, NULL);
return l;
}
same story with new :
LL_NODE *ll_prepend(LLIST *l, void *obj)
{
if (l && obj) {
LL_NODE *new;
if(!cs_malloc(&new,sizeof(LL_NODE), -1)) return NULL;
new->obj = obj;
ll_lock(l);
new->nxt = l->initial;
l->initial = new;
if (!l->last)
l->last = l->initial;
l->count++;
ll_unlock(l);
return new;
}
return NULL;
}
For more functions you could see module-datastruct-llist.c
Would highly appreciate, if any expert tell me, how could I fix that memory leak (if you feel, cs_malloc should be rewritten, or need to add new function, please write the source code you are meaning.
The most common implementations of malloc use heap memory, which is global, so it's very common to have storage allocated in one place passed around between a number of functions before it is finally freed.
Now, there are for instance calls to ll_append_nolock where you ignore the malloced return. I.e.
ll_append_nolock(it->l, obj);
so to avoid a leak you need to do what you do in other places, i.e let the calling function receive the allocated memory into a pointer:
LL_NODE *n = ll_append_nolock(l, obj);
/* do stuff with "n", which points to memory allocated under the name of "new" */
free(n);
And when you're through with n (which as noted above points to the storage allocted under the name "new", that is: same memory, different names), you free it.
HTH.
In your function cs_malloc the first parameter is result however you never assign to it in the function cs_malloc.
Later you use cs_malloc like this
if(!cs_malloc(&new,sizeof(LL_NODE), -1)) return NULL;
new->obj = obj;
which will not work since since "new" is left uninitialized
you should either assign to result in your cs_malloc or just return the block in cs_malloc, if you fail to allocate just return NULL instead.
e.g.
void *cs_malloc(size_t size, int32_t quiterror)
{
void* tmp = calloc(1,size);
if(tmp == NULL)
{
cs_log("Couldn't allocate memory (errno=%d %s)!", errno, strerror(errno));
if(quiterror > -1) cs_exit(quiterror);
}
return tmp;
}
and
if (new = cs_malloc(sizeof(LL_NODE),-1))
{
new->obj = obj;
}
else
{
return NULL;
}
#Anders
Thanks for reply , i would take consideration your note ,an would change it as u described to see memory leaks how it's goes...
How , this line should be change to the new cs_malloc function u had written:
1.
LLIST *l = cs_malloc(&l, sizeof(LLIST), 0);
pthread_mutex_init(&l->lock, NULL);
return l;
2.
if(!cs_malloc(¶,sizeof(struct read_thread_param), -1)) return FALSE;
para->id=i;
I would like to actually use this wrapper, but the problem is i don't know if it's very safe, yet.
I have a few simple questions regarding using malloc(), calloc(), and realloc(). Here's what I have so far:
string.h
typedef struct str str; // pointer for encapsulation
string.c
struct str
{
char *buf;
size_t len;
}
Say i have a helper function that simply does this:
str *NEW_STRING()
{
str *temp = calloc(1, sizeof (struct str));
temp->len = 0;
temp->buf = (char *) malloc(1);
return temp;
}
Is this safe? If it is, what would happen if i did something like this:
str *A_STRING = NEW_STRING();
A_STRING = NEW_STRING();
It would call malloc and calloc twice, is that bad? Would an initializer be better?
void str_init(str *A_STRING)
{
if (A_STRING)
{
free(A_STRING);
}
if (A_STRING->buf)
{
free(A_STRING->buf);
}
A_STRING = calloc(1, sizeof (struct str));
A_STRING->buf = (char *) malloc(1);
A_STRING->len = 0;
}
Finally, is this a good way to free memory?
void free_string(str *A_STRING)
{
if (A_STRING->buf)
{
free(A_STRING->buf);
}
else
{
A_STRING->buf = NULL;
}
if (A_STRING)
{
free(A_STRING);
}
else
{
A_STRING = NULL;
}
A_STRING->len = 0;
}
Any additional information would be great if included. I don't want to release anything to the public as if it were a good library, because i am primarily doing this for learning purposes.
Lots of errors:
is this safe? if it is, what would happen if i did something like this:
No.
str *NEW_STRING()
{
str *temp = calloc(1, sizeof (struct str));
// If calloc fails and returns NULL all the code below is invalid and blows the code up.
Next:
it would call malloc and calloc twice, is that bad? would an initializer be better?
You leak memory.
The second call basically generates a new object the old object is lost and leaks.
Problems in str_init
void str_init(str *A_STRING)
{
Is this the first time that his method is called?
If so then A_STRING contains a random value (that you are about to FREE).
This will blow the code up.
if (A_STRING)
{
free(A_STRING);
}
A_STRING is freed (you can now no longer accesses it).
Any code that does so is bad.
if (A_STRING->buf) // Bang blow up code.
{
free(A_STRING->buf);
}
A_STRING = calloc(1, sizeof (struct str));
No checking the result of calloc.
A_STRING->buf = (char *) malloc(1);
A_STRING->len = 0;
}
is this a good way to free memory?
void free_string(str *A_STRING)
{
if (A_STRING->buf)
{
free(A_STRING->buf);
}
else
{
A_STRING->buf = NULL; // Its already NULL pointless work
}
if (A_STRING)
{
free(A_STRING);
}
else
{
A_STRING = NULL; // ITs already NULL pointless work
}
// BANG you just blew up the space shuttle.
A_STRING->len = 0;
}