CS 254: Data Structures Lab 0
Using the CodeWarrior environment; finding reference information


This lab exercise consists of the following parts:

Using cross-platform projects in CodeWarrior

This part is intended primarily for those of you who have never used CodeWarrior before. However, I encourage everyone to do this exercise. It will go quickly if you are familiar with the environment. If you do not finish in lab you should complete the exercises at some other time, but before the next lab meeting.

Before you begin I suggest you change the screen resolution to provide as much desktop area as you can stand. I like to use 1024 x 768. Of course, if you find it preferable to have a lower resolution so that the text and icons on the screen are larger, you can change the resolution to something lower.

Two cross-platform project stationery folders called mac_pc and pc_mac have been installed in the labs. Either allows a project to be used on a PowerMac, on a 68K Mac, or on a PC. mac_pc is a bit more convient for working on a Mac since its default target is 68K. pc_mac is a bit more convenient for working on a PC since its default target is x86. Some students must switch between one platform and another when transferring work between the labs on campus and a home computer. You may also find it convenient to be able to work either in the Mac lab or in the PC lab depending upon which is available.

  1. Launch CodeWarrior.

  2. Select Preferences... under the Edit menu. Select Build Settings under General. Make sure that Build before running is set to Always and that Save open files before build is checked. With these settings you won't have to save your files explicitely when you run the program. Running the program will automatically save any altered files and rebuild the project.

  3. Select New... under the File menu.

  4. Select the Project tab.

  5. Select SSU Stationery.

  6. Enter an appropriate project name. The choice of the name of the project has no effect on how it works. For this project you might choose the name Lab0Part1.mcp. Metrowerks uses a convention that the project name ends in ".mcp". This is particularly useful if you are using a project in a Windows environment, since it is the extension that allows Windows to associate the proper application. The name of the folder will be made the same as the name of the project, except that the extension ".mcp" will be left off. Thus for the name above the folder will be named Lab0Part1. You can always change the name later.

  7. Click the "Set" button. Use the file navigation dialog box to move to the proper folder. Under the Mac store your work on the hard disk. Under Windows store your work in the folder C:\Student. You should work on the hard disk, not on a floppy. Floppies are slow and relatively unreliable (you should never rely on a single floppy). You can transfer your work to a floppy after you quit CodeWarrior. The Create Folder option should be checked.

    If you plan to store your files on a zip disk, you should use the following guidelines.

  8. Click OK.

  9. Select the mac_pc project stationery and click OK.

  10. A window should appear with a message indicating that an access path for one or more of the targets could not be found. Close that window and ignore that error message. On a Mac there is no support for the x86 target, and on the PC there is no support for the PowerPC and 68K targets.

  11. Choose a target that is appropriate to the lab you are in. In a Mac lab choose PPC or 68K. In a PC lab choose x86.

  12. Select Enable Debugger under the Project menu.

    You could run the program without the debugger, but I want you always to have the debugger enabled. The debugger is your ally. You should become familiar with its features. Proper use of the debugger will save you time. It also catches fatal errors that might otherwise crash the computer and displays (often) meaningful error messages.

    Furthermore, if you ask me for help in debugging your program and you don't have the debugger enabled I won't discuss the problem with you until you have enabled the debugger.

  13. Run the program by selecting Debug under the Project menu or by clicking the Run/Debug button (the arrow) in the project window.

    Notice that there is a keyboaord shortcut for Run. You should be alert for keyboard shortcuts. When you find yourself doing something often for which there is a shortcut, it is time to remember the shortcut.

  14. In the debugger click the Run button on the toolbar (the button all the way on the left that has the shape of a triangle pointing to the right).

  15. On the Mac,

    If you are going to change the code you should make sure the application is terminated (either with Quit or the Stop button). If you leave the application running, return to the IDE, change a source file, and try to Run the program, all that will happen is it will resume the execution that was already in effect.

  16. Examine the source for the program. In the project window click on the triangle (Mac) or + (PC) to the left of the group named Source. Then double click on the icon for main.cp.

  17. Modify the source.

    Complete the documentation. Insert your name and the current date. Change the code adding a line to display "Lab 0 Part 1".

  18. Run the program again. Note that you don't have to save your changes explicitely. The CodeWarrior preferences settings is to automatically save changed files when you run.

  19. Quit CodeWarrior. Select Quit (Mac) or Exit (PC) under the File menu.

  20. Prepare to deposit the folder.

    Anything you deposit should be within a folder. Do not deposit individual program files. I recommend that you make a copy of the folder you have been working with and that you deposit the copy. That way you can use whatever naming convention you like for the work you are saving while still following my naming convention for folders you deposit. It also protects you against accidentally deleting a critical file from your working folder.

    The name of any folder you deposit should conform to the conventions for this class. It should consist of your last name, a dot, and the description of the assignment. For example, when Jane Doe deposits the first graded project the folder will be named "doe.project1". In this case since this is not an actual programming assignment, but simply an exercise in the lab, use "lab0" after the dot. For example, Jane Doe will name the folder "doe.lab0". Please use this convention. When I sort the deposit folder by filename, I expect to see the deposited folders in ascending order by student last name. Don't use your first name, your initials, or any other naming convention. If two students have the same last name they can append their first initial AFTER the last name (e.g. doej.lab0)

    Next make sure that you remove any files from the folder that are not essential. The essential files are the project file and all source files. If your program reasd data from a file, that file is be essential. All other files will be recreated when the project is opened and run.

    Move the inessential files to the trash. The size of the folder without these unnecessary files is substantially smaller than it is with them. When all the solutions of all the students in all the Computer Science classes are on the file server, the storage required might not be available unless the unnecessary files are removed.

  21. Open the directory Courses:CS254 on the CSserver file server. There you will find a folder named "Deposit Lab 0" into which you can drag your folder from the exercise above. For each lab or project there should be a different deposit folder.

  22. Check with the instructor that your file was correctly deposited and named.

  23. At some point you should place the folder on a floppy disk or a ZIP disk and test it on the "other" kind of computer. That is, if you are currently working on a Mac you should try it on a PC, and vice versa. In order to transfer files between the Macs and PCs you should use floppy disks or ZIP disks that are formatted for DOS (not for Mac).

Using multiple source files

  1. Launch CodeWarrior by double clicking the project file you created in the exercise above. If you plan to work on a program, I recommend that you always begin by launching the project. Open source files by first opening the project and then locating the source file in the project window.

  2. Enable the debugger (always!) and run the program. If you are on a Mac do this for both the 68K target and for the PowerPC target. Notice that you must enable the debugger separately for each target, and that the setting that enables the debugger is unset when you delete all the "unnecessary" files from the project folder.

  3. Now you will add a function to the program. The function you will add is the following:
    int cube( int n )
    {
        return n*n*n;
    }
    

    You will place this function in files separate from main.

  4. Create a file "mymath.h".

    Select New TextFile under the File menu. As soon as the file window appears, before you enter any text into it, choose "Save As" under the file window, navigate to the folder that contains your project, enter the name "mymath.h", and click Save.

    Repeat these steps for the file "mymath.cp". Always place files in the folder that contains the project before you add them to the project.

  5. Add the file "mymath.cp" to the project.

    There are several ways to do this. Right now you only need one. In the project window click once on the group name Source to select it. Then select Add Files... under the Project menu. In the dialog box select each file you want to add to the project and click Add. In this case the only file you want to add is "mymath.cp". You do not add header files to the project. Then click Done. When a dialog box appears giving a list of targets to add the files to, make sure that all targets are checked.

  6. Copy the documentation block from main.cp into each of the new files and adjust the documentation so that it correctly describes the files.

  7. In "mymath.h" add the lines:
    #ifndef MYMATH_H
    #define MYMATH_H
    
    #endif
    

    Header files should always have preprocessor guards against multiple inclusion, and you should get into the habit of adding them as soon as you create the header file. If you follow the convention of using the name of the file for the guard symbol (e.g. mymath.h uses MYMATH_H) then you will never have the same symbol used in two different header files.

    In "mymath.cp" add the lines:

    #include "mymath.h"
    

    This is enough new code to justify compiling the project. Run the program and make sure it still works.

    The text that you have just added to these two files is straightforward boilerplate. Whenever you create a code file and header file pair you can add the documentation, the macro guards, and the #include in the code file without stopping to think about the real purpose of these files.

    You may feel that you haven't made any substantial change to the program, so it is premature to run it again. I would like you to get into the habit of adding no more than five to ten lines of new code to a program before you check it. To make this approach worthwhile the five or ten lines you do add must leave the program in a compilable and runnable state.

    If there are any errors, now is the time to fix them.

  8. Insert a prototype into the header file, the code for the function into "mymath.cp", the line #include "mymath.h" in main, and a line in main that will exercise the function. These together constitute six additional lines of code, so afterwards it is time to run the project and get it to work.

  9. You did put documentation into the header file as part of the function prototype, didn't you? The temptation to code first and document later is great. Resist it. If you didn't include any documentation with your prototype, go back now and add it.

  10. Do not deposit your solution to this part. The first part is the only part you will deposit today.

Using the editor

Most of the features of the CodeWarrior editor are those that are found in any Macintosh editor. There are a few features, however, that are particularly useful for programming. These are

  1. Copy the folder "Lab 0" from the CS254 folder on the Courses file server to the hard disk of your computer. Inside you will find a folder containing the project "buggy1.mcp".

  2. Double click on the project file "buggy1.mcp" on your hard disk and open the source for the program.

  3. Position the cursor just after the opening brace of the first if statement and select Balance under the Edit menu. Balance finds the nearest matching braces that enclose the cursor.

  4. Position the cursor after the second left parenthesis in the condition for the first if and select Balance. Balance will also find matching parentheses.

  5. Again position the cursor after the second left parenthesis in the condition for the first if. Select Balance, then select it again, then again. Each time it expands to the next enclosing braces, brackets, or parentheses. Note that the third time you select Balance the selected range does not expand as you would expect (to encompass the entire block of main). Instead it beeps. This indicates an error in the source code.

  6. Position the cursor just before the final brace of main and select Balance. Notice which braces it matches. One of the braces in the else clause faces in the wrong direction. Fix it. Now balance should expand properly to the entire block of main.

  7. Now select the two lines between the braces of the else statement. These lines are not indented properly. With these two lines selected, select Shift Right under the Edit menu. Shift Right shifts the selected lines one tab position to the right. Shift Left shifts the selected lines one tab position to the left. Of course you always indent text with tabs, never with spaces!

  8. Enable the debugger (always) and run the program. After you have verified that it works properly, examine the buttons at the upper left corner of the editor window. One of them shows all the header files that are directly or indirectly included by this source file, and another shows all the functions defined within this source file. Both are pop-up menus and you can navigate directly to an included header file or to the position of any function within the source file. In this small program there isn't much need to navigate quickly to a function, but in a much larger file it might be helpful. Try out this button with the functions root1 and root2. By the way, these buttons don't work until the file has been compiled successfully.

Using the preprocessor

Normally preprocessor and the compiler are both run in sequence. The preprocessor reads your source file and pipes its output directly into the compiler. Sometimes it is helpful to examine the output from the preprocessor phase alone.

  1. Double click on the project file "buggy2.mcp" in the folder of that name on your hard disk and open the source for the program.

  2. Enable the debugger (always) and run the program. It has an error. The error message is certainly peculiar, and you may not see anything wrong with the program. What you are looking at is not the code seen by the compiler. Your source code is first passed through the preprocessor. It is often helpful to examine the code AFTER it comes out of the preprocessor and BEFORE it goes into the compiler.

  3. Open the source file and select Preprocess under the Project menu. This displays the result of passing the source file through only the preprocessor. The first part of the display is from the #include and the other header files included by iostream. The program itself is at the very end of the display. The preprocesser embeds comments into the preprocessor output that indicate from which header file the various source lines are taken.

    Scroll to the bottom of the display. Look carefully at the line that caused the error. You can see that it contains unwanted semicolons. You can probably see now that the statement that #defined PI should not have had a semicolon at the end. Remove the semicolon and run the program.

    Actually this is not the proper way to define the constant PI. Instead you should use:

    const double PI = 3.14159;
    

This is a good time to notice that the preprocessor is not restricted to the manipulation of C or C++ code. In fact, the preprocessor manipulates text files regardless of their content. To see this, create two files named "file1.c" and "file2.x". In "file1.c" enter the following lines:

this is line 1
#include "file2.x"
#ifdef ASYMBOL
this is inside the ifdef
#endif
and in "file2.x" enter the following lines:
this is in file2.x
#define ASYMBOL
Open "file1.c" in the editor and preprocess it. You can see that the preprocess follows the preprocessor directives, and that the fact that nothing in either file is valid C or C++ matters at all to the preprocessor.

Using the debugger

The debugger is the most effective tool for discovering how a program is actually executing. You should know how to use the debugger, and you should use it. I expect you to enable debugging for every program you execute.

Here are the features of the debugger that are fundamental and which you should be able to use effortlessly:

  1. Double click on the project file "buggy3" in the folder of that name on your hard disk.

  2. Enable the debugger (always, always, always) and run the program.

  3. Run the program by clicking the Run button. You will find it helpful to arrange the windows so that as much of the console window as possible is visible when you run the program. On the Mac the console window cannot be repositioned on the screen, so you have to move the debugger windows to the edges of the screen.

    Notice that you only get to play one round, even though the program is designed to play three rounds. Can you see the problem? Whether or not you can, you will use the debugger to find the problem.

  4. You have already used two of the five buttons on the debugger toolbar - the one to run the program and the one to stop the program. The other three buttons are used as follows:

    The braces with an arrow pointing to the right completes one statement even if that statement involves calls to functions. In the Control menu of the debugger it is called Step Over.

    The braces with an arrow pointing down will execute one statement if that statement involves no function calls. If it does involve function calls, it steps into the function instead. In the Control menu of the debugger it is called Step Into.

    The braces with an arrow pointing up executes statements until a return from a function is executed, and then it stops. In the Control menu of the debugger it is called Step Out.

  5. Run the program again and instead of clicking Run use the Step Over button to step through the program. Notice that when you step into a loop the debugger first jumps to the end of the loop and then back to the beginning. When you get to the statement that calls the play function, notice it that executes the entire function call.

  6. Run the program again. This time use the Step Into button. Within a couple of clicks you will find yourself staring at assembly code. There are a lot of functions called by a C++ program that may not be evident at first (or even second) glance from the source code. In this case you stepped into the constructor for the string class. To get out of it click on the Step Out button. A little while later you will step into the function that performs the << operation on cout. Step Out of that function.

  7. Enough of this. Don't step through the entire program using Step Into. Instead use Step Over until the arrow points to the statement that executes the play function. The arrow always points to the instruction that will be executed next. Click Step Into to enter the play function. First the debugger enters the copy constructor for the argument. Step Out and Step In again to find yourself in the actual function.

  8. This is a pretty tedious way to get into the play function. If that is where you want to be to examine the state of the program, it is much faster to use a breakpoint. Restart the program so that the arrow points to the beginning of main.

    Click the dash just to the left of the statement that calls the play function. This sets a breakpoint just before that statement. Now click the Run button. The debugger stops at the breakpoint. Now you can Step Into the function if you like.

  9. If you really want to stop the program somewhere in the play function, you want a breakpoint set inside that function. However, when you start the program you see the source code for main. Return to the project window and double click game.cp to see this source file. There are dashes in the left margin that allow you to set a breakpoint in this file. Set a breakpoint at the first dash of the function play, and then Run the program. It should stop at that statement, and now the main debugger window should display the code from game.cp.

  10. Clear the breakpoint by clicking on it to change it back to a dash. Then rerun the program.

  11. Now that you know how to control the debugger's execution, you need to learn how to examine variables. The variables are shown in the Variables panel of the main debugger window. You may find it convenient to enlarge the Variables panel by dragging the vertical separator between the Stack panel and the Variables panel to the left. Once the Variables panel is larger you can drag the separator between the variable names and their values to the right to expose the full variable name.

  12. Each variable is shown with its value. The mac_pc and pc_mac projects by default also show the types of the variables. You can control whether the types are shown. Select something in the variable pane and then check or uncheck Select Show Types under the Data menu.

  13. Initially the variables have random values. Watch the variables as you Step Over a few times. You can see games_played acquire the value 0 and game_over acquire the value 'false'. You don't see NUM_GAMES at all, and there is no dash to the left of the statement that assigns it the value 3. This is because NUM_GAMES is a const. The compiler doesn't store it in a location in memory as it would for a variable. It simply substitutes the value 3 wherever NUM_GAMES is used in the program.

  14. Continue until you read in a value for user_tool. You would like to be able to examine the variable user_tool and see the string you just entered from the keyboard. Since user_tool is an object of the string class, all that appears in the Variables panel is its address. To see its contents you must click on the triangle to its left. This displays the fields of the class. Open up alloc, then m_. The field named data_ is the contents of the string.

  15. The time has come to fix the error in the program. Step until you come to the expression that increments games_played. Watch the values of the variables carefully as you step. The value of games_played will increment to 1. When you execute the if statement the value of games_played changes again! This is unexpected. This causes you to look closely at the statement, and you see that the operator is assignment (=), not equality (==).

    There is an important lesson here. When you are using the debugger to find a subtle error, you are looking for something unexpected. You naturally think the code you wrote is correct, and if it had been possible to find the error by staring at the code you would have found it already. If you step through the program casually you will not see what the program is really doing - you will see what you expect it to do. Therefore, before stepping you should decide what changes in the variables you expect the next statement to have, and then after you step you should examine the variables to verify that the expected changes happened. Similarly if the statement in question involves a branch or loop, you should decide before you step where in the program the control should shift, and then verify that it actually did so when you step.

  16. Actually I created this problem artifically. In this project I disabled all warning messages. Reenable them and recompile the program to see what assistance the compiler would have provided. To enable warnings select 68K Std C++ Console Settings... (or PPC Std C++ Console Settings or x86 Std C++ Debug Console Settings) under the Edit menu and then select the C/C++ Warnings panel. Check all the boxes in the list except Implicit Arithmetic Conversions and Non-Inlined Function. Don't check the one labelled Treat All Warnings As Errors. To force the compiler to recompile the program and display the warnings, in the project window click to the left of main.cp in the column beneath the check mark and then run it again.

  17. There is more to know about the debugger, but you can learn it as you need it.

C/C++ Standard Library

Two libraries are available with C++. One is the standard C library, the collection of functions that are always available with C. The other is the standard C++ library. To the extent that all C++ environments conform to standard C++, they will all have the same library functions and classes. These libraries provide features that are very commonly needed for programs, and these features are implemented in a robust and efficient manner. You should learn what features are provided by the libraries.

Using a feature provided by the library is quicker and simpler than coding it yourself, but knowing what is available in the libraries and how to use it can take more time than you want to devote to it when you are up against a deadline to finish a program. Therefore you should not wait to learn about the libraries until you desparately need them.

In the CS Department web pages there is a link to a web site with a well organized discussion of the C and C++ libraries. Bring up the CS Department web pages. Click on Other, then on Useful Sites, then on C/C++ Standard Libraries. There is too much material here to go through systematically during this lab. At your leisure you should study this site. Even if you don't learn the details of all the libarary routines, at least learn the organization of the libraries and how to navigate this web site.

For now concentrate on one aspect of the C library and one of the C++ library. In the C library find information about <ctype.h>, and in the C++ library find information about <string>. Don't be dismayed at the complexity of the C++ library documentation. This is a powerful library, and the documentation is somewhat confusing. In particular you should examine the documentation for the c_str member function and for operator[] (described under basic_string).

THINK Reference

This part can only be done on a Macintosh, not on a PC.

THINK Reference is an application that accompanied the Symantec C++ compiler, an environment we used to use before CodeWarrior. It is an on-line database of information about C and C++. It is limited in the range of its information and it is old enough that many new features of C++ are missing, but it is still useful because it is easily invoked from within the CodeWarrior editor.

  1. Double click on the project file "buggy4" in the folder of that name on your hard disk and open the source for the program.

  2. Enable the debugger (always) and run the program. It has an error that prevents it from compiling. There is something wrong with the argument to the function ctime.

  3. Select the name of the function by double clicking on the word "ctime". Then select Find Reference under the Search Menu. This launches THINK Reference and searches for information about ctime.

  4. Using the information shown in THINK Reference, fix the error and get the program to run. Take a few minutes to examine the range of information available in THINK Reference.

If you are not using a Mac you cannot use THINK Reference. Instead use the C Standard Library documentation available through the CS Department web site to find documentation on the ctime function.

CodeWarrior on-line documentation

CodeWarrior comes with volumes of on-line documentation about the environment (IDE), the compilers, the linkers, the debugger, the libraries, and so on. Mostly you won't need this documentation. Often for topics about the C++ language itself there is clearer documentation (e.g. the web site above for the standard libraries). Still, if what you want to know about is unique to CodeWarrior you will have to seek it in the CodeWarrior documentation.

To focus your examination of this documentation, look for a description of conditional breakpoints in the Debugger User Guide. A conditional breakpoint is a breakpoint that doesn't stop unless some condition is satisfied. As an example of the need for such a feature, suppose you wanted to stop at a statement inside a for loop, but you wanted to stop only after the first 999 iterations of the loop. It is unreasonable to step through the loop 999 times by hand. Instead you want to set up a breakpoint with the condition such that the loop index is equal to 999.

When you have found the description of this feature in the documentation, write a short program that loops 1000 times and try stopping inside the loop after 999 iterations.

Putting it all to use

Write a C++ function (not a class) that accepts a string and returns a new string in which the first letter of each word is upper case and the rest are lower case. That is, if RiChArD gorDON is passed to the function then it returns Richard Gordon.

You should (among other things, and not necessarily in this order or all at once):


12/30/02