Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

81%
+7 −0
Q&A Problems with data structures and filestreams.

Notice You'll need to backpedal some! Read the answer by @r~~ above, but I expect you need some hints. This reads as either a selfstudy or schooling exercise task, so I'll stick to review + hints...

posted 3y ago by GerHobbelt‭  ·  edited 3y ago by GerHobbelt‭

Answer
#4: Post edited by user avatar GerHobbelt‭ · 2021-12-18T02:38:29Z (about 3 years ago)
const correctness in example code so it will compile as-is
  • # Notice
  • You'll need to backpedal some! Read the answer by @r~~ above, but I expect you need some hints.
  • This reads as either a selfstudy or schooling exercise task, so I'll stick to review + hints and leave you some discovery of your own; I know lots of peeps have trouble grokking *pointers* and the code shows it, so we'll focus on those. Then there's *organization*, i.e. a mental picture of what you're doing and how stuff is organized on disk and in memory (RAM) and how we might go about editing that organization.
  • ----
  • # Analysis of `struct book` and commentary
  • ```
  • struct book{
  • char *title;
  • char *author;
  • char *subject;
  • };
  • ```
  • This tells us a `book` type consists of 3 *pointers* (the `*` in each `char *` type is a clear mark of that).
  • Pointers *point* somewhere ('referencing') and generally take up separate space themselves. `char *` is often also referred as a *string* but that's just what it's used for *most often*: a *string* in C is a sequence of `char` characters (terminated by a NUL character a.k.a. NUL sentinel to mark the end of the string).
  • Here those 3 pointers imply a `struct book` only carries the 3 *references*, while the actual strings are stored elsewhere. (Usually, those are 'allocated on the heap' if you want to keep them alive for a while; I don't know if you've already exercised with `malloc()` and `free()` (and `calloc()` might be handy!) -- if you haven't, do that first in a separate exercise.
  • ## Pointers (and a sub-par analogy towards understanding them)
  • Pointers are references, a bit like phone numbers: when you've got a phone number, you can call that *phone* (and thus reach somebody), but it's definitely *not* the phone itself. As with phone numbers, which you need to write down or otherwise memorize in order not to loose them (or you won't be able to call that person ever again), *pointers* need a little space of their own to be remembered: a pointer variable. `struct book` has 3 of those, so clearly it intends to remember where 3 `char` (character) type values are stored.
  • The slightly wicked bit in C, which I've found throws off a lot of folks learning the language, is that C can do 'pointer arithmetic': that's a bit like adding or subtracting 1 (or more) from a phone number, so you get the next one. Here the analogy goes horribly bad as that won't be the phone number of their neighbour, but in C and computers, it generally is.
  • ### Very Important Nitpick
  • Hence we usually read `char *` as *pointer to a string* while **technically** it's more properly interpreted as (*almost* always!) a pointer to the *first character (`char`!) of that string value*.
  • > I find that many textbooks and educators gloss over this *precise reading of type declarations*, causing a lot of confusion about pointers at the very start.
  • >
  • > `*` must be read as "*points at \<what came before in the type declaration>*" thus `char *` reads as:
  • > - `char`: "(space for) 1 character".
  • > - `*`: "points at..." *what*? `char`! Hence: "*points at a **single *character***".
  • > We *may* call this particular type a 'string' colloquially, but that's only true in *most* circumstances, because C can do *pointer arithmetic* and thus reach the *next* character, and the one after that, and so on, when it *only has a pointer at the first one*!
  • >
  • > ```
  • > char *p = "Hello world
  • ";
  • > while (*p) {
  • > printf("%c", *p);
  • > p++;
  • > }
  • > ```
  • > `p` now will point at the NUL sentinel `char` at the end of that `"Hello world\n"` string. You have 'walked the pointer down the array of characters that is a string'.
  • >
  • > > BTW: the `while (*p)` loop terminates because `*p` is read and when it has become NUL (0), this evaluates to `while(0)` hence `while(false)` and thus the loop is stopped. OTOH, `while(*p)` at first evaluates to `while('H')`: `'H'` is a non-zero `char` value and thus in the `while` evaluates to `while(true)`. I expect this to be obvious already, but your question and code strongly indicate a missing comprehension of pointers at large, so I address these here to make sure you can pick up from a place where you're probably already comfortable.
  • >
  • > You can also use indexing, which is just another way of using pointers, without *walking* them:
  • > ```
  • > char *p = "Hello world
  • ";
  • > int i = 0;
  • > while (p[i]) {
  • > printf("%c", p[i]);
  • > i++;
  • > }
  • > ```
  • > `i` will now be 12 and `p[i]` will be NUL(0). Meanwhile, `p` has not been changed and can thus be re-used to access that "Hello world" string again if we feel like it.
  • >
  • > Pointer arithmetic also means that this next code is *identical* to the previous one:
  • >
  • > ```
  • > char *p = "Hello world
  • ";
  • > int i = 0;
  • > while (*(p + i)) {
  • > printf("%c", *(p + i));
  • > i++;
  • > }
  • > ```
  • > `p + i` reads as: "*add an offset to the pointer `p`; the offset is `i` times the size of a single element of type `*p`* (in this case `char`)" --> the *effective pointer* resulting from this pointer arithmetic will point at the `i`-th `char` in the string pointed at by `p`. And anywhere I use `string`, you can also use the word 'array'.
  • ## `fread` analysis
  • This means that your `fread(&thislib->collection, ...)` might *compile* but is reading like an oddity, because to a programmer it reads as: `thislib->collection` is of type `struct book`, so this `fread()` is told to load a `struct book` instance (and I **ignore** your `sizeof(struct library),1,` bit there on purpose now, to keep things simple) and thus is supposed to load three **pointers** from disk! That, to a programmer, feels like nonsense (or somebody is being horribly clever and knows *exactly* which machine this is going to run on and how -- somebody like Ken Thompson (look him up) might be able to pull this off, but for all us mere mortals this yells "bug!!1!"
  • Why? Because, unlike those phone numbers from the analogy, pointers (*pointer values*) are ephemeral: they are expected to be different on every run of your application, thus storing and loading *those* from disk will lead to disaster: the data we want isn't there this time around, but stored someplace else in RAM.
  • ### Organization: what is stored where?
  • So we come at your first real hurdle, which is encoded as part of the task list in @f~~ answer: you need to sit down and come up with some *organization*: **how are those 3 strings that make a `book` entry stored on disk?**
  • One of **many** answers to that is to store all 3 strings consecutively and decree that your 'library' (series of `book`s) can then be read as reading the entire series of strings from front to back: (1,2,3),(4,5,6),... -- which is relatively easy, but has the significant drawback that you cannot jump to a book in the middle as those titles, authors and subject descriptions will have varying lengths.
  • Plus you won't be able to detect any tiny mistake the program might have made the previous time it ran: if a string is missing or corrupted (e.g. you haven't delimited/terminated it properly last time you wrote the file) you won't be able to tell as each of the strings you encounter along the way can become a tile, author or subject: you won't know where the book *record boundary* is in that library file.
  • ### More food for thought: using *text* files
  • Since you mention a `.txt` file as the library file, an obvious organizational choice might be to store each book as three lines of text, thus using `'\n'` (NewLine) as a string delimiter on disk. Of course, it is then strictly prohibited to have it anywhere in the `subject`, `title` or `author` strings or you'ld have a problem.
  • To delineate a record, one could add an extra line then as part of each record, e.g. `BOOK RECORD`, so your application could then check that each book record starts with such a line or report an error if it doesn't. The possibilities here abound. (Of course, this assumes we're not worried about, say, the `title` of the book reading `"BOOK RECORD"` itself: think about it and see if you can visualize the file organization in your mind (or on paper) and observe that this would not pose a real problem. Only, possibly, a very minor one when the library file has been damaged ('corrupted') on disk.
  • Given the `.txt` file you mention and my mentioning of `\n` as a delimiter, this doesn't sit well with C, which expects its strings to be NUL terminated. Meanwhile, the human convention is to *not* write NUL characters to `.txt` files, so we can open them without hassle in editors like Notepad or vi.
  • So loading the library from file into memory is a bit of a challenge then. Of course, one can use `fread()` for this (a apply a few bits of cleverness afterwards), but `fread()` and `fwrite()` are generally reserved for use with 'binary file formats'; at least that's what a programmer reading thee code would initially expect. Check `fscanf()`, `fgets()` and friends: that's a hint.
  • Which still leaves the `struct book` layout: once you have those title, author and subject strings read into memory using *any* fstream approach, you still need to make sure those strings have a proper 'lifetime' and are pointed to by the pointers in your `struct book` instance variable before you can add it to your library in memory (RAM).
  • > May I suggest you write a small program attempting to add a few books in memory to your library first? You're expected to use `malloc()` and `free()` as you'll be needing those for a lot of this stuff: anything that needs to survive beyond the function call where it was created is usually allocated on the heap, i.e. storage space is requested for these strings using `malloc()` or `calloc()`.
  • >
  • > I expect this will already be tough enough to get right initially, before you add file I/O to the mix.
  • ---
  • # Analysis of `struct library` and commentary
  • Assuming you have been able to write a few (small) programs to create a single book structure in memory and load or store one to disk, we have a look at your `struct library` because there's something quite odd with it (I guess it's a typo):
  • ```
  • struct library {
  • struct book collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • reads to a human programmer as: this `struct library` apparently is **intended** to contain a *collection of books* (`num_books` entries in total) and apparently also points at the *next* library, so we're looking at the basics for a chain of libraries? Would that be a *linked list* of libraries?
  • But the very odd part is this: when `struct library` would contain (or *reference*?) a *collection of books*, wouldn't we need something similar to that 'trick' C uses to reference *strings*, i.e. *reference the initial element using a pointer* and then use *pointer arithmetic* to get at the subsequent entries? But `struct library` is specified as having a
  • ```
  • struct book collection;
  • ```
  • which reads as: contains one *instance* of a `struct book`! Of course I can use C to get a pointer to that internal instance too, if I want (which is what `&` is good for, as in your `&thislib->collection`, but I (and the machine) won't know where the other `num_books-1` book entries are stored!
  • How about this?
  • ```
  • struct book *collection;
  • ```
  • That's more like it: when `char *title` is read as *pointer to `char`* and thus can be interpreted as *pointer to **first** `char` **in an array of `char`s** (a.k.a. a string)*, then the same would be valid for `struct book *collection;`: *a pointer to thee **first** `struct book` instance in an array of `struct book` instances*.
  • With C strings, we have that NUL sentinel to mark the end, rather than keeping a string length as a value somewhere (check out `strlen()` if you haven't already). Here we have a length: `num_books`, so you're supposed to be using that to keep track of the total number of `struct book` records in this particular library instance.
  • Hence I expected the `struct library` definition to look like this:
  • ```
  • struct library {
  • struct book *collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • because then it would read as something sensible to use as a part of memory *organization* for your library. (And apparently the exercise is hinting there may be more libraries loaded into memory at some time due to the `next` pointer.)
  • ------
  • ## Memory organization once again
  • Again, we need to think about how that `struct library` will sit in memory and how it will be able to get at the individual `struct book`-referenced strings specifying author, title and subject: we must be able to print those using `printf()` before we can assume we'll be successful comparing any book title against a given title in order to *delete* that book.
  • Also, you then need to reconsider how you remove a book from the array of books in memory. Do you need to adjust anything else? What if you delete *multiple* books, before you write the library back to disk?
  • # Spinning down...
  • Plenty questions left, but time is running out here and I think you're best served with slowly descending onto your original posted problem: write several small application codes for the parts as suggested in @f~~'s answer. If that doesn't go smoothly and swiftly, **backpedal** and try to write a small test program for each of the bits discussed above. When you do that, always first try to write a piece of code that does '1 struct instance', *then* follow up with another copy that does work for 2..N (arbitrary number of items): you'll only encounter your pointer problems (and learn from them) once you do that as well and think through the various ways of doing this. At this stage there many ways to skin the cat, from trivial to smart & convoluted, and each will hopefully add to your understanding of C pointers: they're fundamental to the language (and hiding underneath many constructs in other languages).
  • I hope there's no due date on the delivery of that exercise: there's plenty to learn and when it doesn't immediately \*click\* (which is the way it goes with most folks), *persevere* and keep the "Very Important Nitpick" section in mind: that part can be improved (it would probably fill a session back-to-back), but at least it's a start to prevent some horrible confusions I've met along the way.
  • # Notice
  • You'll need to backpedal some! Read the answer by @r~~ above, but I expect you need some hints.
  • This reads as either a selfstudy or schooling exercise task, so I'll stick to review + hints and leave you some discovery of your own; I know lots of peeps have trouble grokking *pointers* and the code shows it, so we'll focus on those. Then there's *organization*, i.e. a mental picture of what you're doing and how stuff is organized on disk and in memory (RAM) and how we might go about editing that organization.
  • ----
  • # Analysis of `struct book` and commentary
  • ```
  • struct book{
  • char *title;
  • char *author;
  • char *subject;
  • };
  • ```
  • This tells us a `book` type consists of 3 *pointers* (the `*` in each `char *` type is a clear mark of that).
  • Pointers *point* somewhere ('referencing') and generally take up separate space themselves. `char *` is often also referred as a *string* but that's just what it's used for *most often*: a *string* in C is a sequence of `char` characters (terminated by a NUL character a.k.a. NUL sentinel to mark the end of the string).
  • Here those 3 pointers imply a `struct book` only carries the 3 *references*, while the actual strings are stored elsewhere. (Usually, those are 'allocated on the heap' if you want to keep them alive for a while; I don't know if you've already exercised with `malloc()` and `free()` (and `calloc()` might be handy!) -- if you haven't, do that first in a separate exercise.
  • ## Pointers (and a sub-par analogy towards understanding them)
  • Pointers are references, a bit like phone numbers: when you've got a phone number, you can call that *phone* (and thus reach somebody), but it's definitely *not* the phone itself. As with phone numbers, which you need to write down or otherwise memorize in order not to loose them (or you won't be able to call that person ever again), *pointers* need a little space of their own to be remembered: a pointer variable. `struct book` has 3 of those, so clearly it intends to remember where 3 `char` (character) type values are stored.
  • The slightly wicked bit in C, which I've found throws off a lot of folks learning the language, is that C can do 'pointer arithmetic': that's a bit like adding or subtracting 1 (or more) from a phone number, so you get the next one. Here the analogy goes horribly bad as that won't be the phone number of their neighbour, but in C and computers, it generally is.
  • ### Very Important Nitpick
  • Hence we usually read `char *` as *pointer to a string* while **technically** it's more properly interpreted as (*almost* always!) a pointer to the *first character (`char`!) of that string value*.
  • > I find that many textbooks and educators gloss over this *precise reading of type declarations*, causing a lot of confusion about pointers at the very start.
  • >
  • > `*` must be read as "*points at \<what came before in the type declaration>*" thus `char *` reads as:
  • > - `char`: "(space for) 1 character".
  • > - `*`: "points at..." *what*? `char`! Hence: "*points at a **single *character***".
  • > We *may* call this particular type a 'string' colloquially, but that's only true in *most* circumstances, because C can do *pointer arithmetic* and thus reach the *next* character, and the one after that, and so on, when it *only has a pointer at the first one*!
  • >
  • > ```
  • > const char *p = "Hello world
  • ";
  • > while (*p) {
  • > printf("%c", *p);
  • > p++;
  • > }
  • > ```
  • > `p` now will point at the NUL sentinel `char` at the end of that `"Hello world\n"` string. You have 'walked the pointer down the array of characters that is a string'.
  • >
  • > > BTW: the `while (*p)` loop terminates because `*p` is read and when it has become NUL (0), this evaluates to `while(0)` hence `while(false)` and thus the loop is stopped. OTOH, `while(*p)` at first evaluates to `while('H')`: `'H'` is a non-zero `char` value and thus in the `while` evaluates to `while(true)`. I expect this to be obvious already, but your question and code strongly indicate a missing comprehension of pointers at large, so I address these here to make sure you can pick up from a place where you're probably already comfortable.
  • >
  • > You can also use indexing, which is just another way of using pointers, without *walking* them:
  • > ```
  • > const char *p = "Hello world
  • ";
  • > int i = 0;
  • > while (p[i]) {
  • > printf("%c", p[i]);
  • > i++;
  • > }
  • > ```
  • > `i` will now be 12 and `p[i]` will be NUL(0). Meanwhile, `p` has not been changed and can thus be re-used to access that "Hello world" string again if we feel like it.
  • >
  • > Pointer arithmetic also means that this next code is *identical* to the previous one:
  • >
  • > ```
  • > const char *p = "Hello world
  • ";
  • > int i = 0;
  • > while (*(p + i)) {
  • > printf("%c", *(p + i));
  • > i++;
  • > }
  • > ```
  • > `p + i` reads as: "*add an offset to the pointer `p`; the offset is `i` times the size of a single element of type `*p`* (in this case `char`)" --> the *effective pointer* resulting from this pointer arithmetic will point at the `i`-th `char` in the string pointed at by `p`. And anywhere I use `string`, you can also use the word 'array'.
  • ## `fread` analysis
  • This means that your `fread(&thislib->collection, ...)` might *compile* but is reading like an oddity, because to a programmer it reads as: `thislib->collection` is of type `struct book`, so this `fread()` is told to load a `struct book` instance (and I **ignore** your `sizeof(struct library),1,` bit there on purpose now, to keep things simple) and thus is supposed to load three **pointers** from disk! That, to a programmer, feels like nonsense (or somebody is being horribly clever and knows *exactly* which machine this is going to run on and how -- somebody like Ken Thompson (look him up) might be able to pull this off, but for all us mere mortals this yells "bug!!1!"
  • Why? Because, unlike those phone numbers from the analogy, pointers (*pointer values*) are ephemeral: they are expected to be different on every run of your application, thus storing and loading *those* from disk will lead to disaster: the data we want isn't there this time around, but stored someplace else in RAM.
  • ### Organization: what is stored where?
  • So we come at your first real hurdle, which is encoded as part of the task list in @f~~ answer: you need to sit down and come up with some *organization*: **how are those 3 strings that make a `book` entry stored on disk?**
  • One of **many** answers to that is to store all 3 strings consecutively and decree that your 'library' (series of `book`s) can then be read as reading the entire series of strings from front to back: (1,2,3),(4,5,6),... -- which is relatively easy, but has the significant drawback that you cannot jump to a book in the middle as those titles, authors and subject descriptions will have varying lengths.
  • Plus you won't be able to detect any tiny mistake the program might have made the previous time it ran: if a string is missing or corrupted (e.g. you haven't delimited/terminated it properly last time you wrote the file) you won't be able to tell as each of the strings you encounter along the way can become a tile, author or subject: you won't know where the book *record boundary* is in that library file.
  • ### More food for thought: using *text* files
  • Since you mention a `.txt` file as the library file, an obvious organizational choice might be to store each book as three lines of text, thus using `'\n'` (NewLine) as a string delimiter on disk. Of course, it is then strictly prohibited to have it anywhere in the `subject`, `title` or `author` strings or you'ld have a problem.
  • To delineate a record, one could add an extra line then as part of each record, e.g. `BOOK RECORD`, so your application could then check that each book record starts with such a line or report an error if it doesn't. The possibilities here abound. (Of course, this assumes we're not worried about, say, the `title` of the book reading `"BOOK RECORD"` itself: think about it and see if you can visualize the file organization in your mind (or on paper) and observe that this would not pose a real problem. Only, possibly, a very minor one when the library file has been damaged ('corrupted') on disk.
  • Given the `.txt` file you mention and my mentioning of `\n` as a delimiter, this doesn't sit well with C, which expects its strings to be NUL terminated. Meanwhile, the human convention is to *not* write NUL characters to `.txt` files, so we can open them without hassle in editors like Notepad or vi.
  • So loading the library from file into memory is a bit of a challenge then. Of course, one can use `fread()` for this (a apply a few bits of cleverness afterwards), but `fread()` and `fwrite()` are generally reserved for use with 'binary file formats'; at least that's what a programmer reading thee code would initially expect. Check `fscanf()`, `fgets()` and friends: that's a hint.
  • Which still leaves the `struct book` layout: once you have those title, author and subject strings read into memory using *any* fstream approach, you still need to make sure those strings have a proper 'lifetime' and are pointed to by the pointers in your `struct book` instance variable before you can add it to your library in memory (RAM).
  • > May I suggest you write a small program attempting to add a few books in memory to your library first? You're expected to use `malloc()` and `free()` as you'll be needing those for a lot of this stuff: anything that needs to survive beyond the function call where it was created is usually allocated on the heap, i.e. storage space is requested for these strings using `malloc()` or `calloc()`.
  • >
  • > I expect this will already be tough enough to get right initially, before you add file I/O to the mix.
  • ---
  • # Analysis of `struct library` and commentary
  • Assuming you have been able to write a few (small) programs to create a single book structure in memory and load or store one to disk, we have a look at your `struct library` because there's something quite odd with it (I guess it's a typo):
  • ```
  • struct library {
  • struct book collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • reads to a human programmer as: this `struct library` apparently is **intended** to contain a *collection of books* (`num_books` entries in total) and apparently also points at the *next* library, so we're looking at the basics for a chain of libraries? Would that be a *linked list* of libraries?
  • But the very odd part is this: when `struct library` would contain (or *reference*?) a *collection of books*, wouldn't we need something similar to that 'trick' C uses to reference *strings*, i.e. *reference the initial element using a pointer* and then use *pointer arithmetic* to get at the subsequent entries? But `struct library` is specified as having a
  • ```
  • struct book collection;
  • ```
  • which reads as: contains one *instance* of a `struct book`! Of course I can use C to get a pointer to that internal instance too, if I want (which is what `&` is good for, as in your `&thislib->collection`, but I (and the machine) won't know where the other `num_books-1` book entries are stored!
  • How about this?
  • ```
  • struct book *collection;
  • ```
  • That's more like it: when `char *title` is read as *pointer to `char`* and thus can be interpreted as *pointer to **first** `char` **in an array of `char`s** (a.k.a. a string)*, then the same would be valid for `struct book *collection;`: *a pointer to thee **first** `struct book` instance in an array of `struct book` instances*.
  • With C strings, we have that NUL sentinel to mark the end, rather than keeping a string length as a value somewhere (check out `strlen()` if you haven't already). Here we have a length: `num_books`, so you're supposed to be using that to keep track of the total number of `struct book` records in this particular library instance.
  • Hence I expected the `struct library` definition to look like this:
  • ```
  • struct library {
  • struct book *collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • because then it would read as something sensible to use as a part of memory *organization* for your library. (And apparently the exercise is hinting there may be more libraries loaded into memory at some time due to the `next` pointer.)
  • ------
  • ## Memory organization once again
  • Again, we need to think about how that `struct library` will sit in memory and how it will be able to get at the individual `struct book`-referenced strings specifying author, title and subject: we must be able to print those using `printf()` before we can assume we'll be successful comparing any book title against a given title in order to *delete* that book.
  • Also, you then need to reconsider how you remove a book from the array of books in memory. Do you need to adjust anything else? What if you delete *multiple* books, before you write the library back to disk?
  • # Spinning down...
  • Plenty questions left, but time is running out here and I think you're best served with slowly descending onto your original posted problem: write several small application codes for the parts as suggested in @f~~'s answer. If that doesn't go smoothly and swiftly, **backpedal** and try to write a small test program for each of the bits discussed above. When you do that, always first try to write a piece of code that does '1 struct instance', *then* follow up with another copy that does work for 2..N (arbitrary number of items): you'll only encounter your pointer problems (and learn from them) once you do that as well and think through the various ways of doing this. At this stage there many ways to skin the cat, from trivial to smart & convoluted, and each will hopefully add to your understanding of C pointers: they're fundamental to the language (and hiding underneath many constructs in other languages).
  • I hope there's no due date on the delivery of that exercise: there's plenty to learn and when it doesn't immediately \*click\* (which is the way it goes with most folks), *persevere* and keep the "Very Important Nitpick" section in mind: that part can be improved (it would probably fill a session back-to-back), but at least it's a start to prevent some horrible confusions I've met along the way.
#3: Post edited by user avatar GerHobbelt‭ · 2021-12-18T02:23:39Z (about 3 years ago)
formatting: ugly line wrap at one point
  • # Notice
  • You'll need to backpedal some! Read the answer by @r~~ above, but I expect you need some hints.
  • This reads as either a selfstudy or schooling exercise task, so I'll stick to review + hints and leave you some discovery of your own; I know lots of peeps have trouble grokking *pointers* and the code shows it, so we'll focus on those. Then there's *organization*, i.e. a mental picture of what you're doing and how stuff is organized on disk and in memory (RAM) and how we might go about editing that organization.
  • ----
  • # Analysis of `struct book` and commentary
  • ```
  • struct book{
  • char *title;
  • char *author;
  • char *subject;
  • };
  • ```
  • This tells us a `book` type consists of 3 *pointers* (the `*` in each `char *` type is a clear mark of that).
  • Pointers *point* somewhere ('referencing') and generally take up separate space themselves. `char *` is often also referred as a *string* but that's just what it's used for *most often*: a *string* in C is a sequence of `char` characters (terminated by a NUL character a.k.a. NUL sentinel to mark the end of the string).
  • Here those 3 pointers imply a `struct book` only carries the 3 *references*, while the actual strings are stored elsewhere. (Usually, those are 'allocated on the heap' if you want to keep them alive for a while; I don't know if you've already exercised with `malloc()` and `free()` (and `calloc()` might be handy!) -- if you haven't, do that first in a separate exercise.
  • ## Pointers (and a sub-par analogy towards understanding them)
  • Pointers are references, a bit like phone numbers: when you've got a phone number, you can call that *phone* (and thus reach somebody), but it's definitely *not* the phone itself. As with phone numbers, which you need to write down or otherwise memorize in order not to loose them (or you won't be able to call that person ever again), *pointers* need a little space of their own to be remembered: a pointer variable. `struct book` has 3 of those, so clearly it intends to remember where 3 `char` (character) type values are stored.
  • The slightly wicked bit in C, which I've found throws off a lot of folks learning the language, is that C can do 'pointer arithmetic': that's a bit like adding or subtracting 1 (or more) from a phone number, so you get the next one. Here the analogy goes horribly bad as that won't be the phone number of their neighbour, but in C and computers, it generally is.
  • ### Very Important Nitpick
  • Hence we usually read `char *` as *pointer to a string* while **technically** it's more properly interpreted as (*almost* always!) a pointer to the *first character (`char`!) of that string value*.
  • > I find that many textbooks and educators gloss over this *precise reading of type declarations*, causing a lot of confusion about pointers at the very start.
  • >
  • > `*` must be read as "*points at \<what came before in the type declaration>*" thus `char *` reads as:
  • > - `char`: "(space for) 1 character".
  • > - `*`: "points at..." *what*? `char`! Hence: "*points at a **single *character***".
  • > We *may* call this particular type a 'string' colloquially, but that's only true in *most* circumstances, because C can do *pointer arithmetic* and thus reach the *next* character, and the one after that, and so on, when it *only has a pointer at the first one*!
  • >
  • > ```
  • > char *p = "Hello world\n";
  • > while (*p) {
  • > printf("%c", *p);
  • > p++;
  • > }
  • > ```
  • > `p` now will point at the NUL sentinel `char` at the end of that `"Hello world\n"` string. You have 'walked the pointer down the array of characters that is a string'.
  • >
  • > > BTW: the `while (*p)` loop terminates because `*p` is read and when it has become NUL (0), this evaluates to `while(0)` hence `while(false)` and thus the loop is stopped. OTOH, `while(*p)` at first evaluates to `while('H')`: `'H'` is a non-zero `char` value and thus in the `while` evaluates to `while(true)`. I expect this to be obvious already, but your question and code strongly indicate a missing comprehension of pointers at large, so I address these here to make sure you can pick up from a place where you're probably already comfortable.
  • >
  • > You can also use indexing, which is just another way of using pointers, without *walking* them:
  • > ```
  • > char *p = "Hello world\n";
  • > int i = 0;
  • > while (p[i]) {
  • > printf("%c", p[i]);
  • > i++;
  • > }
  • > ```
  • > `i` will now be 12 and `p[i]` will be NUL(0). Meanwhile, `p` has not been changed and can thus be re-used to access that "Hello world" string again if we feel like it.
  • >
  • > Pointer arithmetic also means that this next code is *identical* to the previous one:
  • >
  • > ```
  • > char *p = "Hello world\n";
  • > int i = 0;
  • > while (*(p + i)) {
  • > printf("%c", *(p + i));
  • > i++;
  • > }
  • > ```
  • > `p + i` reads as: "*add an offset to the pointer `p`; the offset is `i` times the size of a single element of type `*p`* (in this case `char`)" --> the *effective pointer* resulting from this pointer arithmetic will point at the `i`-th `char` in the string pointed at by `p`. And anywhere I use `string`, you can also use the word 'array'.
  • ## `fread` analysis
  • This means that your `fread(&thislib->collection, ...)` might *compile* but is reading like an oddity, because to a programmer it reads as: `thislib->collection` is of type `struct book`, so this `fread()` is told to load a `struct book` instance (and I **ignore** your `sizeof(struct library),1,` bit there on purpose now, to keep things simple) and thus is supposed to load three **pointers** from disk! That, to a programmer, feels like nonsense (or somebody is being horribly clever and knows *exactly* which machine this is going to run on and how -- somebody like Ken Thompson (look him up) might be able to pull this off, but for all us mere mortals this yells "bug!!1!"
  • Why? Because, unlike those phone numbers from the analogy, pointers (*pointer values*) are ephemeral: they are expected to be different on every run of your application, thus storing and loading *those* from disk will lead to disaster: the data we want isn't there this time around, but stored someplace else in RAM.
  • ### Organization: what is stored where?
  • So we come at your first real hurdle, which is encoded as part of the task list in @f~~ answer: you need to sit down and come up with some *organization*: **how are those 3 strings that make a `book` entry stored on disk?**
  • One of **many** answers to that is to store all 3 strings consecutively and decree that your 'library' (series of `book`s) can then be read as reading the entire series of strings from front to back: (1,2,3),(4,5,6),... -- which is relatively easy, but has the significant drawback that you cannot jump to a book in the middle as those titles, authors and subject descriptions will have varying lengths.
  • Plus you won't be able to detect any tiny mistake the program might have made the previous time it ran: if a string is missing or corrupted (e.g. you haven't delimited/terminated it properly last time you wrote the file) you won't be able to tell as each of the strings you encounter along the way can become a tile, author or subject: you won't know where the book *record boundary* is in that library file.
  • ### More food for thought: using *text* files
  • Since you mention a `.txt` file as the library file, an obvious organizational choice might be to store each book as three lines of text, thus using `'\n'` (NewLine) as a string delimiter on disk. Of course, it is then strictly prohibited to have it anywhere in the `subject`, `title` or `author` strings or you'ld have a problem.
  • To delineate a record, one could add an extra line then as part of each record, e.g. `BOOK RECORD`, so your application could then check that each book record starts with such a line or report an error if it doesn't. The possibilities here abound. (Of course, this assumes we're not worried about, say, the `title` of the book reading `"BOOK RECORD"` itself: think about it and see if you can visualize the file organization in your mind (or on paper) and observe that this would not pose a real problem. Only, possibly, a very minor one when the library file has been damaged ('corrupted') on disk.
  • Given the `.txt` file you mention and my mentioning of `\n` as a delimiter, this doesn't sit well with C, which expects its strings to be NUL terminated. Meanwhile, the human convention is to *not* write NUL characters to `.txt` files, so we can open them without hassle in editors like Notepad or vi.
  • So loading the library from file into memory is a bit of a challenge then. Of course, one can use `fread()` for this (a apply a few bits of cleverness afterwards), but `fread()` and `fwrite()` are generally reserved for use with 'binary file formats'; at least that's what a programmer reading thee code would initially expect. Check `fscanf()`, `fgets()` and friends: that's a hint.
  • Which still leaves the `struct book` layout: once you have those title, author and subject strings read into memory using *any* fstream approach, you still need to make sure those strings have a proper 'lifetime' and are pointed to by the pointers in your `struct book` instance variable before you can add it to your library in memory (RAM).
  • > May I suggest you write a small program attempting to add a few books in memory to your library first? You're expected to use `malloc()` and `free()` as you'll be needing those for a lot of this stuff: anything that needs to survive beyond the function call where it was created is usually allocated on the heap, i.e. storage space is requested for these strings using `malloc()` or `calloc()`.
  • >
  • > I expect this will already be tough enough to get right initially, before you add file I/O to the mix.
  • ---
  • # Analysis of `struct library` and commentary
  • Assuming you have been able to write a few (small) programs to create a single book structure in memory and load or store one to disk, we have a look at your `struct library` because there's something quite odd with it (I guess it's a typo):
  • ```
  • struct library {
  • struct book collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • reads to a human programmer as: this `struct library` apparently is **intended** to contain a *collection of books* (`num_books` entries in total) and apparently also points at the *next* library, so we're looking at the basics for a chain of libraries? Would that be a *linked list* of libraries?
  • But the very odd part is this: when `struct library` would contain (or *reference*?) a *collection of books*, wouldn't we need something similar to that 'trick' C uses to reference *strings*, i.e. *reference the initial element using a pointer* and then use *pointer arithmetic* to get at the subsequent entries? But `struct library` is specified as having a
  • ```
  • struct book collection;
  • ```
  • which reads as: contains one *instance* of a `struct book`! Of course I can use C to get a pointer to that internal instance too, if I want (which is what `&` is good for, as in your `&thislib->collection`, but I (and the machine) won't know where the other `num_books - 1` book entries are stored!
  • How about this?
  • ```
  • struct book *collection;
  • ```
  • That's more like it: when `char *title` is read as *pointer to `char`* and thus can be interpreted as *pointer to **first** `char` **in an array of `char`s** (a.k.a. a string)*, then the same would be valid for `struct book *collection;`: *a pointer to thee **first** `struct book` instance in an array of `struct book` instances*.
  • With C strings, we have that NUL sentinel to mark the end, rather than keeping a string length as a value somewhere (check out `strlen()` if you haven't already). Here we have a length: `num_books`, so you're supposed to be using that to keep track of the total number of `struct book` records in this particular library instance.
  • Hence I expected the `struct library` definition to look like this:
  • ```
  • struct library {
  • struct book *collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • because then it would read as something sensible to use as a part of memory *organization* for your library. (And apparently the exercise is hinting there may be more libraries loaded into memory at some time due to the `next` pointer.)
  • ------
  • ## Memory organization once again
  • Again, we need to think about how that `struct library` will sit in memory and how it will be able to get at the individual `struct book`-referenced strings specifying author, title and subject: we must be able to print those using `printf()` before we can assume we'll be successful comparing any book title against a given title in order to *delete* that book.
  • Also, you then need to reconsider how you remove a book from the array of books in memory. Do you need to adjust anything else? What if you delete *multiple* books, before you write the library back to disk?
  • # Spinning down...
  • Plenty questions left, but time is running out here and I think you're best served with slowly descending onto your original posted problem: write several small application codes for the parts as suggested in @f~~'s answer. If that doesn't go smoothly and swiftly, **backpedal** and try to write a small test program for each of the bits discussed above. When you do that, always first try to write a piece of code that does '1 struct instance', *then* follow up with another copy that does work for 2..N (arbitrary number of items): you'll only encounter your pointer problems (and learn from them) once you do that as well and think through the various ways of doing this. At this stage there many ways to skin the cat, from trivial to smart & convoluted, and each will hopefully add to your understanding of C pointers: they're fundamental to the language (and hiding underneath many constructs in other languages).
  • I hope there's no due date on the delivery of that exercise: there's plenty to learn and when it doesn't immediately \*click\* (which is the way it goes with most folks), *persevere* and keep the "Very Important Nitpick" section in mind: that part can be improved (it would probably fill a session back-to-back), but at least it's a start to prevent some horrible confusions I've met along the way.
  • # Notice
  • You'll need to backpedal some! Read the answer by @r~~ above, but I expect you need some hints.
  • This reads as either a selfstudy or schooling exercise task, so I'll stick to review + hints and leave you some discovery of your own; I know lots of peeps have trouble grokking *pointers* and the code shows it, so we'll focus on those. Then there's *organization*, i.e. a mental picture of what you're doing and how stuff is organized on disk and in memory (RAM) and how we might go about editing that organization.
  • ----
  • # Analysis of `struct book` and commentary
  • ```
  • struct book{
  • char *title;
  • char *author;
  • char *subject;
  • };
  • ```
  • This tells us a `book` type consists of 3 *pointers* (the `*` in each `char *` type is a clear mark of that).
  • Pointers *point* somewhere ('referencing') and generally take up separate space themselves. `char *` is often also referred as a *string* but that's just what it's used for *most often*: a *string* in C is a sequence of `char` characters (terminated by a NUL character a.k.a. NUL sentinel to mark the end of the string).
  • Here those 3 pointers imply a `struct book` only carries the 3 *references*, while the actual strings are stored elsewhere. (Usually, those are 'allocated on the heap' if you want to keep them alive for a while; I don't know if you've already exercised with `malloc()` and `free()` (and `calloc()` might be handy!) -- if you haven't, do that first in a separate exercise.
  • ## Pointers (and a sub-par analogy towards understanding them)
  • Pointers are references, a bit like phone numbers: when you've got a phone number, you can call that *phone* (and thus reach somebody), but it's definitely *not* the phone itself. As with phone numbers, which you need to write down or otherwise memorize in order not to loose them (or you won't be able to call that person ever again), *pointers* need a little space of their own to be remembered: a pointer variable. `struct book` has 3 of those, so clearly it intends to remember where 3 `char` (character) type values are stored.
  • The slightly wicked bit in C, which I've found throws off a lot of folks learning the language, is that C can do 'pointer arithmetic': that's a bit like adding or subtracting 1 (or more) from a phone number, so you get the next one. Here the analogy goes horribly bad as that won't be the phone number of their neighbour, but in C and computers, it generally is.
  • ### Very Important Nitpick
  • Hence we usually read `char *` as *pointer to a string* while **technically** it's more properly interpreted as (*almost* always!) a pointer to the *first character (`char`!) of that string value*.
  • > I find that many textbooks and educators gloss over this *precise reading of type declarations*, causing a lot of confusion about pointers at the very start.
  • >
  • > `*` must be read as "*points at \<what came before in the type declaration>*" thus `char *` reads as:
  • > - `char`: "(space for) 1 character".
  • > - `*`: "points at..." *what*? `char`! Hence: "*points at a **single *character***".
  • > We *may* call this particular type a 'string' colloquially, but that's only true in *most* circumstances, because C can do *pointer arithmetic* and thus reach the *next* character, and the one after that, and so on, when it *only has a pointer at the first one*!
  • >
  • > ```
  • > char *p = "Hello world\n";
  • > while (*p) {
  • > printf("%c", *p);
  • > p++;
  • > }
  • > ```
  • > `p` now will point at the NUL sentinel `char` at the end of that `"Hello world\n"` string. You have 'walked the pointer down the array of characters that is a string'.
  • >
  • > > BTW: the `while (*p)` loop terminates because `*p` is read and when it has become NUL (0), this evaluates to `while(0)` hence `while(false)` and thus the loop is stopped. OTOH, `while(*p)` at first evaluates to `while('H')`: `'H'` is a non-zero `char` value and thus in the `while` evaluates to `while(true)`. I expect this to be obvious already, but your question and code strongly indicate a missing comprehension of pointers at large, so I address these here to make sure you can pick up from a place where you're probably already comfortable.
  • >
  • > You can also use indexing, which is just another way of using pointers, without *walking* them:
  • > ```
  • > char *p = "Hello world\n";
  • > int i = 0;
  • > while (p[i]) {
  • > printf("%c", p[i]);
  • > i++;
  • > }
  • > ```
  • > `i` will now be 12 and `p[i]` will be NUL(0). Meanwhile, `p` has not been changed and can thus be re-used to access that "Hello world" string again if we feel like it.
  • >
  • > Pointer arithmetic also means that this next code is *identical* to the previous one:
  • >
  • > ```
  • > char *p = "Hello world\n";
  • > int i = 0;
  • > while (*(p + i)) {
  • > printf("%c", *(p + i));
  • > i++;
  • > }
  • > ```
  • > `p + i` reads as: "*add an offset to the pointer `p`; the offset is `i` times the size of a single element of type `*p`* (in this case `char`)" --> the *effective pointer* resulting from this pointer arithmetic will point at the `i`-th `char` in the string pointed at by `p`. And anywhere I use `string`, you can also use the word 'array'.
  • ## `fread` analysis
  • This means that your `fread(&thislib->collection, ...)` might *compile* but is reading like an oddity, because to a programmer it reads as: `thislib->collection` is of type `struct book`, so this `fread()` is told to load a `struct book` instance (and I **ignore** your `sizeof(struct library),1,` bit there on purpose now, to keep things simple) and thus is supposed to load three **pointers** from disk! That, to a programmer, feels like nonsense (or somebody is being horribly clever and knows *exactly* which machine this is going to run on and how -- somebody like Ken Thompson (look him up) might be able to pull this off, but for all us mere mortals this yells "bug!!1!"
  • Why? Because, unlike those phone numbers from the analogy, pointers (*pointer values*) are ephemeral: they are expected to be different on every run of your application, thus storing and loading *those* from disk will lead to disaster: the data we want isn't there this time around, but stored someplace else in RAM.
  • ### Organization: what is stored where?
  • So we come at your first real hurdle, which is encoded as part of the task list in @f~~ answer: you need to sit down and come up with some *organization*: **how are those 3 strings that make a `book` entry stored on disk?**
  • One of **many** answers to that is to store all 3 strings consecutively and decree that your 'library' (series of `book`s) can then be read as reading the entire series of strings from front to back: (1,2,3),(4,5,6),... -- which is relatively easy, but has the significant drawback that you cannot jump to a book in the middle as those titles, authors and subject descriptions will have varying lengths.
  • Plus you won't be able to detect any tiny mistake the program might have made the previous time it ran: if a string is missing or corrupted (e.g. you haven't delimited/terminated it properly last time you wrote the file) you won't be able to tell as each of the strings you encounter along the way can become a tile, author or subject: you won't know where the book *record boundary* is in that library file.
  • ### More food for thought: using *text* files
  • Since you mention a `.txt` file as the library file, an obvious organizational choice might be to store each book as three lines of text, thus using `'\n'` (NewLine) as a string delimiter on disk. Of course, it is then strictly prohibited to have it anywhere in the `subject`, `title` or `author` strings or you'ld have a problem.
  • To delineate a record, one could add an extra line then as part of each record, e.g. `BOOK RECORD`, so your application could then check that each book record starts with such a line or report an error if it doesn't. The possibilities here abound. (Of course, this assumes we're not worried about, say, the `title` of the book reading `"BOOK RECORD"` itself: think about it and see if you can visualize the file organization in your mind (or on paper) and observe that this would not pose a real problem. Only, possibly, a very minor one when the library file has been damaged ('corrupted') on disk.
  • Given the `.txt` file you mention and my mentioning of `\n` as a delimiter, this doesn't sit well with C, which expects its strings to be NUL terminated. Meanwhile, the human convention is to *not* write NUL characters to `.txt` files, so we can open them without hassle in editors like Notepad or vi.
  • So loading the library from file into memory is a bit of a challenge then. Of course, one can use `fread()` for this (a apply a few bits of cleverness afterwards), but `fread()` and `fwrite()` are generally reserved for use with 'binary file formats'; at least that's what a programmer reading thee code would initially expect. Check `fscanf()`, `fgets()` and friends: that's a hint.
  • Which still leaves the `struct book` layout: once you have those title, author and subject strings read into memory using *any* fstream approach, you still need to make sure those strings have a proper 'lifetime' and are pointed to by the pointers in your `struct book` instance variable before you can add it to your library in memory (RAM).
  • > May I suggest you write a small program attempting to add a few books in memory to your library first? You're expected to use `malloc()` and `free()` as you'll be needing those for a lot of this stuff: anything that needs to survive beyond the function call where it was created is usually allocated on the heap, i.e. storage space is requested for these strings using `malloc()` or `calloc()`.
  • >
  • > I expect this will already be tough enough to get right initially, before you add file I/O to the mix.
  • ---
  • # Analysis of `struct library` and commentary
  • Assuming you have been able to write a few (small) programs to create a single book structure in memory and load or store one to disk, we have a look at your `struct library` because there's something quite odd with it (I guess it's a typo):
  • ```
  • struct library {
  • struct book collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • reads to a human programmer as: this `struct library` apparently is **intended** to contain a *collection of books* (`num_books` entries in total) and apparently also points at the *next* library, so we're looking at the basics for a chain of libraries? Would that be a *linked list* of libraries?
  • But the very odd part is this: when `struct library` would contain (or *reference*?) a *collection of books*, wouldn't we need something similar to that 'trick' C uses to reference *strings*, i.e. *reference the initial element using a pointer* and then use *pointer arithmetic* to get at the subsequent entries? But `struct library` is specified as having a
  • ```
  • struct book collection;
  • ```
  • which reads as: contains one *instance* of a `struct book`! Of course I can use C to get a pointer to that internal instance too, if I want (which is what `&` is good for, as in your `&thislib->collection`, but I (and the machine) won't know where the other `num_books-1` book entries are stored!
  • How about this?
  • ```
  • struct book *collection;
  • ```
  • That's more like it: when `char *title` is read as *pointer to `char`* and thus can be interpreted as *pointer to **first** `char` **in an array of `char`s** (a.k.a. a string)*, then the same would be valid for `struct book *collection;`: *a pointer to thee **first** `struct book` instance in an array of `struct book` instances*.
  • With C strings, we have that NUL sentinel to mark the end, rather than keeping a string length as a value somewhere (check out `strlen()` if you haven't already). Here we have a length: `num_books`, so you're supposed to be using that to keep track of the total number of `struct book` records in this particular library instance.
  • Hence I expected the `struct library` definition to look like this:
  • ```
  • struct library {
  • struct book *collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • because then it would read as something sensible to use as a part of memory *organization* for your library. (And apparently the exercise is hinting there may be more libraries loaded into memory at some time due to the `next` pointer.)
  • ------
  • ## Memory organization once again
  • Again, we need to think about how that `struct library` will sit in memory and how it will be able to get at the individual `struct book`-referenced strings specifying author, title and subject: we must be able to print those using `printf()` before we can assume we'll be successful comparing any book title against a given title in order to *delete* that book.
  • Also, you then need to reconsider how you remove a book from the array of books in memory. Do you need to adjust anything else? What if you delete *multiple* books, before you write the library back to disk?
  • # Spinning down...
  • Plenty questions left, but time is running out here and I think you're best served with slowly descending onto your original posted problem: write several small application codes for the parts as suggested in @f~~'s answer. If that doesn't go smoothly and swiftly, **backpedal** and try to write a small test program for each of the bits discussed above. When you do that, always first try to write a piece of code that does '1 struct instance', *then* follow up with another copy that does work for 2..N (arbitrary number of items): you'll only encounter your pointer problems (and learn from them) once you do that as well and think through the various ways of doing this. At this stage there many ways to skin the cat, from trivial to smart & convoluted, and each will hopefully add to your understanding of C pointers: they're fundamental to the language (and hiding underneath many constructs in other languages).
  • I hope there's no due date on the delivery of that exercise: there's plenty to learn and when it doesn't immediately \*click\* (which is the way it goes with most folks), *persevere* and keep the "Very Important Nitpick" section in mind: that part can be improved (it would probably fill a session back-to-back), but at least it's a start to prevent some horrible confusions I've met along the way.
#2: Post edited by user avatar GerHobbelt‭ · 2021-12-18T02:19:33Z (about 3 years ago)
typo fixes
  • # Notice
  • You'll need to backpedal some! Read the answer by @r~~ above, but I expect you need some hints.
  • This reads as either a selfstudy or schooling exercise task, so I'll stick to review + hints and leave you some discovery of your own; I know lost of peeps have trouble grokking *pointers* and the code shows it, so we'll focus on those. Then there's *organization*, i.e. a mental picture of what you're doing and how stuff is organized on disk and in memory (RAM) and how we might go about editing that organization.
  • ----
  • # Analysis of `struct book` and commentary
  • ```
  • struct book{
  • char *title;
  • char *author;
  • char *subject;
  • };
  • ```
  • This tells us a `book` type consists of 3 *pointers* (the `*` in each `char *` type is a clear mark of that).
  • Pointers *point* somewhere ('referencing') and generally take up separate space themselves. `char *` is often also referred as a *string* but that's just what it's used for *most often*: a *string* in C is a sequence of `char` characters (terminated by a NUL character a.k.a. NUL sentinel to mark the end of the string).
  • Here those 3 pointers imply a `struct book` only carries the 3 *references*, while the actual strings are stored elsewhere. (Usually, those are 'allocated on the heap' if you want to keep them alive for a while; I don't know if you've already exercised with `malloc()` and `free()` (and `calloc()` might be handy!) -- if you haven't, do that first in a separate exercise.
  • ## Pointers (and a sub-par analogy towards understanding them)
  • Pointers are references, a bit like phone numbers: when you've got a phone number, you can call that *phone* (and thus reach somebody), but it's definitely *not* the phone itself. As with phone numbers, which you need to write down or otherwise memorize in order not to loose them (or you won't be able to call that person ever again), *pointers* need a little space of their own to be remembered: a pointer variable. `struct book` has 3 of those, so clearly it intends to remember where 3 `char` (character) type values are stored.
  • The slightly wicked bit in C, which I've found throws off a lot of folks learning the language, is that C can do 'pointer arithmetic': that's a bit like adding or subtracting 1 (or more) from a phone number, so you get the next one. Here the analogy goes horribly bad as that won't be the phone number of their neighbour, but in C and computers, it generally is.
  • ### Very Important Nitpick
  • Hence we usually read `char *` as *pointer to a string* while **technically** it's more properly interpreted as (*almost* always!) a pointer to the *first character (`char`!) of that string value*.
  • > I find that many textbooks and educators gloss over this *precise reading of type declarations*, causing a lot of confusion about pointers at the very start.
  • >
  • > `*` must be read as "*points at \<what came before in the type declaration>*" thus `char *` reads as:
  • > - `char`: "(space for) 1 character".
  • > - `*`: "points at..." *what*? `char`! Hence: "*points at a **single *character***".
  • > We *may* call this particular type a 'string' coloquially, but that's only true in *most* circumstances, because C can do *pointer arithmetic* and thus reach the *next* character, and the one after that, and so on, when it *only has a pointer at the first one*!
  • >
  • > ```
  • > char *p = "Hello world\n";
  • > while (*p) {
  • > printf("%c", *p);
  • > p++;
  • > }
  • > ```
  • > `p` now will point at the NUL sentinel `char` at the end of that `"Hello world\n"` string. You have 'walked the pointer down the array of characters that is a string'.
  • >
  • > > BTW: the `while (*p)` loop terminates because `*p` is read and when it has become NUL (0), this evaluates to `while(0)` hence `while(false)` and thus the loop is stopped. OTOH, `while(*p)` at first evaluates to `while('H')`: `'H'` is a non-zero `char` value and thus in the `while` evaluates to `while(true)`. I expect this to be obvious already, but your question and code strongly indicate a missing comprehension of pointers at large, so I address these here to make sure you can pick up from a place where you're probably already comfortable.
  • >
  • > You can also use indexing, which is just another way of using pointers, without *walking* them:
  • > ```
  • > char *p = "Hello world\n";
  • > int i = 0;
  • > while (p[i]) {
  • > printf("%c", p[i]);
  • > i++;
  • > }
  • > ```
  • > `i` will now be 12 and `p[i]` will be NUL(0). Meanwhile, `p` has not been changed and can thus be re-used to access that "Hello world" string again if we feel like it.
  • >
  • > Pointer arithmetic also means that this next code is *identical* to the previous one:
  • >
  • > ```
  • > char *p = "Hello world\n";
  • > int i = 0;
  • > while (*(p + i)) {
  • > printf("%c", *(p + i));
  • > i++;
  • > }
  • > ```
  • > `p + i` reads as: "*add an offset to the pointer `p`; the offset is `i` times the size of a single element of type `*p`* (in this case `char`)" --> the *effective pointer* resulting from this pointer arithmetic will point at the `i`-th `char` in the string pointed at by `p`. And anywhere I use `string`, you can also use the word 'array'.
  • ## `fread` analysis
  • This means that your `fread(&thislib->collection, ...)` might *compile* but is reading like an oddity, because to a programmer it reads as: `thislib->collection` is of type `struct book`, so this `fread()` is told to load a `struct book` instance (and I **ignore** your `sizeof(struct library),1,` bit there on purpose now, to keep things simple) and thus is supposed to load three **pointers** from disk! That, to a programmer, feels like nonsense (or somebody is being horribly clever and knows *exactly* which machine this is going to run on and how -- somebody like Ken Thompson (look him up) might be able to pull this off, but for all us mere mortals this yells "bug!!1!"
  • Why? Because, unlike those phone numbers from the analogy, pointers (*pointer values*) are ephemeral: they are expected to be different on every run of your application, thus storing and loading *those* from disk will lead to disaster: the data we want isn't there this time around, but stored someplace else in RAM.
  • ### Organization: what is stored where?
  • So we come at your first real hurdle, which is encoded as part of the task list in @f~~ answer: you need to sit down and come up with some *organization*: **how are those 3 strings that make a `book` entry stored on disk?**
  • One of **many** answers to that is to store all 3 strings consecutively and decree that your 'library' (series of `book`s) can then be read as reading the entire series of strings from front to back: (1,2,3),(4,5,6),... -- which is relatively easy, but has the significant drawback that you cannot jump to a book in the middle as those titles, authors and subject descriptions will have varying lengths.
  • Plus you won't be able to detect any tiny mistake the program might have made the previous time it ran: if a string is missing or corrupted (e.g. you haven't delimited/terminated it properly last time you wrote the file) you won't be able to tell as each of the strings you encounter along the way can become a tile, author or subject: you won't know where the book *record boundary* is in that library file.
  • ### More food for thought: using *text* files
  • Since you mention a `.txt` file as the library file, an obvious organizational choice might be to store each book as three lines of text, thus using `'\n'` (NewLine) as a string delimiter on disk. Of course, it is then strictly prohibited to have it anywhere in the `subject`, `title` or `author` strings or you'ld have a problem.
  • To delineate a record, one could add an extra line then as part of each record, e.g. `BOOK RECORD`, so your application could then check that each book record starts with such a line or report an error if it doesn't. The possibilities here abound. (Of course, this assumes we're not worried about, say, the `title` of the book reading `"BOOK RECORD"` itself: think about it and see if you can visualize the file organization in your mind (or on paper) and observe that this would not pose a real problem. Only, possibly, a very minor one when the library file has been damaged ('corrupted') on disk.
  • Given the `.txt` file you mention and my mentioning of `\n` as a delimiter, this doesn't sit well with C, which expects its strings to be NUL terminated. Meanwhile, the human convention is to *not* write NUL characters to `.txt` files, so we can open them without hassle in editors like Notepad or vi.
  • So loading the library from file into memory is a bit of a challenge then. Of course, one can use `fread()` for this (a apply a few bits of cleverness afterwards), but `fread()` and `fwrite()` are generally reserved for use with 'binary file formats'; at least that's what a programmer reading thee code would initially expect. Check `fscanf()`, `fgets()` and friends: that's a hint.
  • Which still leaves the `struct book` layout: once you have those title, author and subject strings read into memory using *any* fstream approach, you still need to make sure those strings have a proper 'lifetime' and are pointed to by the pointers in your `struct book` instance variable before you can add it to your library in memory (RAM).
  • > May I suggest you write a small program attempting to add a few books in memory to your library first? You're expected to use `malloc()` and `free()` as you'll be needing those for a lot of this stuff: anything that needs to survive beyond the function call where it was created is usually allocated on the heap, i.e. storage space is requested for these strings using `malloc()` or `calloc()`.
  • >
  • > I expect this will already be tough enough to get right initially, before you add file I/O to the mix.
  • ---
  • # Analysis of `struct library` and commentary
  • Assuming you have been able to write a few (small) programs to create a single book structure in memory and load or store one to disk, we have a look at your `struct library` because there's something quite odd with it (I guess it's a typo):
  • ```
  • struct library {
  • struct book collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • reads to a human programmer as: this `struct library` apparently is **intended** to contain a *collection of books* (`num_books` entries in total) and apparently also points at the *next* library, so we're looking at the basics for a chain of libraries? Would that be a *linked list* of libraries?
  • But the very odd part is this: when `struct library` would contain (or *reference*?) a *collection of books*, wouldn't we need something similar to that 'trick' C uses to reference *strings*, i.e. *reference the initial element using a pointer* and then use *pointer arithmetic* to get at the subsequent entries? But `struct library` is specified as having a
  • ```
  • struct book collection;
  • ```
  • which reads as: contains one *instance* of a `struct book`! Of course I can use C to get a pointer to that internal instance too, if I want (which is what `&` is good for, as in your `&thislib->collection`, but I (and the machine) won't know where the other `num_books - 1` book entries are stored!
  • How about this?
  • ```
  • struct book *collection;
  • ```
  • That's more like it: when `char *title` is read as *pointer to `char`* and thus can be interpreted as *pointer to **first** `char` **in an array of `char`s** (a.k.a. a string)*, then the same would be valid for `struct book *collection;`: *a pointer to thee **first** `struct book` instance in an array of `struct book` instances*.
  • With C strings, we have that NUL sentinel to mark the end, rather than keeping a string length as a value somewhere (check out `strlen()` if you haven't already). Here we have a length: `num_books`, so you're supposed to be using that to keep track of the total number of `struct book` records in this particular library instance.
  • Hence I expected the `struct library` definition to look like this:
  • ```
  • struct library {
  • struct book *collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • because then it would read as something sensible to use as a part of memory *organization* for your library. (And apparently the exercise is hinting there may be more libraries loaded into memory at some time due to the `next` pointer.)
  • ------
  • ## Memory organization once again
  • Again, we need to think about how that `struct library` will sit in memory and how it will be able to get at the individual `struct book`-referenced strings specifying author, title and subject: we must be able to print those using `printf()` before we can assume we'll be successful comparing any book title against a given title in order to *delete* that book.
  • Also, you then need to reconsider how you remove a book from the array of books in memory. Do you need to adjust anything else? What if you delete *multiple* books, before you write the library back to disk?
  • # Spinning down...
  • Plenty questions left, but time is running out here and I think you're best served with slowly descending onto your original posted problem: write several small application codes for the parts as suggested in @f~~'s answer. If that doesn't go smoothly and swiftly, **backpedal** and try to write a small test program for each of the bits discussed above. When you do that, always first try to write a piece of code that does '1 struct instance', *then* follow up with another copy that does work for 2..N (arbitrary number of items): you'll only encounter your pointer problems (and learn from them) once you do that as well and think through the various ways of doing this. At this stage there many ways to skin the cat, from trivial to smart & convoluted, and each will hopefully add to your understanding of C pointers: they're fundamental to the language (and hiding underneath many constructs in other languages).
  • I hope there's no due date on the delivery of that exercise: there's plenty to learn and when it doesn't immediately \*click\* (which is the way it goes with most folks), *persevere* and keep the "Very Important Nitpick" section in mind: that part can be improved (it would probably fill a session back-to-back), but at least it's a start to prevent some horrible confusions I've met along the way.
  • # Notice
  • You'll need to backpedal some! Read the answer by @r~~ above, but I expect you need some hints.
  • This reads as either a selfstudy or schooling exercise task, so I'll stick to review + hints and leave you some discovery of your own; I know lots of peeps have trouble grokking *pointers* and the code shows it, so we'll focus on those. Then there's *organization*, i.e. a mental picture of what you're doing and how stuff is organized on disk and in memory (RAM) and how we might go about editing that organization.
  • ----
  • # Analysis of `struct book` and commentary
  • ```
  • struct book{
  • char *title;
  • char *author;
  • char *subject;
  • };
  • ```
  • This tells us a `book` type consists of 3 *pointers* (the `*` in each `char *` type is a clear mark of that).
  • Pointers *point* somewhere ('referencing') and generally take up separate space themselves. `char *` is often also referred as a *string* but that's just what it's used for *most often*: a *string* in C is a sequence of `char` characters (terminated by a NUL character a.k.a. NUL sentinel to mark the end of the string).
  • Here those 3 pointers imply a `struct book` only carries the 3 *references*, while the actual strings are stored elsewhere. (Usually, those are 'allocated on the heap' if you want to keep them alive for a while; I don't know if you've already exercised with `malloc()` and `free()` (and `calloc()` might be handy!) -- if you haven't, do that first in a separate exercise.
  • ## Pointers (and a sub-par analogy towards understanding them)
  • Pointers are references, a bit like phone numbers: when you've got a phone number, you can call that *phone* (and thus reach somebody), but it's definitely *not* the phone itself. As with phone numbers, which you need to write down or otherwise memorize in order not to loose them (or you won't be able to call that person ever again), *pointers* need a little space of their own to be remembered: a pointer variable. `struct book` has 3 of those, so clearly it intends to remember where 3 `char` (character) type values are stored.
  • The slightly wicked bit in C, which I've found throws off a lot of folks learning the language, is that C can do 'pointer arithmetic': that's a bit like adding or subtracting 1 (or more) from a phone number, so you get the next one. Here the analogy goes horribly bad as that won't be the phone number of their neighbour, but in C and computers, it generally is.
  • ### Very Important Nitpick
  • Hence we usually read `char *` as *pointer to a string* while **technically** it's more properly interpreted as (*almost* always!) a pointer to the *first character (`char`!) of that string value*.
  • > I find that many textbooks and educators gloss over this *precise reading of type declarations*, causing a lot of confusion about pointers at the very start.
  • >
  • > `*` must be read as "*points at \<what came before in the type declaration>*" thus `char *` reads as:
  • > - `char`: "(space for) 1 character".
  • > - `*`: "points at..." *what*? `char`! Hence: "*points at a **single *character***".
  • > We *may* call this particular type a 'string' colloquially, but that's only true in *most* circumstances, because C can do *pointer arithmetic* and thus reach the *next* character, and the one after that, and so on, when it *only has a pointer at the first one*!
  • >
  • > ```
  • > char *p = "Hello world\n";
  • > while (*p) {
  • > printf("%c", *p);
  • > p++;
  • > }
  • > ```
  • > `p` now will point at the NUL sentinel `char` at the end of that `"Hello world\n"` string. You have 'walked the pointer down the array of characters that is a string'.
  • >
  • > > BTW: the `while (*p)` loop terminates because `*p` is read and when it has become NUL (0), this evaluates to `while(0)` hence `while(false)` and thus the loop is stopped. OTOH, `while(*p)` at first evaluates to `while('H')`: `'H'` is a non-zero `char` value and thus in the `while` evaluates to `while(true)`. I expect this to be obvious already, but your question and code strongly indicate a missing comprehension of pointers at large, so I address these here to make sure you can pick up from a place where you're probably already comfortable.
  • >
  • > You can also use indexing, which is just another way of using pointers, without *walking* them:
  • > ```
  • > char *p = "Hello world\n";
  • > int i = 0;
  • > while (p[i]) {
  • > printf("%c", p[i]);
  • > i++;
  • > }
  • > ```
  • > `i` will now be 12 and `p[i]` will be NUL(0). Meanwhile, `p` has not been changed and can thus be re-used to access that "Hello world" string again if we feel like it.
  • >
  • > Pointer arithmetic also means that this next code is *identical* to the previous one:
  • >
  • > ```
  • > char *p = "Hello world\n";
  • > int i = 0;
  • > while (*(p + i)) {
  • > printf("%c", *(p + i));
  • > i++;
  • > }
  • > ```
  • > `p + i` reads as: "*add an offset to the pointer `p`; the offset is `i` times the size of a single element of type `*p`* (in this case `char`)" --> the *effective pointer* resulting from this pointer arithmetic will point at the `i`-th `char` in the string pointed at by `p`. And anywhere I use `string`, you can also use the word 'array'.
  • ## `fread` analysis
  • This means that your `fread(&thislib->collection, ...)` might *compile* but is reading like an oddity, because to a programmer it reads as: `thislib->collection` is of type `struct book`, so this `fread()` is told to load a `struct book` instance (and I **ignore** your `sizeof(struct library),1,` bit there on purpose now, to keep things simple) and thus is supposed to load three **pointers** from disk! That, to a programmer, feels like nonsense (or somebody is being horribly clever and knows *exactly* which machine this is going to run on and how -- somebody like Ken Thompson (look him up) might be able to pull this off, but for all us mere mortals this yells "bug!!1!"
  • Why? Because, unlike those phone numbers from the analogy, pointers (*pointer values*) are ephemeral: they are expected to be different on every run of your application, thus storing and loading *those* from disk will lead to disaster: the data we want isn't there this time around, but stored someplace else in RAM.
  • ### Organization: what is stored where?
  • So we come at your first real hurdle, which is encoded as part of the task list in @f~~ answer: you need to sit down and come up with some *organization*: **how are those 3 strings that make a `book` entry stored on disk?**
  • One of **many** answers to that is to store all 3 strings consecutively and decree that your 'library' (series of `book`s) can then be read as reading the entire series of strings from front to back: (1,2,3),(4,5,6),... -- which is relatively easy, but has the significant drawback that you cannot jump to a book in the middle as those titles, authors and subject descriptions will have varying lengths.
  • Plus you won't be able to detect any tiny mistake the program might have made the previous time it ran: if a string is missing or corrupted (e.g. you haven't delimited/terminated it properly last time you wrote the file) you won't be able to tell as each of the strings you encounter along the way can become a tile, author or subject: you won't know where the book *record boundary* is in that library file.
  • ### More food for thought: using *text* files
  • Since you mention a `.txt` file as the library file, an obvious organizational choice might be to store each book as three lines of text, thus using `'\n'` (NewLine) as a string delimiter on disk. Of course, it is then strictly prohibited to have it anywhere in the `subject`, `title` or `author` strings or you'ld have a problem.
  • To delineate a record, one could add an extra line then as part of each record, e.g. `BOOK RECORD`, so your application could then check that each book record starts with such a line or report an error if it doesn't. The possibilities here abound. (Of course, this assumes we're not worried about, say, the `title` of the book reading `"BOOK RECORD"` itself: think about it and see if you can visualize the file organization in your mind (or on paper) and observe that this would not pose a real problem. Only, possibly, a very minor one when the library file has been damaged ('corrupted') on disk.
  • Given the `.txt` file you mention and my mentioning of `\n` as a delimiter, this doesn't sit well with C, which expects its strings to be NUL terminated. Meanwhile, the human convention is to *not* write NUL characters to `.txt` files, so we can open them without hassle in editors like Notepad or vi.
  • So loading the library from file into memory is a bit of a challenge then. Of course, one can use `fread()` for this (a apply a few bits of cleverness afterwards), but `fread()` and `fwrite()` are generally reserved for use with 'binary file formats'; at least that's what a programmer reading thee code would initially expect. Check `fscanf()`, `fgets()` and friends: that's a hint.
  • Which still leaves the `struct book` layout: once you have those title, author and subject strings read into memory using *any* fstream approach, you still need to make sure those strings have a proper 'lifetime' and are pointed to by the pointers in your `struct book` instance variable before you can add it to your library in memory (RAM).
  • > May I suggest you write a small program attempting to add a few books in memory to your library first? You're expected to use `malloc()` and `free()` as you'll be needing those for a lot of this stuff: anything that needs to survive beyond the function call where it was created is usually allocated on the heap, i.e. storage space is requested for these strings using `malloc()` or `calloc()`.
  • >
  • > I expect this will already be tough enough to get right initially, before you add file I/O to the mix.
  • ---
  • # Analysis of `struct library` and commentary
  • Assuming you have been able to write a few (small) programs to create a single book structure in memory and load or store one to disk, we have a look at your `struct library` because there's something quite odd with it (I guess it's a typo):
  • ```
  • struct library {
  • struct book collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • reads to a human programmer as: this `struct library` apparently is **intended** to contain a *collection of books* (`num_books` entries in total) and apparently also points at the *next* library, so we're looking at the basics for a chain of libraries? Would that be a *linked list* of libraries?
  • But the very odd part is this: when `struct library` would contain (or *reference*?) a *collection of books*, wouldn't we need something similar to that 'trick' C uses to reference *strings*, i.e. *reference the initial element using a pointer* and then use *pointer arithmetic* to get at the subsequent entries? But `struct library` is specified as having a
  • ```
  • struct book collection;
  • ```
  • which reads as: contains one *instance* of a `struct book`! Of course I can use C to get a pointer to that internal instance too, if I want (which is what `&` is good for, as in your `&thislib->collection`, but I (and the machine) won't know where the other `num_books - 1` book entries are stored!
  • How about this?
  • ```
  • struct book *collection;
  • ```
  • That's more like it: when `char *title` is read as *pointer to `char`* and thus can be interpreted as *pointer to **first** `char` **in an array of `char`s** (a.k.a. a string)*, then the same would be valid for `struct book *collection;`: *a pointer to thee **first** `struct book` instance in an array of `struct book` instances*.
  • With C strings, we have that NUL sentinel to mark the end, rather than keeping a string length as a value somewhere (check out `strlen()` if you haven't already). Here we have a length: `num_books`, so you're supposed to be using that to keep track of the total number of `struct book` records in this particular library instance.
  • Hence I expected the `struct library` definition to look like this:
  • ```
  • struct library {
  • struct book *collection;
  • int num_books;
  • struct library *next;
  • };
  • ```
  • because then it would read as something sensible to use as a part of memory *organization* for your library. (And apparently the exercise is hinting there may be more libraries loaded into memory at some time due to the `next` pointer.)
  • ------
  • ## Memory organization once again
  • Again, we need to think about how that `struct library` will sit in memory and how it will be able to get at the individual `struct book`-referenced strings specifying author, title and subject: we must be able to print those using `printf()` before we can assume we'll be successful comparing any book title against a given title in order to *delete* that book.
  • Also, you then need to reconsider how you remove a book from the array of books in memory. Do you need to adjust anything else? What if you delete *multiple* books, before you write the library back to disk?
  • # Spinning down...
  • Plenty questions left, but time is running out here and I think you're best served with slowly descending onto your original posted problem: write several small application codes for the parts as suggested in @f~~'s answer. If that doesn't go smoothly and swiftly, **backpedal** and try to write a small test program for each of the bits discussed above. When you do that, always first try to write a piece of code that does '1 struct instance', *then* follow up with another copy that does work for 2..N (arbitrary number of items): you'll only encounter your pointer problems (and learn from them) once you do that as well and think through the various ways of doing this. At this stage there many ways to skin the cat, from trivial to smart & convoluted, and each will hopefully add to your understanding of C pointers: they're fundamental to the language (and hiding underneath many constructs in other languages).
  • I hope there's no due date on the delivery of that exercise: there's plenty to learn and when it doesn't immediately \*click\* (which is the way it goes with most folks), *persevere* and keep the "Very Important Nitpick" section in mind: that part can be improved (it would probably fill a session back-to-back), but at least it's a start to prevent some horrible confusions I've met along the way.
#1: Initial revision by user avatar GerHobbelt‭ · 2021-12-18T02:17:21Z (about 3 years ago)
# Notice

You'll need to backpedal some! Read the answer by @r~~ above, but I expect you need some hints.

This reads as either a selfstudy or schooling exercise task, so I'll stick to review + hints and leave you some discovery of your own; I know lost of peeps have trouble grokking *pointers* and the code shows it, so we'll focus on those. Then there's *organization*, i.e. a mental picture of what you're doing and how stuff is organized on disk and in memory (RAM) and how we might go about editing that organization.

----

# Analysis of `struct book` and commentary

```
struct book{
 char *title;
 char *author;
 char *subject;
};
```

This tells us a `book` type consists of 3 *pointers* (the `*` in each `char *` type is a clear mark of that).

Pointers *point* somewhere ('referencing') and generally take up separate space themselves. `char *` is often also referred as a *string* but that's just what it's used for *most often*: a *string* in C is a sequence of `char` characters (terminated by a NUL character a.k.a. NUL sentinel to mark the end of the string).

Here those 3 pointers imply a `struct book` only carries the 3 *references*, while the actual strings are stored elsewhere. (Usually, those are 'allocated on the heap' if you want to keep them alive for a while; I don't know if you've already exercised with `malloc()` and `free()` (and `calloc()` might be handy!) -- if you haven't, do that first in a separate exercise.


## Pointers (and a sub-par analogy towards understanding them)

Pointers are references, a bit like phone numbers: when you've got a phone number, you can call that *phone* (and thus reach somebody), but it's definitely *not* the phone itself. As with phone numbers, which you need to write down or otherwise memorize in order not to loose them (or you won't be able to call that person ever again), *pointers* need a little space of their own to be remembered: a pointer variable. `struct book` has 3 of those, so clearly it intends to remember where 3 `char` (character) type values are stored.

The slightly wicked bit in C, which I've found throws off a lot of folks learning the language, is that C can do 'pointer arithmetic': that's a bit like adding or subtracting 1 (or more) from a phone number, so you get the next one. Here the analogy goes horribly bad as that won't be the phone number of their neighbour, but in C and computers, it generally is. 

### Very Important Nitpick

Hence we usually read `char *` as *pointer to a string* while **technically** it's more properly interpreted as (*almost* always!) a pointer to the *first character (`char`!) of that string value*.

> I find that many textbooks and educators gloss over this *precise reading of type declarations*, causing a lot of confusion about pointers at the very start.
>
> `*` must be read as "*points at \<what came before in the type declaration>*" thus `char *` reads as: 
> - `char`: "(space for) 1 character".
> - `*`: "points at..." *what*? `char`! Hence: "*points at a **single *character***".
> We *may* call this particular type a 'string' coloquially, but that's only true in *most* circumstances, because C can do *pointer arithmetic* and thus reach the *next* character, and the one after that, and so on, when it *only has a pointer at the first one*!
>
> ```
> char *p = "Hello world\n";
> while (*p) { 
>   printf("%c", *p);
>   p++;
> }
> ```
> `p` now will point at the NUL sentinel `char` at the end of that `"Hello world\n"` string. You have 'walked the pointer down the array of characters that is a string'.
>
> > BTW: the `while (*p)` loop terminates because `*p` is read and when it has become NUL (0), this evaluates to `while(0)` hence `while(false)` and thus the loop is stopped. OTOH, `while(*p)` at first evaluates to `while('H')`: `'H'` is a non-zero `char` value and thus in the `while` evaluates to `while(true)`. I expect this to be obvious already, but your question and code strongly indicate a missing comprehension of pointers at large, so I address these here to make sure you can pick up from a place where you're probably already comfortable.
>
> You can also use indexing, which is just another way of using pointers, without *walking* them:
> ```
> char *p = "Hello world\n";
> int i = 0;
> while (p[i]) { 
>   printf("%c", p[i]);
>   i++;
> }
> ```
> `i` will now be 12 and `p[i]` will be NUL(0). Meanwhile, `p` has not been changed and can thus be re-used to access that "Hello world" string again if we feel like it.
>
> Pointer arithmetic also means that this next code is *identical* to the previous one:
>
> ```
> char *p = "Hello world\n";
> int i = 0;
> while (*(p + i)) { 
>   printf("%c", *(p + i));
>   i++;
> }
> ```
> `p + i` reads as: "*add an offset to the pointer `p`; the offset is `i` times the size of a single element of type `*p`* (in this case `char`)" --> the *effective pointer* resulting from this pointer arithmetic will point at the `i`-th `char` in the string pointed at by `p`. And anywhere I use `string`, you can also use the word 'array'. 


## `fread` analysis

This means that your `fread(&thislib->collection, ...)` might *compile* but is reading like an oddity, because to a programmer it reads as: `thislib->collection` is of type `struct book`, so this `fread()` is told to load a `struct book` instance (and I **ignore** your `sizeof(struct library),1,` bit there on purpose now, to keep things simple) and thus is supposed to load three **pointers** from disk! That, to a programmer, feels like nonsense (or somebody is being horribly clever and knows *exactly* which machine this is going to run on and how -- somebody like Ken Thompson (look him up) might be able to pull this off, but for all us mere mortals this yells "bug!!1!"

Why? Because, unlike those phone numbers from the analogy, pointers (*pointer values*) are ephemeral: they are expected to be different on every run of your application, thus storing and loading *those* from disk will lead to disaster: the data we want isn't there this time around, but stored someplace else in RAM.

### Organization: what is stored where?

So we come at your first real hurdle, which is encoded as part of the task list in @f~~ answer: you need to sit down and come up with some *organization*: **how are those 3 strings that make a `book` entry stored on disk?**

One of **many** answers to that is to store all 3 strings consecutively and decree that your 'library' (series of `book`s) can then be read as reading the entire series of strings from front to back: (1,2,3),(4,5,6),... -- which is relatively easy, but has the significant drawback that you cannot jump to a book in the middle as those titles, authors and subject descriptions will have varying lengths. 
Plus you won't be able to detect any tiny mistake the program might have made the previous time it ran: if a string is missing or corrupted (e.g. you haven't delimited/terminated it properly last time you wrote the file) you won't be able to tell as each of the strings you encounter along the way can become a tile, author or subject: you won't know where the book *record boundary* is in that library file.

### More food for thought: using *text* files

Since you mention a `.txt` file as the library file, an obvious organizational choice might be to store each book as three lines of text, thus using `'\n'` (NewLine) as a string delimiter on disk. Of course, it is then strictly prohibited to have it anywhere in the `subject`, `title` or `author` strings or you'ld have a problem.

To delineate a record, one could add an extra line then as part of each record, e.g. `BOOK RECORD`, so your application could then check that each book record starts with such a line or report an error if it doesn't. The possibilities here abound. (Of course, this assumes we're not worried about, say, the `title` of the book reading `"BOOK RECORD"` itself: think about it and see if you can visualize the file organization in your mind (or on paper) and observe that this would not pose a real problem. Only, possibly, a very minor one when the library file has been damaged ('corrupted') on disk.

Given the `.txt` file you mention and my mentioning of `\n` as a delimiter, this doesn't sit well with C, which expects its strings to be NUL terminated. Meanwhile, the human convention is to *not* write NUL characters to `.txt` files, so we can open them without hassle in editors like Notepad or vi.

So loading the library from file into memory is a bit of a challenge then. Of course, one can use `fread()` for this (a apply a few bits of cleverness afterwards), but `fread()` and `fwrite()` are generally reserved for use with 'binary file formats'; at least that's what a programmer reading thee code would initially expect. Check `fscanf()`, `fgets()` and friends: that's a hint.

Which still leaves the `struct book` layout: once you have those title, author and subject strings read into memory using *any* fstream approach, you still need to make sure those strings have a proper 'lifetime' and are pointed to by the pointers in your `struct book` instance variable before you can add it to your library in memory (RAM). 

> May I suggest you write a small program attempting to add a few books in memory to your library first? You're expected to use `malloc()` and `free()` as you'll be needing those for a lot of this stuff: anything that needs to survive beyond the function call where it was created is usually allocated on the heap, i.e. storage space is requested for these strings using `malloc()` or `calloc()`.
>
> I expect this will already be tough enough to get right initially, before you add file I/O to the mix.


---

# Analysis of `struct library` and commentary

Assuming you have been able to write a few (small) programs to create a single book structure in memory and load or store one to disk, we have a look at your `struct library` because there's something quite odd with it (I guess it's a typo):

```
struct library {
    struct book collection;
    int num_books;
    struct library *next;
};
```

reads to a human programmer as: this `struct library` apparently is **intended** to contain a *collection of books* (`num_books` entries in total) and apparently also points at the *next* library, so we're looking at the basics for a chain of libraries? Would that be a *linked list* of libraries?

But the very odd part is this: when `struct library` would contain (or *reference*?) a *collection of books*, wouldn't we need something similar to that 'trick' C uses to reference *strings*, i.e. *reference the initial element using a pointer* and then use *pointer arithmetic* to get at the subsequent entries? But `struct library` is specified as having a

```
    struct book collection;
```

which reads as: contains one *instance* of a `struct book`! Of course I can use C to get a pointer to that internal instance too, if I want (which is what `&` is good for, as in your `&thislib->collection`, but I (and the machine) won't know where the other `num_books - 1` book entries are stored!

How about this?

```
    struct book *collection;
```

That's more like it: when `char *title` is read as *pointer to `char`* and thus can be interpreted as *pointer to **first** `char` **in an array of `char`s** (a.k.a. a string)*, then the same would be valid for `struct book *collection;`: *a pointer to thee **first** `struct book` instance in an array of `struct book` instances*.

With C strings, we have that NUL sentinel to mark the end, rather than keeping a string length as a value somewhere (check out `strlen()` if you haven't already). Here we have a length: `num_books`, so you're supposed to be using that to keep track of the total number of `struct book` records in this particular library instance.

Hence I expected the `struct library` definition to look like this:

```
struct library {
    struct book *collection;
    int num_books;
    struct library *next;
};
```

because then it would read as something sensible to use as a part of memory *organization* for your library. (And apparently the exercise is hinting there may be more libraries loaded into memory at some time due to the `next` pointer.)

------

## Memory organization once again

Again, we need to think about how that `struct library` will sit in memory and how it will be able to get at the individual `struct book`-referenced strings specifying author, title and subject: we must be able to print those using `printf()` before we can assume we'll be successful comparing any book title against a given title in order to *delete* that book.

Also, you then need to reconsider how you remove a book from the array of books in memory. Do you need to adjust anything else? What if you delete *multiple* books, before you write the library back to disk?


# Spinning down...

Plenty questions left, but time is running out here and I think you're best served with slowly descending onto your original posted problem: write several small application codes for the parts as suggested in @f~~'s answer. If that doesn't go smoothly and swiftly, **backpedal** and try to write a small test program for each of the bits discussed above. When you do that, always first try to write a piece of code that does '1 struct instance', *then* follow up with another copy that does work for 2..N (arbitrary number of items): you'll only encounter your pointer problems (and learn from them) once you do that as well and think through the various ways of doing this. At this stage there many ways to skin the cat, from trivial to smart & convoluted, and each will hopefully add to your understanding of C pointers: they're fundamental to the language (and hiding underneath many constructs in other languages).

I hope there's no due date on the delivery of that exercise: there's plenty to learn and when it doesn't immediately \*click\* (which is the way it goes with most folks), *persevere* and keep the "Very Important Nitpick" section in mind: that part can be improved (it would probably fill a session back-to-back), but at least it's a start to prevent some horrible confusions I've met along the way.