The Panic Page
The Panic Page is a place to come to see if the cause of your frustrations have already been hashed through by someone else. This should become the first place you come for assistance as I hope to post the results of conversations and e-mails with students here if I feel that others may benefit. Rest assured, any such post will be completely sanitized of any identifying information.
18 MAR 03 - Course going to 3 credit hours? Can I get the extra hour of credit?
Word appears to be getting around that, starting in the Fall, this course will be worth three credit hours. We are starting to get a few inquiries about this and they seem to reflect a misconception. One question that has been asked is why can't you, the students taking it now, get the extra hour of credit. I think this reflects a tendency to think that the work in this course is too much for a two credit hour course and I think this has led people to assume that a change to three credit hours means nothing more than the same course with the same amount of work but worth 50% more credit. This isn't the case and, in addition to setting the record straight on this issue, I would like to take this opportunity to point out some of the common reasons that people spend a lot of time on the programs for this course - more time than they really should need to.
Too much work for 2 credit hours
We often hear the complaint that people are putting is as much time on this course as they do for their three credit hour courses. Well - good. That's the way it should be. Consider how much time you will be putting into this class in the last five weeks of the semester - none! For the ten weeks that this course meets, it IS a three credit hour course. It is unreasonable to expect the work pace to be less than that for a three credit course AND for the course to only last 2/3 as long. One or the other - not both.
In fact, it is not unexpected or out of the ordinary for you to be putting in more time for this course than for many of your three credit hour lecture courses. Programming courses, like lab courses, have always required more time than a normal lecture course worth the same number of credits. That's the nature of the beast and reflects the amount of time it takes to learn a skill as opposed to the amount of time it takes to learn information.
Too much material covered
If you think that the amount of time you are spending now is a lot, consider that three years ago students had to write multiple programs (three or four on average) every two weeks AND we covered more material - we covered macros and recursion which we are not covering now. Plus, students had to work all of the Exam Practice problems at the end of each chapter plus submit hard copies of their code and pseudocode along with additional work showing the application of the book's Five Step Problem Solving Method for each problem.
Takes too long to write the programs
Each of the programs this semester are quite tractable and can be solved in a reasonable amount of time - IF the problem solving approach that we have tried to emphasize - in the assignment write-ups and website at least - are followed. I can sit down and, from scratch, completely code most of the homework assignments in somewhere between fifteen minutes and slightly over an hour each. That certainly does not mean that we expect the students to be able to do that, but being able to write your programs in five hours or so should be quite doable. From working with a number of students as they have struggled with their programs I have seen the same pattern time and time again - namely a refusal to apply the divide-and-conquer approach that we have said is so important. I will sit down with a student and they have their entire program written and coded yet they have syntax errors in the first few lines of code or logic errors that prevent their program from even opening the data file. Now they are confronted with debugging three or four pages of code instead of six to ten lines of code. Worse, their code reflects the disadvantages of trying to solve an entire problem all at once instead of breaking the problem down into specific, well-defined tasks and then tackling each task in turn.
What you will probably find if you look carefully is that you are making the course much harder than it needs to be. This is a common theme that we see every semester and highlights the adage that the burnt hand teaches best. There is only so much advice we can offer and we can't force people to use that advice. I've gone to great lengths to reveal the common pitfalls that many students fall into and how to avoid them and, each semester, a large fraction of the students choose to ignore them and then proceed to run right into them. For instance, after emphasizing how important it is to always verify that a file opens successfully before using it, I will have numerous students come up, after spending hours trying to figure out why their program isn't working, only to discover that their fopen() operation failed and, since they didn't bother to check, things only got worse from there. The attitude (sometimes flatly stated) was that they intended to throw that "extra stuff that we require" on at the end before they turn it in so that they don't lose points. I have a hard time developing a lot of sympathy for the amount of time spent on the homework in situations like that.
Just a couple of weeks ago a student came in asking for some help and one of the things I strongly recommended was that, when checking for equality against a constant, that they put the constant on the left hand side of the equality operator specifically so that if they make the common mistake of using the assignment operator instead of the equality operator that it will become a syntax error, and hence caught by the compiler, in addition to just a logic error. They said, "But that looks odd." I agreed, it does look odd at first, but pointed out that the choice was between "looking odd" and being able to save lots of time otherwise spent tracking down some very subtle logic errors. Two hours later they came back, having made no headway with their program at all, and the problem was that they were setting their file pointer equal to NULL instead of checking to see if it was equal to NULL. They had made the exact mistake I had cautioned them about only minutes before they made it - but they had ignored my advice because "it looks odd". What can I do? Similar tales concerning integer division repeat themselves numerous times each semester - which is why it is a recurring theme on the quizzes and exams.
There are some things that people just have to learn by paying the price of choosing not to listen to the people that have made those same mistakes before them - and I learned many of these same lessons the exact same way. After having been told about the need to clearly identify, in the comments, which pointers need to be allocated memory at run time and making sure that the memory is allocated, I elected not to explicitly identify them - I couldn't be bothered since the program was due the next day - and, as a result, was working with a pointer to an array that hadn't been allocated sufficient memory. It took me twelve non-stop hours to track down. Had I listened and applied the offered advice, it would have taken a few minutes. But does that mean that the program assignment itself was unreasonably long? No. It means that I chose to make it take an unreasonably long time to complete because I chose to ignore the advice that had been freely given. I wasn't happy about it - but it was one of my more useful and effective, not to mention memorable, lessons about the art of computer programming.
Course going to three credit hours
As far as the course going to three credit hours - that is both true and false. This course, ECE-1011, is remaining two credit hours but is being discontinued. A new course, ECE-1021, is being added in its place that is three semester hours, lasts all semester, and has correspondingly more material and more work. Furthermore, the nature of the course will be undergoing a fundamental shift and the focus will be primarily on Engineering Problem Solving, something that we are forced to neglect in the present ten week format. While still being developed and therefore subject to change, the students will be working on both programming and nonprogramming problems and the programs they write will be more sophisticated including command line arguments, the use of structures in most of the programs, the introduction and use of macros and recursive approaches very early in the course, and probably some type of class project where students work to implement specific portions of a much larger project. Clearly there is really no question of receiving an additional hour's credit for ECE-1011 under these circumstances.
14 MAR 03 - Stack Size limitations in Turbo C and how it affects your HW#6 program.
Even if you don't use Turbo C - remember that the grader does!
Like so many things, every C compiler is free to organize its memory usage pretty much as it sees fit. Turbo C chooses to place all "automatic" variables ("automatic" means that memory is allocated and deallocated for them automatically at run time - local variables are automatic variables) in a portion of memory called the "stack" - another area of memory is called the "heap". With most compilers, dynamically allocated memory is placed on the heap.
The amount of stack memory available is limited and many other things are placed on the stack as well. The Turbo C compiler provides for 4096 bytes of total stack memory and size a variable of type double requires 8 bytes and since each data record from the rocket data file has two doubles associated with it, there is only enough stack space available to store 256 records of data from the file - and that's if every single byte of stack memory was devoted to storing data records which is not the case.
There are (at least) five ways to get around this.
1) Write your program so that it only works with a small subset of the data - such as 128 records - at a time. This is cumbersome but doable and, in the "old" days and even today with some systems or some applications this is still required.
2) Dynamically allocate the memory you need from the heap. This is the best method and, if insufficient heap memory is unavailable, you have little choice but to go back to option #1.
3) Use a compiler that doesn't have this limitations - for instance, Visual C appear tolerant of fairly large local arrays.
4) Get your declared arrays to be stored someplace other than the stack. You can do this, at least in Turbo C, by telling the compiler that they are not automatic variables. You can do this with the "static" key word which tells the compiler that this data is to be stored off the stack and is to remain viable for the entire time that the program is running. To declare a static variable you simply precede it with the "static" keyword when you declare it - after that everything else is the same.
Example:
static double myarray[2000];
5) Another way to get the data stored off the stack is to make it global - global data is stored on the heap. This is highly frowned upon for the simple reason that global variables in general are highly frowned upon and should only be used if there is a very good reason. If this were the only way - given the constraint that you haven't been introduced to dynamic memory allocation yet - to get an array large enough to work hold your data then it would be considered a very good reason. But, since you have the ability to employ method #4 - declaring the arrays locally but as static variables - you do not have a good enough reason for making them global.
23 FEB 03 - Exam Review Clarification
It should be noted that when the Course Policies page and the Exam Review sheets were put together, that the Quiz Prep Sheets did not exist - they were added for your benefit to aid more narrowly focused studying. Much of the information on the Quiz Prep Sheets came from the previous Exam Review sheets, but quite a bit of new material has also been added. These Quiz Prep sheets should be considered as part of the Exam Review sheet - in particular, when it is stated that at least 25% of the points on the exam will come verbatim from the review material, the source material for those questions are the Quiz Prep sheets and the actual Exam Review sheet. Don't forget that the Exam Practice problems located in the book are also included in this list by way of being explicitly mentioned on the Exam Review sheet.
Remember that you may print out the Exam Reference page and bring it to the exam. Do not write on the front of the sheet but you may write anything you wish on the reverse side. If you bring one, you will be turning that sheet in along with your exam. You are not required to bring one, but note that one will not be included as part of the exam.
22 FEB 03 - HW#5 - Starter code available on the website
You can download the source code (the main() is virtually identical to what is in the homework assignment example) and use it as the basis for your code. There are a couple of minor bugs in the program. See if you can find and fix them - consider the price you pay for getting the main() and all of the function prototypes and compilable and runnable "hand example" code for each of the functions for free.
A couple of additions from the code in the homework assignment. There is a PrintHeader() function:
(1) Take a look at what it does and notice that it has no program-specific information contained in it at all - it's output is modified entirely in response to the #define constants at the top of the program. So this function can just be copied and invoked in all future assignments without any need to touch it again.
(2) Notice that some of the variables are now declared as "long int". Why is this so? This is directly related to some of the comments in the homework assignment.
Notice that this code, as it is presented, is a direct example of Top-Down algorithm development. The entire main() program has been completed even though none (except for PrintHeader() ) of the underlying functionality has been implemented. Yet we can test and debug our main() even without this by implementing "hand example" code in each of the functions that returns something simple - sometimes its a constant (such as the number of games to play) and sometimes is a a simple random number (such as whether the game was won or lost). We could now give this code to different people and have them develop the functions independently - because all of the interaction between them has been established (except that the rand_int() function will probably be called by the PlayMontyHall() function, but even so the I/O has been established).
18 FEB 03 - HW#4 - Remember to Divide and Conquer!
Remember to apply the problem solving method that you are supposed to be learning in this class! Do not try to write this program all at once and then compile it and then figure out why it doesn't work. Break the assignment into a bunch of progressive steps and tackle them one at a time. For instance, you might get started by using the following strategy:
1) Take your program for HW#3 and simply add the ability to generate the output file correctly. Leave everything else alone until you get that new feature working properly. Since you aren't opening any files yet, just hard code the output as though the opened successfully.
2) Add the necessary code to be able to open the three files, check for success, place the proper lines in the output file and exit if necessary. Then test this by intentionally having bad filenames or the files not being there (rename them to something else).
3) Then, since you know you will be reading the values that describe the envelope from a data file, you need to convert your corresponding #define constants to variables. So go ahead and do this part and make them variables that are initialized to the same values that the #define constants were. Then run your program and see if this messes anything up.
Keep proceeding like this - adding and debugging one feature at a time. And feel free to keep breaking each new feature into smaller and smaller pieces. That's the whole idea. If you approach the problem this way, you will find that you spend a lot less time overall then if you try to tackle it all at once simply because your development efforts are more focused and your debugging efforts are more efficient.
17 FEB 03 - Example Code for HW#3 appears to crash - but it really doesn't.
The example program from HW#3 that is available on the web site appears to crash after you type 'n' or 'N' telling it that you have entered all of the data. What is actually happening is that the program finishes running and exits so quickly that it only appears to crash. The reason is that it is a Console Application compiled with Visual C and when you execute it from within Windows the operating system recognizes it as a Console Application and automatically opens a Command Console (DOS window) for it to run in. Once it is done running, the operating system automatically closes the Console Window. You can avoid this by saving the .exe file to your local drive, opening up a Command Console manually, and then running the program from the command prompt. For those of you that write your code using Visual C, the reason that you do not see this behavior when you run your programs from within the Visual C environment is because, in that case, the environment (called the IDE for Integrated Development Environment) is the process that opened the console - not the operating system in direct response to your program being launched. So the IDE has control over when it gets closed.
Turbo C handles this differently in that it actually inserts additional code so that your program launches a simple graphic window (not the command console) and this configures it so that the window has to be closed by the user manually.
Both HW#3 and HW#2 (which had been recompiled using Visual C this past weekend) have been modified to explicitly ask the user to hit a return in order to exit the program. Since the HW#2 source code is on the website, you can check it to see how this was accomplished.
16 FEB 03 - "Condition Always TRUE/FALSE" or "Unreachable Code" warnings.
If you were to write an if() statement like the following:
if( 1 < 0 ) printf("Impossible Condition.\n");
The if() test will never pass and the printf() statement will never get executed and is therefore referred to as "unreachable code". Since the if() test is comprised entirely of constants, it can be evaluated by the compiler at compile time and the fact that it will always evaluate the same way is recognized by the compiler and a warning flag is raised and reported to the programmer since writing code like this is almost always the result of a logic error.
But what if you had written the code as follows:
if( MYCONSTANT1 < MYCONSTANT2 ) printf("Impossible Condition.\n");
where the two constants are defined using #define statements? As far as the compiler is concerned, nothing has changed. The preprocessor replaces the occurrences of the words with their corresponding values and the expression still reduces to one that can be evaluated by the compiler at compile time and which will always pass or always fail as far as that particular compilation is concerned. So the compiler will still issue the same warning. But in this case, what you have done is created a situation where, depending on the values of these two #define constants, you are (albeit in a clumsy and inefficient manner) controlling whether the code associated with the if() or the code associated with the else (if any) gets compiled. If this is the behavior you want, then you may ignore the warning recognizing that the compiler can't read your mind and that this is an example of you using a trick that, in most cases, is an inadvertent error.
It turns out that there is a cleaner and much more preferred means of selectively compiling portions of code based on the values of #define constants using specific preprocessor directives that are beyond the scope of this course. Using these directives would avoid these types of warnings.
06 FEB 03 - HW#2 Output Explained in more detail.
There appears to be quite a bit of confusion regarding the output of HW#2 - above and beyond the typo in the example output for the forward CG limit. We will try to help clear up some of this confusion, but part of this assignment is for you to identify that a somewhat tricky situation exists and to figure out how to solve it - i.e., apply engineering problem solving skills.
First and foremost, do not forget that HW#2 was generated without using ANY
if() statements or other control structures in the
code at all - so all that program could do was print information out, indicate
under what conditions that information was applicable, and let the user chose
which line of the output applied to them. That was an artificial constraint that
only existed because it was too early in the semester and we hadn't covered
these program flow control structures yet.
The reason for the odd looking output for the AFT limit is because the aft
limit, for a T-41B, is a constant (a vertical line all the way from minimum
gross weight to maximum gross weight). It lacks the breakpoint that appears at
1950 pounds in the forward limit of the envelope. The 1950 pounds is the weight
at which the FORWARD limit changes character. Below that weight it is a constant
and above that weight it begins to shift backwards with increasing weight. There
appears to be this strong tendency to use that 1950 pound weight in order to do
something with the aft limit. Look at the CG envelope! What happens to the aft
limit at 1950 lb? Nothing! If the aft limit DID have a break in it, it might or
might not occur at the same weight that the breakpoint in the forward limit does
- most likely it would not.
As was explained in the reading assignment, the AFT CG limit is a fixed value
for many, but not all, light aircraft. But your code has to be
able to work correctly with BOTH types of aircraft and it has to be able to do
that based upon values in a set of #define constants located above your code. So
you want to be able to treat the case where you have a single fixed limit as a
special case of the more general situation where it is fixed below some
breakpoint weight and slopes linearly inward above it. How you do that is up to
you.
The output in the HW#2 assignment (which is specifically offered as a suggestion
and you are free to present your output differently) deals with
this a certain way. And don't forget that the output from HW#2 is going to be
clumsy because it doesn't use any if() statements.
05 FEB 03 - Source Code for HW#1 posted.
If you go to the index page you will find that the source code link for HW#1 is now active. This is the file that was used to generate the executable file that was previously (as still is) linked to as an example.
Hopefully you noted a few things while working on this assignment. Probably the most valuable is to not reinvent the wheel when you don't have to. Since the homework assignment contained nearly all of the output text that your code was to generate, you could have (and hopefully did) save a lot of typing by copying the expected output text from the homework assignment, pasting it into your code and then adding a bunch of printf(); statements around each line (again, using a cut and paste method).