Questions in this FAQ:
A. There are 2 primary newsgroups where you can post questions: comp.lang.c and comp.lang.c++. If you find the newsgroup helpful, you should also try to answer questions there, as it will also help you learn. Just make sure your answer isn't wrong, otherwise someone else will!
A. If memmove is not working for you (it's in string.h and/or memory.h), or you have to add a typecast to the return of malloc to avoid errors, then it sounds like your not using an ANSI standard compiler. Under UNIX, you usually have several C compilers. The cc compiler is most often K&R (Kernighan and Ritchie, the large-foreheaded brainiacs who created C) C, and older, non-standard C. At Morgan Stanley, you also have Eden C which is ANSI standard. You should be using Eden, but there is usually several steps involved to set up your login so that the environment will work correctly with Eden C. At the time of this writting, I am not sure what those steps are.
A. Basically, a memory leak is when you allocated some memory, but forgot to free it. In most cases, you should follow the flow of the program, and make sure that each allocation has one (and only one) free. In some of our structures (like the String structure), there are functions that are a little more complicated. The StringFromStr function for example.
In StringFromStr, the function's job is to store a C-style string in our String structure. In order to do this, it probably needs to create a new buffer of the correct size. But, don't forget that there may have already been a string in the String structure. Before you assign the new one, make sure you free any previous contents it had.
A. It's true (in most operating systems, in normal conditions) that when your program terminates, any files you had opened, and any memory you allocated is automatically released. But, it's still good practice to clean up after yourself. Part of the reason is because you don't know how the code you write will be used in the future (always look at the big picture).
For example, image you write a function that loads a large file into memory, and prints it to the console. If your writing a program to do just this, then there really isn't much of a problem. But, now image your getting into writting internet software, and your writting a web server, which needs to open a file, and display it to the console (which goes to the client's browser). This is a program that may be running for days, weeks, or months without terminating. Now the memory leak that wasn't a problem in the original program turns into a bug that causes the server to crash every few weeks. This type of bug (very intermitten, and based on code with no prior problems) is very difficult to track down.
A. Since this is a data structures class, we are no longer really learning C. We are now learning how to apply C to solve problems. In order to do this, we design a data structure and functions that help us interface (use) those data structures. I refer to the data structure, the supporting functions, and the concept/implementation of the 2, as a 'framework'.
A framework should be a well thought out, documented, and implemented 'package'. It is no longer just 1 or 2 functions like in introduction-style classes. In the future, you will find that the concepts we are building, tie into Object Oriented Programming techniques and C++
Q. In the String structure, you said that empty strings should be stored as a NULL. What's up with that?
A. First, remember that the String structure (like all our structures) represents a framework. If a String structure contains an empty string, then it would be a waste of space to keep track of just the single null terminator in the string. So, in this framework, we have come up with a better ida. Rather then store the null terminator, we will just set the Buffer pointer in the String to NULL. While this will save space, we now have to tackle problem of treating NULL as an empty string ("") in our code.
The StringStr function was designed to handle this problem. The purpose of StringStr is to provide a standard C-style string from our String structure. Normally, if the String structure's Buffer pointer actually points to a valid string, then we can just return that pointer. But, if the Buffer is NULL, then we should return "". The StringStr macro would look something like:
#define StringStr(x) ((x)->Buffer==NULL?"":(x)->Buffer)
Now, any function that needs a standard C-style string from a String structure can use this function:
String * Test;
Test = StringConstruct( "A test" );
printf( "Test is \"%s\"\n", StringStr(Test)
);
A. Depending on where it's discussed, you will find that the roles of the Head and Tail in a queue are reversed. What's important to remember, is that one of them is where data is inserted into the queue, and the other is where data is removed from. As long as the implementation (the code) of the queue consitantly implements the logic, it doesn't matter if head points to the front or rear of the queue.
A. Just to keep it simple. If you have completed the Stack, or Queue, and wish to go one step futher, then by all means make it dynamic in size. For both items, you simply need to change the functions which add data to the item (StackPush() and QueAdd()). But, note that making the queue dynamic is slightly more involved than the stack because of the 'circular' storage method used. In both however, a call to realloc() is the key to making them dynamically sized. Also, don't change any of the original functions' definitions, so that backward compatibility is maintained (you might be tempted to remove the Size parameter from the construct functions, since it isn't really needed anymore).
In discussing a linked list ( a dynamically sized item, by definition), we will see how to make a linked list act like either a queue or a stack.