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);
Related
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.
I have a function that adds one item to a list that I created. If it's the first time and the list points to NULL, it allocates the list and completes it, returning the address. If it's not the first time, it adds another item and again returns the first item (by now I could disregard this return). The list and the function WORKS fine, here is the prototype:
typedef struct structTAppConsoleList {
char *text;
void (*cbFunction)(int);
int number; // This is the item number
struct structTAppConsoleList *previous;
struct structTAppConsoleList *next;
} TList;
TList *AppConsoleListAddItem(TList *p_list, const char *p_string, void (*p_funcPtr)(int));
So, somewhere in my code I have to create a lot of them and I'm trying to make it as the code below shows. Thing is, I can't make it work... I want to create something to group the lists I want to create and then use it in the function. The code below is an idea of what I'm trying to do. Consider only the part where I try to allocate the 3 lists, the rest is not important for this example.
TList *list1;
TList *list2;
TList *list3;
int main(void)
{
int i,j;
TList **groupMyLists;
TList *temp;
groupMyLists=malloc(sizeof(TList)*3);
*groupMyLists =(TList*)&list1;
*(groupMyLists+1)=(TList*)&list2;
*(groupMyLists+2)=(TList*)&list3;
for(j=0;j<3;j++) {
temp=NULL;
for(i=0;i<10;i++) {
temp=AppConsoleListAddItem(temp,"some text",someFunc);
}
**groupMyLists=temp; // my make won't let me do this
groupMyLists++;
}
}
I'm pretty sure that this would do it, but I can't compile it.
In my head, (*groupMyLists) would be the same as (&list1), (&list2), (&list3), the same way that (**groupMyLists) would be the same as (list1), (list2) and (list3). So why I can't do (**groupMyLists=temp)? Anyone?
I hope I made myself clear!! I's not easy to explain this madness I'm trying to do...
Change this line, you are using the wrong indirection.
*groupMyLists=temp;
In addition to the above two answers about the incorrect indirection of **groupMyLists you probably also want to assign the list1,list2,list3 pointers correct pointer values instead of writing garbage values into the allocated memory in groupMyLists i.e.
TList * groupMyList = malloc(sizeof(TList)*3);
list1 = &groupMyList[0];
list2 = &groupMyList[1];
list3 = &groupMyList[2];
but, this does not really match the rest of your code as it seems that AppConsoleAddListItem allocates the temp list so in that case your malloc would be incorrect as it should allocate the space for the pointers instead of space for the lists as in:
TList ** groupMyList = (TList **)malloc(sizeof(TList *)*3);
TList * temp;
if (!groupMyList) {
/* Print allocation error warning or handle in some proper fashion */
exit(1);
}
for(j=0;j<3;j++) {
temp=NULL;
for(i=0;i<10;i++) {
temp=AppConsoleListAddItem(temp,"some text",someFunc);
}
groupMyLists[j]=temp; // Here you now assign the pointer in temp to the memory for // pointers that you allocated above
}
list1 = groupMyList[0]; // Here we need to assign the list1,2,3 after the AppConsole calls
list2 = groupMyList[1]; // as these calls will changes the pointer addresses written into
list3 = groupMyList[2]; // groupMyList
Although I can not be sure exactly what you are trying to do there are several inconsistencies of pointers and indirections in your original code and the above two examples can hopefully be of some guidance
This would do the job:
**groupMyLists = *temp;
of copying one struct referenced by temp to another struct referenced by *groupMyLists.
But only if *groupMyLists would reference any valid memory, which is does not do - at least not from the source you posted.
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.
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).
I'm having problem with this small program:
UPDATED (As per some requests, I've included everything here so to make clear what I'm doing. Sorry for it being too long):
Student.h file:
typedef struct Student {
char *name;
int age;
char *major;
char *toString;
} *Student;
extern Student newStudent(char *name, int age, char *major);
Student.c file:
c
har *studentToString(Student s);
static void error(char *s) {
fprintf(stderr,"%s:%d %s\n",__FILE__,__LINE__,s);
exit(1);
}
extern Student newStudent(char *name, int age, char *major) {
Student s;
if (!(s=(Student)malloc(sizeof(*s)))){
error("out of memory");
}
s->name=name;
s->age=age;
s->major=major;
s->toString = studentToString(s);
return s;
}
char *studentToString(Student s) {
const int size=3;
char age[size+1];
snprintf(age,size,"%d",s->age);
char *line=newString();
line=catString(line,"<");
line=catString(line,s->name);
line=catString(line," ");
line=catString(line,age);
line=catString(line," ");
line=catString(line,s->major);
line=catString(line,">");
return line;
}
Students.c file:
static void error(char *s) {
fprintf(stderr,"%s:%d %s\n",__FILE__,__LINE__,s);
exit(1);
}
static StudentList alloc(StudentList students, Student student) {
StudentList p;
if (!(p=(StudentList)malloc(sizeof(*p)))){
error("out of memory");}
p->student=student;
p->students=students;
return p;
}
extern Students newStudents() {
Students p;
if (!(p=(Students)malloc(sizeof(*p)))){
error("out of memory");
}
p->cursor=0;
p->students=0;
return p;
}
extern void addStudent(Students students, Student student) {
StudentList p=students->students;
if (!p) {
students->students=alloc(0,student);
return;
}
while (p->students)
p=p->students;
p->students=alloc(0,student);
}
extern void initStudent(Students students) {
students->cursor=students->students;
}
extern Student currStudent(Students students) {
StudentList cursor=students->cursor;
if (!cursor)
return 0;
return cursor->student;
}
extern void nextStudent(Students students) {
students->cursor=students->cursor->students;
}
And my main method:
int main() {
Students students=newStudents();
addStudent(students,newStudent("Julie",22,"CS"));
addStudent(students,newStudent("Trevor",32,"EE"));
for (initStudent(students);
currStudent(students);
nextStudent(students)) {
char *line=currStudent(students)->toString;
printf("%s\n",line);
free(currStudent(students));
free(line);
}
free(students->students);
free(students);
return 0;
}
I'm using valgrind to check memory leaks, and it is popping following error:
8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==9520== at 0x40054E5: malloc (vg_replace_malloc.c:149)
==9520== by 0x8048908: alloc (Students.c:13)
==9520== by 0x80489EB: addStudent (Students.c:42)
==9520== by 0x804882E: main (StudentList.c:10)
I understand that I need to free the memory allocated for p in alloc function, but where should I call free(p)? Or is there something else I'm doing wrong? Please help!
The question is what do you do when you are finished with a Student or a StudentList and don't need it any more. That's the point where you should call free() for all the allocated things in that structure.
Probably you would want a freeStudents function that walks through a list of students and frees all the Students and all the StudentList items in it. Then you call that function whenever you want to get rid of a list of students.
Sorry, this is tangential, but you could really do a lot to make your code more readable.
You make a struct and then redefine its type to be a pointer to it. Yikes, that's just asking for trouble when it comes to maintainability. It's usually a bad idea to hide the fact that pointers are pointers, because when people see something like this:
Students newStudents()
{
Students p;
// ...
return p;
}
convention forces us to assume that you're returning a struct that was allocated on the stack, which would be obviously incorrect. (Edit: Not necessarily "obviously incorrect", but a wasteful copy.)
Things get even hairier when you add your malloc...
Students p;
if (!(p=(Students)malloc(sizeof(*p))))
{
error("out of memory");
}
For one thing, as mentioned, people assume that with no *, Students is a full structure on the stack. This will make anyone that sees "sizeof(*p)" do a double take. It's not obvious what you're doing.
And while squishing assignments and comparisons into one if statement is perfectly valid C and C++, it's usually not the most readable solution. Much improved:
Students* newStudents ()
{
Students* p = (Students*) malloc (sizeof (Students));
if (p == NULL)
{
// ...
}
// ...
return p;
}
And people enjoy pointing out that casting the return value of malloc isn't necessary in C, but it is in C++.
As for your leak, well... valgrind didn't report your catString usage, but it's still pretty sketchy since you're hiding the memory usage. Using snprintf is a better, more idiomatic way to create the string you want.
The leak valgrind is reporting: it looks like you're just freeing the first "students" node in your list. You need to traverse it and free them all, probably like this:
Students p = students;
while (p)
{
Students next = p->students;
free (p);
p = next;
}
I think you have a problem in alloc.
If you want a pointer it should be
StudentList *p;
p = (StudentList*)malloc(sizeof(StudentList));
I don't ever use sizeof with a variable if I am using malloc(). You should be using malloc(n * sizeof(StudentList)). With that said...
You've got some major concerns. First of all, you don't tell us what Students and StudentList are specifically defined to be. You pass 0 in some cases when I think you meant NULL -- never use 0 when you mean NULL. And if malloc() fails -- meaning, it returned NULL--then you don't call free() on the result. Never, ever free NULL.
You should only free a block of memory that was (a) successfully allocated, and (b) when you don't need it anymore. That doesn't seem to enter into your code here.
There are other issues as well (you assume students in addStudent is non-NULL when you initialize p), but they don't quite address your question concerning the usage of free().
What is this doing:
if (!(p=(StudentList)malloc(sizeof(*p)))){
free(p);
So, if p can't be allocated, free it?
I am assuming that StudentList is typedefined to be some type of pointer to Student. This being the case, this line in your alloc function is a problem.
p=(StudentList)malloc(sizeof(*p))
You are attempting to dereference the pointer before it has been assigned and that is not good.
You should free the memory when you no longer need to use the objects you've allocated.
What are StudentList and Student? It looks to me like your data structure is constructed improperly. Why is your StudentList storing a pointer to another StudentList? You should probably create a StudentList using malloc and return the pointer to that using your newStudents function, and rather than returning a new StudentList struct with a copy of the old one in addStudent, simply modify the structure you created originally.
So it would go something like this:
StudentList *newStudentList() {
//malloc a new StudentList and return the pointer
}
void freeStudentList(StudentList *list) {
//free the list pointer and all its child resources (i.e. the students in the list)
}
Student *newStudent(const char *name, etc) {
//malloc a new Student and return the pointer
}
void addStudentToList(StudentList *list, Student *student) {
//modify the StudentList which has been passed in by adding the student to it.
}
You could consolidate newStudent into addStudent if you don't plan on using them separately, and you may need a freeStudent and removeStudentFromList function if you plan on doing those things separately from freeStudentList as well. You should look at Google for examples of dynamic data structures in C (here is the first result on Google, but there are many others).
You really need to define what your types are. Could you edit your post to include the definitions of Student, StudentList and Students?
Also,
StudentList p;
p = (StudentList) malloc(sizeof(*p);
Unless StudentList is a typedef for a pointer, I'm not sure how that's compiling.
We also have the line:
StudentList p=students->students
With p being defined as a Students. Then Students must be a typedef for a pointer as well.
Also, what I think is your issue in the end, is that when you try to insert a student in the linked list, you end up losing any existing list. What would be interesting for you to try would be to insert 3 students into the list, and then attempt to print the list.