structure is not copying to other struct - c

So, I have this struct:
typedef struct person {
char *name;
int age;
struct person* next;
}
I also have my hash_table:
person *table[50];
I have a function that gets the variables I want to use:
void add_person() {
person *new_person = (person*) malloc(sizeof(new_person));
char str1[50];
int age;
scanf(" %[^:\n]:%d",str1,&age);
new_person->name = (char*) malloc((strlen(str1)+1)*sizeof(char));
strcpy(new_person->name,str1);
new_person->age =age;
printf("%s %d",new_person->name,new_person->age) //checking if struct variables change (they do)
insert_hash_table(new_person);
free(new_person);
free(new_person->name);
}
Here it is my insert_hash_table(person *p) that adds an element to my hash table:
void insert_hash_table(person *p) {
int index = hash(p->name); //hash function
p->next = table[index];
table[index] = p;
printf("%s",table[index]->name); //just to check if the struct was copied
}
But when I type this input:
Josh:35
It dosenĀ“t print "Josh" which was suposed to because of this command:
printf("%s",table[index]->name)
Any suggestions? I would be thankful for any help you could give.

add_person allocates a person and its name, but insert_hash_table does not copy the data, it merely references it. Consequently, after the data was freed in add_person, it is not accessible anymore. Also, the deallocation order is incorrect. The name has to be freed first as its pointer is stored in person. Only then, person can be deallocated.
Besides changing the deallocation order, you have to make an actual copy in insert_hash_table. You can do so by allocating another person, copy the person by dereferencing both pointers to trigger a shallow copy (*hashtable_person = *person) and copy the name using strdup.
I'm not sure what the next field represents, though. next implies a singly-linked list, but the code does not remember the last inserted person to be able to create one.
In this specific case, it might be simpler to just pass the ownership of person on to the hash table by calling insert_hash_table. Then you can allocate the person, but do not have to worry about deallocation. Removing the person from the hash table could either trigger a deallocation or - to be consistent - you could pass the ownership back to the caller in that case.

Related

Accessing nested structures

I'm having some trouble accessing the checkups struct that is contained within dog. Since dog is contained within contained I assumed that I would just point to the checkups from dog from the container but that seems to be causing an error when trying to add a checkup date or replace one.
// used to create a linked list of containers, each contaning a "dog"
struct container {
struct dog *dog;
struct container *next;
} *list = NULL;
// used to hold dog information and linked list of "checkups"
struct dog {
char name[30];
char breed[30];
struct checkup *checkups;
};
// used to create a linked list of checkups containing "dates"
struct checkup {
char date[30];
struct checkup *next;
};
Here is the code where I try to add a new date to the checkups but when it tried to add it on my program crashes. Any direction on why this would be occurring would be greatly appreciated.
void add_checkup(char* name, char* date)
{
struct container *tempList = list;
struct checkup *tempCheck = (struct checkup *) malloc(sizeof(struct checkup));
while (tempList != NULL) {
if (strcmp(tempList->dog->name, name) == 0) {
strcpy(tempCheck->date, date);
strcpy(tempList->dog->checkups, tempCheck);
}
tempList->next;
}
}
Why are you using strcpy() to set the value of a struct checkup *? That attempts to copy the data pointed to by the source pointer to the array assumed to be pointed to by destination pointer, when in fact I see no particular reason to suppose that the destination pointer is even valid. Certainly there's no reason to suppose that the source data has the form of a null-terminated array of char, so your original code is badly wrong here.
Simple assignment is more likely to be correct:
tempList->dog->checkups = tempCheck;
Note, however, that that presents a serious risk of memory leakage because the previous value of tempList->dog->checkups is lost. Note also that it doesn't match the behavior implied by the add_ part of the function name -- it's more like set_. If you want to be able to maintain more than one checkup per dog then you would need to do more work.

Refering to an Array. Incrementing and saving index

Learning C through "Learning C the hard way", and doing some of my own exercises. I stumbled upon the following problem.
Let's say I have the following structure:
struct Person {
char name[MAX_INPUT];
int age;
}
In main(), I have declared the following array:
int main(int argc, char *argv[]) {
struct Person personList[MAX_SIZE];
return 0;
}
Now let's say 2 functions away (main calls function1 which calls function2) I want to save a person inside the array I declared in the main function like so:
int function2(struct Person *list) {
struct Person *prsn = malloc(sizeof(struct Person));
assert(prsn != NULL); // Why is this line necessary?
// User input code goes here ...
// Now to save the Person created
strcpy(prsn->name, nameInput);
ctzn->age = ageInput;
list = prsn; // list was passed by reference by function1, does main need to pass the array by
// reference to function1 before?
// This is where I get lost:
// I want to increment the array's index, so next time this function is called and a
// new person needs to be saved, it is saved in the correct order in the array (next index)
}
So if I return to my main function and wanted to print the first three persons saved in it like so:
...
int i = 0;
for(i = 0; i < 3; i++) {
printf("%s is %d old", personList[i].name, personList[i].age);
}
...
Basically how to reference the array across the application while keeping it persistent. Keeping in mind that main does not necessarily call the function directly that makes use of the array. I'm suspecting someone might suggesting declaring it as a global variable, then what would be the alternative? Double pointers? How do double pointers work?
Thank you for your time.
Here are a few pointers (no pun intended!) to help you along:
As it stands, the line struct Person personList[MAX_SIZE]; allocates memory for MAX_SIZE number of Person structs. You don't actually need to allocate more memory using malloc if this is what you are doing.
However, you could save some memory by only allocating memory when you actually need a person. In this case, you want the personList array to contain pointers to Person structs, not the structs themselves (which you create using malloc).
That is: struct Person * personList[MAX_SIZE];
When you create the person:
struct Person * person = (struct Person *) malloc(sizeof(struct Person));
personList[index] = person;
And when you use the person list: printf("%s", personList[index]->name);
Arrays don't magically keep a record of any special index. You have to do this yourself. One way is to always pass the length of the array to each function that needs it.
void function1(struct Person * personList, int count);
If you wanted to modify the count variable when you returned back to the calling function, you could pass it by reference:
void function1(struct Person * personList, int * count);
A possibly more robust way would be to encapsulate the count and the array together into another structure.
struct PersonList { struct Person * list[MAX_SIZE]; int count; }
This way you can write a set of functions that always deal with the list data coherently -- whenever you add a new person, you always increment the count, and so on.
int addNewPerson(struct PersonList * personList, char * name, int age);
I think that much should be helpful to you. Just leave a comment if you would like something to be explained in more detail.
First of all, malloc does not guarantee to allocate new space from the memory and return it. If it cannot allocate the requested memory, it returns a NULL value. That's why it is necessary to check the pointer.
While you are calling function two, you can pass the address of the next element by using a variable that holds the current count of the array in function1;
function2(&personList[count++]);
then you return the current count from function1 to the main function;
int size=function1(personList);

C generic linked-list

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.

define a function returning struct pointer

Please bear with me, i m from other language and newbie to c and learning it from http://c.learncodethehardway.org/book/learn-c-the-hard-way.html
struct Person {
char *name;
int age;
int height;
int weight;
};
struct Person *Person_create(char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof(struct Person));
assert(who != NULL);
who->name = strdup(name);
who->age = age;
who->height = height;
who->weight = weight;
return who;
}
I understand the second Person_create function returns a pointer of struct Person. I don't understand is(may be because i m from other language, erlang, ruby), why does it define it as
struct Person *Person_create(char *name, int age, int height, int weight)
not
struct Person Person_create(char *name, int age, int height, int weight)
and is there other way to define a function to return a structure?
sorry if this question is too basic.
It is defined so because it returns a pointer to a struct, not a struct. You assign the return value to a struct Person *, not to struct Person.
It is possible to return a full struct, like that:
struct Person Person_create(char *name, int age, int height, int weight)
{
struct Person who;
who.name = strdup(name);
who.age = age;
who.height = height;
who.weight = weight;
return who;
}
But it is not used very often.
The Person_create function returns a pointer to a struct Person so you have to define the return value to be a pointer (by adding the *). To understand the reason for returning a pointer to a struct and not the struct itself one must understand the way C handles memory.
When you call a function in C you add a record for it on the call stack. At the bottom of the call stack is the main function of the program you're running, at the top is the currently executing function. The records on the stack contain information such as the values of the parameters passed to the functions and all the local variables of the functions.
There is another type of memory your program has access to: heap memory. This is where you allocate space using malloc, and it is not connected to the call stack.
When you return from a function the call stack is popped and all the information associated with the function call are lost. If you want to return a struct you have two options: copy the data inside the struct before it is popped from the call stack, or keep the data in heap memory and return a pointer to it. It's more expensive to copy the data byte for byte than to simply return a pointer, and thus you would normally want to do that to save resources (both memory and CPU cycles). However, it doesn't come without cost; when you keep your data in heap memory you have to remember to free it when you stop using it, otherwise your program will leak memory.
The function returns who, which is a struct Person * - a pointer to a structure. The memory to hold the structure is allocated by malloc(), and the function returns a pointer to that memory.
If the function were declared to return struct Person and not a pointer, then who could also be declared as a structure. Upon return, the structure would be copied and returned to the caller. Note that the copy is less efficient than simply returning a pointer to the memory.
Structs are not pointers (or references) by default in C/C++, as they are for example in Java. Struct Person Function() would therefor return struct itself (by value, making a copy) not a pointer.
You often don't want to create copies of objects (shallow copies by default, or copies created using copy constructors) as this can get pretty time consuming soon.
To copy the whole struct and not just pointer is less efficient because a pointer's sizeof is usually much smaller than sizeof of a whole struct itself.
Also, a struct might contain pointers to other data in memory, and blindly copying that could be dangerous for dynamically allocated data (if a code handling one copy would free it, the other copy would be left with invalid pointer).
So shallow copy is almost always a bad idea, unless you're sure that the original goes out of scope - and then why wouldn't you just return a pointer to the struct instead (a struct dynamically allocated on heap of course, so it won't be destroyed like the stack-allocated entities are destroyed, on return from a function).

Why does my list only ever return the element most recently added?

Greetings again,
I have this problem again on C but now using struct.
Having this structure of student
struct student {
char *name;
int age;
}
I wanted to a list where I could add a number of Student and can also view all of its elements. Here's the code I have done so far.
#include<stdio.h>
#include<stdlib.h>
// struct student ...
void add(student **list, char* name, int age) {
student* temp = (student *)malloc(sizeof(student));
temp->name = name
temp->age = age;
*list = temp;
*(list++) = (student *)malloc(sizeof(student));
}
void view(student **list) {
student* data = *list;
while(data != '\0') { printf("%s%i", data->name, data->age); *(data++); }
}
main() {
student* list = (student *)malloc(sizeof(student));
char* name = (char *)malloc(sizeof(char));
int age=0;
// inputs for name and age
// do-while(option != EXIT_VALUE);
// inside do-while are the following below
add(&list, name, age);
view(&list);
}
I only get the newest student upon the view method.
It makes sense, since you are allocation space for 1 single student structure:
student* list = (student *)malloc(sizeof(student));
You should do something like:
int list_size = 20;
student* list = (student *)malloc(sizeof(student) * list_size);
The name variable suffers from the same problem.
A dynamic linked list should have a reference to the next and previous elements. You'll have to change your program to work with:
struct student {
char *name;
int age;
struct student* next;
struct student* previous;
}
Also you are doing *(data++) which isn't necessary. data++ is just fine. You really shouldn't be needing the double pointers everywhere, it only complicates things. For allocation, fine (if you think that's the best way), but for passing to other functions that only READ the pointer, there's no need.
view expects list to be an array (null-terminated) of pointers to student. However, you allocate it in main as a pointer to a single student. Then, you just reassign that student pointer every time you call add. As Chris said, it would probably be fine (and simpler) just to have a list of students.
If you want to implement the list as an array, you need a strategy to re-allocate the list when it gets larger than the initial array size. A typical strategy is to choose an arbitrary size that will handle most cases without wasting tons of memory, and then double that amount when the bound is reached. So, say you start with an 8-element list. When you add the 9th element, it would reallocate the array to a 16-element list.
The other strategy is to use a linked list, where you add a struct pointer (typically named "next") to your structure and use that to iterate through your list. This makes allocation and traversal a lot simpler, although list retrieval becomes an O(n) operation instead of an O(1) operation, which means it takes longer to get a particular element from the list as the list gets larger.
You have two options.
Create linked list and you will be able to insert as many students you want. (each student has also pointer on next student if any else it is NULL)
Create array of pointers on students as suggested by karlphillip, but method to add new student will need to implement position searching or storing this postion. (you have to figure out where should you store pointer that points on student)
I only get the newest student upon the view method.
void add(student **list, char* name, int age) {
student* temp = (student *)malloc(sizeof(student));
temp->name = name
temp->age = age;
*list = temp;
*(list++) = (student *)malloc(sizeof(student));
}
Notice that you are making list point to the newly allocated memory location. The previous value it was pointing to is lost and also causes memory leak. And so only you are getting the last entry. You need to implement a linked list like structure.
struct student {
char *name;
int age;
struct student *next ;
}
// ....
void add(student **list, char* name, int age) {
// Make sure that every new location is saved in next
// And also the next time when you call this method, it should be
// location of "next" pointing to being passed as parameter.
}
int main()
{
student *list = malloc(sizeof(student)); // No need to type cast malloc
student *preserveHeadNode = list ;
add(&list, name, age);
// .. While viewing pass the "preserveHeadNode" and run the loop until
// student::next == NULL
}

Resources