I have a generic linked-list that holds data of type void* I am trying to populate my list with type struct employee, eventually I would like to destruct the object struct employee as well.
Consider this generic linked-list header file (i have tested it with type char*):
struct accListNode //the nodes of a linked-list for any data type
{
void *data; //generic pointer to any data type
struct accListNode *next; //the next node in the list
};
struct accList //a linked-list consisting of accListNodes
{
struct accListNode *head;
struct accListNode *tail;
int size;
};
void accList_allocate(struct accList *theList); //allocate the accList and set to NULL
void appendToEnd(void *data, struct accList *theList); //append data to the end of the accList
void removeData(void *data, struct accList *theList); //removes data from accList
--------------------------------------------------------------------------------------
Consider the employee structure
struct employee
{
char name[20];
float wageRate;
}
Now consider this sample testcase that will be called from main():
void test2()
{
struct accList secondList;
struct employee *emp = Malloc(sizeof(struct employee));
emp->name = "Dan";
emp->wageRate =.5;
struct employee *emp2 = Malloc(sizeof(struct employee));
emp2->name = "Stan";
emp2->wageRate = .3;
accList_allocate(&secondList);
appendToEnd(emp, &secondList);
appendToEnd(emp2, &secondList);
printf("Employee: %s\n", ((struct employee*)secondList.head->data)->name); //cast to type struct employee
printf("Employee2: %s\n", ((struct employee*)secondList.tail->data)->name);
}
Why does the answer that I posted below solve my problem? I believe it has something to do with pointers and memory allocation. The function Malloc() that i use is a custom malloc that checks for NULL being returned.
Here is a link to my entire generic linked list implementation: https://codereview.stackexchange.com/questions/13007/c-linked-list-implementation
The problem is this accList_allocate() and your use of it.
struct accList secondList;
accList_allocate(&secondList);
In the original test2() secondList is memory on the stack. &secondList is a pointer to that memory. When you call accList_allocate() a copy of the pointer is passed in pointing at the stack memory. Malloc() then returns a chunk of memory and assigns it to the copy of the pointer, not the original secondList.
Coming back out, secondList is still pointing at uninitialised memory on the stack so the call to appendToEnd() fails.
The same happens with the answer except secondList just happens to be free of junk. Possibly by chance, possibly by design of the compiler. Either way it is not something you should rely on.
Either:
struct accList *secondList = NULL;
accList_allocate(&secondList);
And change accList_allocate()
accList_allocate(struct accList **theList) {
*theList = Malloc(sizeof(struct accList));
(*theList)->head = NULL;
(*theList)->tail = NULL;
(*theList)->size = 0;
}
OR
struct accList secondList;
accList_initialise(secondList);
With accList_allocate() changed to accList_initialise() because it does not allocate
accList_initialise(struct accList *theList) {
theList->head = NULL;
theList->tail = NULL;
theList->size = 0;
}
I think that your problem is this:
You've allocated secondList on the stack in your original test2 function.
The stack memory is probably dirty, so secondList requires initialization
Your accList_allocate function takes a pointer to the list, but then overwrites it with the Malloc call. This means that the pointer you passed in is never initialized.
When test2 tries to run, it hits a bad pointer (because the memory isn't initialized).
The reason that it works when you allocate it in main is that your C compiler probably zeros the stack when the program starts. When main allocates a variable on the stack, that allocation is persistent (until the program ends), so secondList is actually, and accidentally, properly initialized when you allocate it in main.
Your current accList_allocate doesn't actually initialize the pointer that's been passed in, and the rest of your code will never see the pointer that it allocates with Malloc. To solve your problem, I would create a new function: accList_initialize whose only job is to initialize the list:
void accList_initialize(struct accList* theList)
{
// NO malloc
theList->head = NULL;
theList->tail = NULL;
theList->size = 0;
}
Use this, instead of accList_allocate in your original test2 function. If you really want to allocate the list on the heap, then you should do so (and not mix it with a struct allocated on the stack). Have accList_allocate return a pointer to the allocated structure:
struct accList* accList_allocate(void)
{
struct accList* theList = Malloc( sizeof(struct accList) );
accList_initialize(theList);
return theList;
}
Two things I see wrong here based on the original code, in the above question,
What you've seen is undefined behaviour and arose from that is the bus error message as you were assigning a string literal to the variable, when in fact you should have been using the strcpy function, you've edited your original code accordinly so.. something to keep in mind in the future :)
The usage of the word Malloc is going to cause confusion, especially in peer-review, the reviewers are going to have a brain fart and say "whoa, what's this, should that not be malloc?" and very likely raise it up. (Basically, do not call custom functions that have similar sounding names as the C standard library functions)
You're not checking for the NULL, what if your souped up version of Malloc failed then emp is going to be NULL! Always check it no matter how trivial or your thinking is "Ah sher the platform has heaps of memory on it, 4GB RAM no problem, will not bother to check for NULL"
Have a look at this question posted elsewhere to explain what is a bus error.
Edit: Using linked list structures, in how the parameters in the function is called is crucial to the understanding of it. Notice the usage of &, meaning take the address of the variable that points to the linked list structure, and passing it by reference, not passing by value which is a copy of the variable. This same rule applies to usage of pointers also in general :)
You've got the parameters slightly out of place in the first code in your question, if you were using double-pointers in the parameter list then yes, using &secondList would have worked.
It may depend on how your Employee structure is designed, but you should note that
strcpy(emp->name, "Dan");
and
emp->name = "Dan";
function differently. In particular, the latter is a likely source of bus errors because you generally cannot write to string literals in this way. Especially if your code has something like
name = "NONE"
or the like.
EDIT: Okay, so with the design of the employee struct, the problem is this:
You can't assign to arrays. The C Standard includes a list of modifiable lvalues and arrays are not one of them.
char name[20];
name = "JAMES" //illegal
strcpy is fine - it just goes to the memory address dereferenced by name[0] and copies "JAMES\0" into the memory there, one byte at a time.
Related
This is my code:-
typedef struct Frame
{
char* name;
unsigned int duration;
char* path; // may need to scan (with fgets)
}frame_t;
typedef struct Link
{
frame_t* frame;
struct Link* next;
}link_t;
void addNewFrame(void)
{
link_t* newLink = (link_t**)malloc(sizeof(link_t*));
printf(" *** Creating new frame ***\n\n");
printf("Please insert frame path:\n");
// newLink->frame->name = (char*)malloc(sizeof(char) * MAX_LEN);
fgets(newLink->frame->name, MAX_LEN,stdin);
printf("%s", newLink->frame->name);
}
I just need to add a data to name variable in the "Frame" link list, please help me by reviewing this code.
You want to allocate the right types here:-
link_t* newLink = malloc(sizeof(link_t)); //Pointer to Link st
if(newLink){
newLink->frame = malloc(sizeof(frame_t)); //Pointer to frame member
if(newLink->frame){
newLink->frame->name = malloc(sizeof(char) * MAX_LEN); //Pointer to name member
if(newLink->frame->name){
//Rest of your code
}
}
}
EDIT:-
1. As pointed out in comments there's no need to cast the pointer returned by malloc()
2. Another very imp point you may want to check validity of the pointers before de-referencing them
First. You don't need to cast void * so (link_t **)malloc(... can be only malloc(....
Second. You allocated enough memory for a pointer not for a struct. I think you mean malloc(sizeof(link_t)) or even better malloc(sizeof(*newLink))
Third newLink->frame is a pointer so you need to allocate data for it too, newLink->frame = malloc(sizeof(frame_t))
Fourth newLink->frame->name is still a pointer so you need to allocate data for it too. newLink->frame->name = malloc(MAX_LEN)
The confusion that you are doing is pretty common. When you say type *something you are allocating a pointer to type in the stack. A pointer needs to point to somewhere else or NULL, or bad things happen. This applies to structures too. If your structure has a pointer member you need to point it to somewhere else. The somewhere else is where the real "type" object resides.
This also applies to arrays. If you say 'int foo[10]' you are allocating ten integers in the stack. If you say int *foo[10] you are allocating ten pointers in the stack. If you say int **foo you are allocating one pointer in the stack. Again all pointers need to be initialized, I mean, they need to point to some valid object, allocated somewhere else in the memory.
I hope this helps.
Some other points.
Always check pointer coming from malloc, if allocation failed you'll receive NULL. Dereferencing NULL will break your program.
Always initialize memory coming from malloc, fill it with zeros or something.
Don't use _t suffix, is POSIX reserved.
I didn't test any of this.
For some reason, I cannot manage to correctly free the structure I created, which goes as follows:
struct dictionary{
char word[50]; // Word to be described
char definition[150]; // Definition of said word
struct dictionary* next; // Linked list structure
}*dic;
typedef struct dictionary Dic;
Dic* newWord(char word[], char definition[]){
Dic* dc;
dc = malloc(sizeof(*dic));
strcpy(dc->word,word);
strcpy(dc->definition,definition);
dc->next = NULL;
return dc;
}
Now, if I do something like:
dc = newWord("Word","definition");
free(dc);
printWord(dc);
where:
void printWord(Dic* dic){
if(dic){
printf("<%s>\n",dic->word);
printf(".%s\n",dic->definition);
}
}
will result in no word being printed, yet the definition still being printed, which I can only assume means I'm not properly freeing this structure's memory. How do I go by properly doing it?
You are using a pointer after freeing it. This is undefined behavior, which means the C compiler can do whatever it wants. It can crash, it can appear to behave properly, or it can do anything in between. Calling free on a pointer doesn't set it to NULL, it releases your claim on the memory, but you have to set it to NULL yourself.
This question already has answers here:
Returning a struct pointer
(6 answers)
returning a local variable from function in C [duplicate]
(4 answers)
Closed 8 years ago.
I'm creating a structure inside a function and then returning its pointer. For some reason I keep getting the same memory address each time.
typedef struct list_type
{
node_t *head;
node_t *tail;
} list_t;
list_t newList() {
list_t list = {NULL, NULL};
list_t *listptr = &list;
printf("newList: %p\n", listptr);
return listptr;
}
Outputs:
newList: 0x7fffb42c8ae0
newList: 0x7fffb42c8ae0
newList: 0x7fffb42c8ae0
What am I doing?...
You are allocating list on the stack, but you're trying to return a reference to it from a function. This is a big no-no in C, since the struct's memory is automatically freed by the compiler when it goes out of scope. If you want memory to persist beyond the scope it is allocated in, you need to use malloc.
Furthermore, list_t and a pointer to it (list_t*) are two different types, and you need to indicate that as such in your function definition.
list_t* newList() {
list_t *listptr = malloc(sizeof(list_t));
printf("newList: %p\n", listptr);
return listptr;
}
However, be careful when using malloc. Since you're allocating memory manually, you'll now need to ensure that you call free on the pointer when you're done with it, or it will leak, since the compiler cannot free the memory for you.
list_t list = {NULL, NULL};
allocates memory on stack. Memory allocated on stack is automatically freed when the variable, list in your case, goes out of scope.
That is why you are getting the same address again and again as memory allocated to list on stack gets freed when program comes out of function newList() and is available for allocation. And, when you call the newList() again, same memory is alloacted again.
You should read about different memory allocations and pay more attention to the compiler warnings.
You're returning a pointer to an object allocated on the stack. If you use the value that the pointer points to in the function that calls newList(), you'll cause undefined behavior to occur.
You should read about malloc() and free().
Your function returns a list_t, a structure type, which is perfectly fine by-value. I see no evidence you're trying to dynamically allocate a list_t, and no evidence you even need to do so.
Lose the pointer stuff for this specific structure type entirely and just do this:
list_t newList()
{
list_t list = {NULL, NULL};
return list;
}
void freeList(list_t lst)
{
// TODO: delete lst nodes by walking lst.head through lst.tail
}
int main()
{
list_t lst = newList();
...
freeList(lst);
}
Don't step into the arena of managing dynamic memory because you can; do it when you need to do so, and in this case, you don't. You obviously need to in order to manage the actual list nodes, but thats a different issue than this one.
I have a bunch of structures that all look like
typedef struct {
A[1..100] *next; // this is not an array, just indicating A1 or A2 or A3 and so on
//other stuff that varies from struct to struct
} A[1..100] // A1, A2, and so on
I generate a few linked lists of different same type structures. Somewhere in a function, I allocate the memory with something like
A55 *struct_list;
A55 *next_in_list;
struct_list = (A55 *)malloc(sizeof(A55));
(*struct_list).next = NULL;
//some loop
next_in_list = (A55 *)malloc(sizeof(A55));
(*next_in_list).next = struct_list;
struct_list = next_in_list;
At the end of the loop, struct_list is a pointer to the end of the linked list.
I would like to have a single function that would free any list regardless of the structure that populates it. I feel the following might work, but I need something which will not break any rules and might be implementation safe:
void freeStruct(*void start){
void ** current, * next;
current = (void **) start;
do{
next = *current;
free(current);
current = (void **) next;
}while(current != NULL)
}
My question is whether NULL has the same numerical value for all pointers to all types, including struct. And, is there a better way of doing this without having to copy the same function 100 times for the different struct definitions?
NULL always has the same value: 0.
You can do what you want. The way to do it is to put the "next" pointer at the very beginning of the struct, so it is always in the same place for each struct.
If you have any more structure than a single "next" pointer, you should probably make a struct out of it all, and then put the struct at the beginning of each struct. For example, if you will make a doubly-linked list with both "prev" and "next" pointers, I suggest making a struct with the two pointers.
If each struct can simply be freed, you can just call the free() function on each pointer. If you sometimes need to run a cleanup function, you should make your universal linked-list freeing function take a pointer to a cleanup function, and call the cleanup on each struct as it is reached in the list.
You need to traverse through the entire list while freeing the pointers one at a time.
I am using a tree in C to keep track of an undefined and varying number of input fields. I have a struct with a set number of fields as follows:
struct mystruct {
int id, mpid;
char *name;
struct myotherstruct *myostr;
};
I have a pointer instance of this type ( mystruct ) which I allocate memory for, then I fill these struct values in with input I've read from a file. I then use the tsearch function from search.h to add my mystruct object to the mystruct tree. The problem I am having, is that if I use the tfind function to retrieve a mystruct pointer from the tree, the mystruct memory that is returned has no recollection of the myotherstruct pointer data I allocated and pointed to when creating the value prior to adding to the tree.
The general sequence is as follows:
struct mystruct {
int id, mpid;
char *name;
struct myotherstruct *myostr;
};
struct myotherstruct {
int spid;
};
// allocate memory to temporary mystruct pointer
// add mpid field to mystruct pointer ( used in comparison function )
if( // tfind == NULL )
{
// set id value
// allocate char memory and strncpy correct value in
// allocate myotherstruct memory and assign all values
// tsearch for this newly created mystruct memory chunk so that it is added to tree
}
else
{
// fails here when attempting to access the data returned by mytfind
}
...
Using gdb, the program very clearly enters the if loop the first time ( since the tree is empty ) and creates a valid and full mystruct pointer with proper memory allocation. When tsearch returns it's output, the memory location is different from that of the mystruct pointer I filled in and then I am incapable of doing prints for mystruct->myotherstruct variables such as spid. The exact output I get when attempting to print in gdb is : Cannot access memory at address 0x----- where the -'s are various locations in memory that I apparently cannot access.
I suspect that there may be a problem with my comparison function since I am only comparing mystruct mpid fields to determine whether a tree node exists yet for a mystruct object, but my inexperience with both C and tsearch/tfind functionality are showing a bit here. Hopefully someone with more experience is able to help me since the examples provided on various tsearch.h webpages don't handle very sophisticated examples. Thanks in advance for the help!
PS: the code must remain in C, so language swapping doesn't suffice :(
EDIT:
here is my compare function:
int distcmp(const void *a, const void *b){
return ((int)((struct mystruct *)a)->mpid) != (int)(((struct mystruct *)b)->mpid);
}
Also, I use tfind initially because I want to know whether a particular value exists in the tree yet. If it does not exist ( it returns NULL ) then it enters the if loop and fills in the new mystruct object and adds it to the tree ( using tsearch ). If it already exists, a pointer to that object comes out of tfind which I assign to a mystruct pointer and use in the else portion of the code. Hopefully this helps. Thanks again.
SOLVED:
Just to update, the issue is that the returned pointer from tsearch and tfind is not of the type mystruct. It is a pointer to the memory location of the mystruct value that matched my search. In this case, the issue would be resolved by accessing the returned pointer with a * and passing that value to a mystruct pointer. Thanks to those who commented.