First of all, you don't have to create two seperate structs to make a linked list ..
You can do it like this:
struct list {
/* struct members */
struct list *next;
}
Just like that ! Also, In functions you should be aware of pointer decay .. In simple terms, it's when you pass a function an array, the function recieves it as a pointer (which is technically different). Best way to deal with this is to receive an array as struct employeeData ** data_array, and a pointer as struct employeeData * data. So there is no meaning in struct employeeData employee[]. Also, this: &*employee is exactly the same as this: employee, except that the second is slightly more efficient, because in the first one you get the address of the variable and then dereference it, essentially doing nothing.
In the structs definitions ..
There's no need to define a struct called node, because that just complicates the program and confuses you. The way linked lists are actually implemented is by adding a member of the same type as the struct its defined in, as I explained eariler.
Here is the improved version:
struct employeeData {
int EMP_ID;
char name[20];
int dept;
int rank;
double salary;
struct employeeData *next;
};
In the initializeList function ..
There's no need for the second argument if you're just going to duplicate it in the function ..
Also, you don't need to derefence the malloc'd pointer before returning it back, because the function caller won't probably be able to deduce that he needs to free it afterwards to prevent a memory leak, unless he uses a tool like valgrind ..
You also don't need to invoke fscanf twice.
I also renamed the function to be: getEmployeeData, because I think that makes more sense.
This is the final form of the improved function:
struct employeeData *getEmployeeData (const char * filename) {
FILE *ifp = fopen(filename, "r");
if ( ifp == NULL )
return NULL;
struct employeeData *employee = malloc(sizeof(struct employeeData));
struct employeeData *temptr = employee;
int num = (int)getNumberOfLines(filename);
for (int line = 1;
(fscanf(ifp, "%d %s %d %d %lf\n", &temptr->EMP_ID, temptr->name, &temptr->dept, &temptr->rank, &temptr->salary) == 5)
&& line < num; ++line) {
temptr->next = malloc(sizeof(struct employeeData));
temptr = temptr->next;
}
fclose(ifp); /* fopen uses malloc internally */
return employee;
}
You might notice that (unlike your version of the function) I do this: temptr->next = malloc(sizeof(struct employeeData)). This is most certainly the reason why your program crashed, it's because you only malloc the first element of the node and try to use fscanf on struct members that aren't even allocated in memory. And that's why you have to allocate it before you use it. Remember, each element in a node is (mostly) independent from the other, even in memory allocation.
This function is simpler and much more efficient. You might also notice that I call another function called getNumberOfLines that gets the number of lines in a file.
Here it is:
size_t getNumberOfLines(const char * filename) {
FILE *stream = fopen(filename, "r");
if ( stream == NULL )
return EOF;
size_t lines = 0;
char c;
while (( c = getc(stream)) != EOF )
if ( c == '\n' )
lines++;
fclose(stream);
return lines;
}
The reason I did this, is because if fscanf doesn't find the formatted text to store in the variables, it simply stores "0", as a float, an int, a char or even a string.
So, if fscanf scans an empty line, it simply stores 0 in all of the variables ..
To prevent that, you have to make sure that fscanf scans only occupied lines, even if they aren't correctly formatted, because the other condition that checks if fscanf returned 5 (the number of variables required to store in) will not be true if the line isn't correctly formatted but will however return true if the line isn't even occupied ( This is what I experienced with the gcc implementation, if you don't need that, remove it ).
The print function
void print (struct employeeData *employee){
for( struct employeeData *temptr = employee; ( temptr != NULL ); temptr = temptr->next )
printf("%d %s %d %d %lf\n", temptr->EMP_ID, temptr->name, temptr->dept, temptr->rank, temptr->salary);
}
I think I explained all the ideas applied here. Let's move on ..
Memory freeing
We need to free dynamic memory to prevent memory leaks, and when you're trying to free a linked list that becomes even trickier! If you try to free them sequentially that most certainly won't work, unless some universal coincidence happens at the time your program was running! The reason for that is simply really .. It's because the only way you can link to the next list is through a member of the struct at hand. Obviously, if you delete all your struct's members from memory, then you have no clue where to look for the next list! One solution to this is through recursion, like this:
void releaseData(struct employeeData *data) {
/* freeing nodes in reverse because nodes are connected in memory, if you free the first, you lose grasp on the second and the third, resulting in a memory leak .. */
if (data->next)
releaseData2(data->next);
free(data);
}
But I don't prefer this method, because it triggers allocating memory for functions and their arguments then deallocating them, and also keeping track of the calling function, and there's actually a limit to that depending entirely on the operating system and the running kernal to determine. So as you can see, this method is mostly avoidable and is only used when there's no other way, and that's why I've created this one:
void releaseData(struct employeeData *data) {
/* freeing nodes in reverse because nodes are connected in memory, if you free the first, you lose grasp on the second and the third, resulting in a memory leak .. */
struct employeeData * temptr = data;
int num = 0, first = 1;
while ( temptr != NULL ) {
if ( temptr->next != NULL ) {
if (first) {
while ( temptr->next != NULL ) {
temptr = temptr->next;
num++;
}
first = 0;
} else {
for(int i = 0; i < num - 1; ++i)
temptr = temptr->next;
num--;
}
}
free(temptr);
temptr = (num == 0) ? NULL : data;
}
/* We could have used recursion, but that come with unnecessary overhead and less memory efficiency */
}
As you can see, this one is much more complex but it is also much more effiecient.
We use two variables to keep track of the loop: num and first.
num is used to count how many nested nodes are there to go through, because when I free a pointer, it most certainly is not NULL so the loop would be infinite because it simply checks for a value in there ..
first is used to indicate if it is the first time to run the loop, because if it was, we most certainly wouldn't know how many nodes are in there.
I think the rest of the function is self-explanatory so I'd leave it to you to figure it out.
The main function
int main () {
/* Never dereference a malloc'd pointer before freeing it, so we'll get the pointer returned from the function as is */
struct employeeData *employee = getEmployeeData("empInfo.txt"); /* Getting employee data in the struct */
print(employee); /* Printing them out */
releaseData(employee); /* Freeing Dynamic Memory */
return 0;
}
That's it.
printf()with a newline. You might even need to add afflush(stdout);too. Otherwise, output is not guaranteed to appear in a timely fashion. (Even with newlines, if the output is piped to a command such asless, the newlines may not force the data out to the pipe.)