Saving and loading a linked list into a binary file (C)

I am trying to write and read a linked list from a binary file in C. My goal is to save and load the resident data for the nursing home (in fact, I am a nurse) in order to classify each resident through resource utilization groups. I already did this for a fixed number of inhabitants (32, that is, the potential of the object) using an array of structures, but now I need to do this for a variable set of inhabitants to do a statistical study.It is obvious that for this first attempt I simplified the structure to a minimum, since the actual structure contains 109 data.

I am very close to a solution, but something is not working, i.e. each element of the saved list alternates with void one.This code should read the list from a binary file, display it on the terminal, add a new element, save the list. Of course, each procedure must be placed in a function.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ncurses.h> struct pat { char surn [16]; char name [16]; struct pat *next; }; static FILE *h; static struct pat *osp; static struct pat *first; struct pat *make_structure (void); int main() { initscr(); raw(); keypad (stdscr, TRUE); noecho(); clear(); osp=make_structure(); first=osp; h=fopen ("archivio","r"); if (h==NULL) printw ("Archivio inesistente\n"); else { while (!feof(h)) { printw ("Lungh. nome = %d\n",sizeof osp->name); fread (osp->surn,sizeof osp->surn,1,h); fread (osp->name,sizeof osp->name,1,h); printw ("Cognome: %s\tNome: %s\n",osp->surn,osp->name); osp->next=make_structure(); osp=osp->next; } } getch(); echo(); printw ("Surname: "); scanw ("%s",osp->surn); printw ("\nName: "); scanw ("%s",osp->name); noecho(); osp=first; h=fopen ("archivio","w"); while (osp != NULL) { fwrite (osp->surn,sizeof osp->surn,1,h); fwrite (osp->name,sizeof osp->name,1,h); osp=osp->next; } return 0; } struct pat *make_structure(void) { struct pat *a; a = (struct pat *)malloc(sizeof(struct pat)); return (a); } 
+7
c linked-list
source share
1 answer

You were so close, I’m not even sure what the true reason for the failure was, because for the first cut I just applied [most] the corrections suggested by others and got a working program.

Although this worked, I found that the way you made the make_structure call forward was less flexible when you extend the program to do other things.

For example, you will have a hanging record of a “ghost”, if instead of adding a new record you decide not to add a new record, but to make some statistics or manipulate existing ones.

So, I created the second version of the program, which has more isolation and commonality.


Here is the minimally modified version [please forgive the free stylish cleaning]:

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ncurses.h> struct pat { char surn[16]; char name[16]; struct pat *next; }; static FILE *h; static struct pat *osp; static struct pat *first; struct pat *make_structure(void); int main() { int rlen; initscr(); raw(); keypad(stdscr, TRUE); noecho(); clear(); osp = make_structure(); first = osp; h = fopen("archivio", "r"); if (h == NULL) printw("Archivio inesistente\n"); else { while (1) { printw("Lungh. nome = %d\n", sizeof osp->name); // leave early on EOF or badly formed entry rlen = fread(osp->surn, sizeof osp->surn, 1, h); if (rlen != 1) break; // leave early on EOF or badly formed entry fread(osp->name, sizeof osp->name, 1, h); if (rlen != 1) break; printw("Cognome: %s\tNome: %s\n", osp->surn, osp->name); osp->next = make_structure(); osp = osp->next; } fclose(h); } // NOTE: this just chews the first character (debug, I suppose?) #if 0 getch(); #endif // add new element echo(); printw("Surname: "); scanw("%15s", osp->surn); printw("Name: "); scanw("%15s", osp->name); noecho(); h = fopen("archivio", "w"); osp = first; while (osp != NULL) { fwrite(osp->surn, sizeof osp->surn, 1, h); fwrite(osp->name, sizeof osp->name, 1, h); osp = osp->next; } fclose(h); return 0; } struct pat * make_structure(void) { struct pat *a; a = malloc(sizeof(struct pat)); // NOTE: do this for good measure a->next = NULL; return (a); } 

Here is a more generalized version that can give you some ideas when expanding the program:

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ncurses.h> struct pat { char surn[16]; char name[16]; struct pat *next; }; static FILE *h; static struct pat *osp; static struct pat *first; static struct pat *prev; void read_archive(const char *file); void add_new_elements(void); void write_archive(const char *file); struct pat *make_structure(void); void add_to_list(struct pat *pat); int main() { initscr(); raw(); keypad(stdscr, TRUE); noecho(); clear(); read_archive("archivio"); // NOTE: this just chews the first character (debug, I suppose?) #if 0 getch(); #endif // NOTE: instead of just automatically adding new elements, this might // be replaced with a menu, such as: // Enter Operation: // (1) Add new names // (2) Calculate statistics // (3) Backup database add_new_elements(); write_archive("archivio"); return 0; } // read_archive -- read in archive void read_archive(const char *file) { int rlen; h = fopen(file, "r"); if (h == NULL) printw("Archivio inesistente\n"); else { while (1) { osp = make_structure(); // leave early on EOF or badly formed entry rlen = fread(osp->surn, sizeof osp->surn, 1, h); if (rlen != 1) break; // leave early on EOF or badly formed entry fread(osp->name, sizeof osp->name, 1, h); if (rlen != 1) break; printw("Cognome: %s\tNome: %s\n", osp->surn, osp->name); add_to_list(osp); } // NOTE: this is _always_ for EOF or bad entry, so free it free(osp); fclose(h); } } // add_new_elements -- prompt for new elements void add_new_elements(void) { echo(); while (1) { osp = make_structure(); printw("Surname: "); osp->surn[0] = 0; scanw("%15s", osp->surn); if (osp->surn[0] == 0) break; printw("Name: "); osp->name[0] = 0; scanw("%15s", osp->name); if (osp->name[0] == 0) break; add_to_list(osp); } noecho(); free(osp); } // write_archive -- write out archive void write_archive(const char *file) { h = fopen(file, "w"); for (osp = first; osp != NULL; osp = osp->next) { fwrite(osp->surn, sizeof osp->surn, 1, h); fwrite(osp->name, sizeof osp->name, 1, h); } fclose(h); } struct pat * make_structure(void) { struct pat *a; a = malloc(sizeof(struct pat)); // NOTE: do this for good measure a->next = NULL; return (a); } void add_to_list(struct pat *pat) { if (first == NULL) first = pat; else prev->next = pat; prev = pat; } 

UPDATE:

I was still trying to find out the cause of my failure.

I did not debug / run your source code because I thought that the logic of the linked list would be needed to fix it, and I wanted to do it quickly. However, after I looked through it, the logic was beautiful. Based on my best analysis of the assumptions, the probable failure was feof , which I had already changed to check length on fread .

Of course, I was going to better organize the program using functions

I assumed you would do it. Separation in the second program was more of a learning tool to clarify and illustrate the principle and not be a critique of modularity.

In the source code you had to add a new entry because osp was empty, but was already linked in the list. Wrong, zombie record if you do.

That is, the list had an entry linked before it was filled out and confirmed. In other words, after a reading cycle, but before the user is prompted to enter a new record, the list can be considered distorted (ie, [Small] violation of the principles of "contract programming" or "design by contract").

The separation of functions in the second program was simply to emphasize this. In particular, by moving the reading cycle into a separate function, he illustrates / applies the construction of the contract.

That is, upon entry, the list is intact and well-formed [albeit empty]. Upon return, it is either empty [if the input file does not exist] or it has only well-formed / complete entries in it.

In the second program, partial / incorrect recording is never connected. add_to_list always executed last [only for the whole / full record].

So, for both read_archive and add_new_entries , when they call, they are assigned a complete / complete list with only valid fully-formed entries. This is a “contract” for them.

And to fulfill their part of the “contract”, these functions must leave things the same, preserving the integrity of the list upon exit. This is a "contract" of functions for the outside world.


UPDATE # 2:

Excuse me for OT, but can you offer me a good IDE for C - C ++ that works well with Debian / GNU Linux?

I cannot be the best person to advise you this, because I do not use it. I wrote C long before they existed, so I developed my own toolkit, which is much more powerful than any IDE I've seen. Also, when I looked at them, I could never find a way to cross them.

I am at home with Code :: Blocks, but, unfortunately, the so-called nightly assembly of buggies and crashes are very common

If you are at home with code blocks, but building the buggy at night, perhaps the easiest solution is to switch your update to a "stable" tree, if possible. This may be the best "short answer".

(the code completion utility is very useful, but I do not have to type str ..., otherwise it freezes) and it is very frustrating!

Perhaps you can check the error database and see if there are problems that you encountered, known error messages. If not, you can / should write it down.


I installed codeblocks . It looks clean and simple. I also installed eclipse and looked at kdevelop . Eclipse receives high marks from several web pages, and netbeans closes the second

I tried using them in the source file that I was lying around, which was created using the Makefile. the code blocks were intuitive enough for me to run it quickly. I had problems with others. eclipse was originally developed by IBM for internal use and then released as a public service. It is well maintained and mature.

I started eclipse without a CDT, but as soon as I added that eclipse gets my vote because it seems to have enough capabilities to manage everything that I am going to omit below; -)

An IDE is a somewhat personal choice [if your company does not name it], so you should use what you like. In other words, try some and see what features they have and how they work. Here is the list page: https://en.wikipedia.org/wiki/Comparison_of_integrated_development_environments

When choosing an IDE, you should look at the "most used" features. Most often you do this scrolling in the source file. Therefore, the editor must support hjkl aliases hjkl arrow keys, for example vi . Moving the right hand to the arrow and back keys slows down so much that it is not a starter.

eclipse uses gvim [graphical vim ], so a plus.

I am not a fan of editing with the simple WYSIWYG editor panel, which has only rude search / replace functions. Similarly, vim allows regular expression searches by simply typing / , so again the most common “at your fingertips” operations

I do not use [or do not want] the autocomplete function. When I tried them, they often make mistakes, and it takes more time to backtrack on what they did. I am a very fast typist.

I will also turn off syntax highlighting and source coloring. When you enter the source code, the colors change with approximately every character entered because the editor thinks I'm typing (for example, I type a comment, but he thinks it's code, etc.). I found this distracting.

Also, looking at the final result, I found that the colored result is “too busy” (ie more information that I need to filter), and not something that helps me see what I need to see.

I'm pretty persistent about padding, breaking long blocks of code with blank lines to improve readability. And of course, good comments. For me it is much more important than coloring. I have a special tool for indentation [As you remember, when I posted the code above, it was reused again because I ran it through my tool before publishing.

Another feature is the graphical debugger. Is it fully functional? For example, ddd is a graphical wrapper around [very powerful] gdb . ddd provides a graphical shell and windows for common things, but it still allows a direct text window for the gdb prompt, so you can manually enter more complex commands (like watch symbol ).

Is the IDE extensible? Can you add plugins? Can you easily add / create your own?

Which source control system uses the IDE? I used it for many years and now it is completely sold on git . So, if the IDE does not support git , this is not a starter.

It should be noted that git has so many functions that they cannot be contained in the graphical interface. So, really powerful stuff uses the command line tool in the terminal window.

My IDE? Multiple xterm windows, vi , git and my toolkit [which currently makes up 250,000 lines of perl scripts ;-)] Is the IDE forcing you to do your best? How easy is it to import / export configuration, etc. To other external tools and IDEs?

I have a very powerful build script of my own design. Thus, I would like the IDE when I click the "Create" button to not do what I normally did, but give control to my build script. Similarly for any other operation that the IDE can offer.

Is the IDE portable and affordable on all major platforms: Linux, OSX, and Windows? In the past, this was another reason I avoided the IDE. They will be available on only one platform. Or, since I was engaged in consulting work, I ended up in an environment that would not allow the IDE to be installed / used due to the policy [sysadmin].

+2
source share

All Articles