By the time you have completed this lab, you should be able to
The Computer Science Department's Curriculum Committee wants each student (individually) to fill out and submit the Undergraduate Curriculum Assessment Survey #1. It should only take a couple of minutes for you to complete. Please do that now, before going on to Step 1.
Partner up (and remember to switch roles after awhile). If your regular partner is more than 5 minutes late, ask the TA to pair you with someone else for this week.
This lab's pilot should log in. You will (both) work in this account for the rest of the lab.
Create a ~/cs24/lab03 directory and make it your current directory:
mkdir ~/cs24/lab03 cd ~/cs24/lab03
Now copy all of the files for this lab from ~cs24/labs/lab03/ in the class account to your cs24/lab03 directory:
cp ~cs24/labs/lab03/* ~/cs24/lab03
With a simple program like hello.cpp, we normally compile and link in one step, and then it is ready to run like so:
g++ hello.cpp -o hello ./hello
But we can also compile the program without linking it. Type the following, and then
use ls
to find out what happened:
-bash-4.3$ g++ -c hello.cpp -bash-4.3$ ls hello.* hello.cpp hello.o
Notice the new object file hello.o
,
which contains the machine language instructions in binary form. In a second step we
would link this object file (just to standard libraries in this case) as follows:
g++ hello.o -o hello
That step will finally produce the executable file "hello" - but only
because we specified by the g++ option -o
that the output file should
be named hello. The program is run exactly like before by typing ./hello at the prompt.
When it comes to large projects in C++, it's always useful to organize your project into separate parts. When some parts of your program change, then only these parts need to be recompiled.
See the three files main.cpp, functions.cpp and functions.h. Notice that both of the .cpp files include the .h file - this is a typical case. It won't be necessary to compile functions.h by itself, as it will become part of both object files produced when the .cpp files are compiled.
Your job for Step 3:
Thought question: Why is it unnecessary to separately compile functions.h?
When first compiling your programs it's always good to use the "-Wall" and the "-g" options in order to force g++ to give you warnings about possible errors in the source code, and include extra debugging information in its output, respectively.
The -g option is also necessary in order to use the gdb debugger later in the lab.
If your program uses any of the extended C++ features of the C++ 2011 standard, then you must give the -std=c++11 option to g++ too.
Now imagine that you have a project with 20 different files, or even hundreds of files ...
Wouldn't it be better if we could compile all our files just by typing one command? Wouldn't it
be better and save us time if we have to compile only the files we have changed, and not all of
them? This is why we use the Makefile and the make
command.
Now let's write a simple makefile to easily compile the files main.cpp, functions.cpp, and functions.h that we used on the previous section. Open a text editor and name the file "Makefile" (or "makefile"). You should always name your make files in this way.
Don't just copy & paste this text! Be sure there is a tab character at the beginning of every command line (in this case, the three g++ commands and the rm command) - spaces will not work.
hello: main.o functions.o g++ main.o functions.o -o hello main.o: main.cpp functions.h g++ main.cpp -c functions.o: functions.cpp functions.h g++ functions.cpp -c clean: rm hello main.o functions.o
Save Makefile. Then make a minor change to one of the source code files (main.cpp, functions.cpp or functions.h). And finally type "make" in your console. Fix Makefile if it doesn't compile the changed parts and reproduce the executable file. If it does, then find out what happens if you type "make" again. ;-)
By default, make
will execute whatever is necessary to produce the first target which is
hello (the executable) in this case. Alternatively, you can specify the target you want make to
make by typing it as a command line argument, as in "make hello" to execute the default another way.
Suppose that now you want to remove the objects and executable file. What should you type in your console? Be prepared to answer that question if the TA asks you.
Maybe time to switch partner roles?
Inevitably sometime you will need to debug your code. gdb
is a very powerful
tool. Here we learn just the basics.
Look at buggy1.cpp to know its major parts.
(Emacs Users Hint: If you are using emacs you can see line numbers on the editor by typing M-x linum-mode. This will make your life easier. If you prefer running gdb on e-macs maybe you should try M-x gdb-many-windows in order to split your screen and be able to see variable values, gdb and e-macs all together. You gain control back to emacs by typing: C-x 0)
This program is supposed to ask a student about the courses he/she plans to register. So a non-buggy version of the program would execute like this:
Enter a course name: Computer Networks You are registered for the following 1 courses: 1. Computer Networks Do you Want to register for another course? (y/n) y Your Answer is:y Enter a course name: Computer Security You are registered for the following 2 courses: 1. Computer Networks 2. Computer Security Do you Want to register for another course? (y/n)
But this code has errors. Your job will be to find one of them using gdb. No need to fix it now, just find it. That's the purpose of gdb - it helps you find errors in your code.
Compile the code, and remember to compile with the -g option. Use this command:
g++ -g -o buggy1 buggy1.cppThen run it. Here is an example run:
Enter a course name: history You are registered for the following 1 courses: 1. history Do you Want to register for another course? (y/n) y Your Answer is:y Enter a course name: You are registered for the following 2 courses: 1. history 2. Do you Want to register for another course? (y/n) y Your Answer is:y Enter a course name: You are registered for the following 3 courses: 1. history 2. 3. Do you Want to register for another course? (y/n) y Your Answer is:y Enter a course name: You are registered for the following 4 courses: 1. history 2. 3. 4. Do you Want to register for another course? (y/n)
Hmmm... Well it seems that our program doesn't wait for our input after the first course name ... Let's try to debug it with gdb to see how the basic gdb commands work. Begin by starting gdb with buggy1 as its command line argument:
bash-4.3$ gdb buggy1
Then type "run" (or just "r") at the gdb prompt:
(gdb) run
The program starts and asks for user input. Push CTRL-C and write the gdb command to set a breakpoint at line 19 (You could also do the following by typing just "b 19")
(gdb) break 19
Enter "run" again, and then start the program from the beginning by typing "y" when asked:
(gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y
This way the program will run until line 19. Write the command "step" or just "s" in order to move one step forward. Now the program asks for your input:
Enter a course name: 20 getline(cin,courseName,'\n');
Don't give your input at this time. Instead move to the next step by writing again "step" or just by hitting enter (enter repeats the previous command you had typed). After you hit enter the getline function on line 20 is called and waits for your input. So you just have to enter a course name again - "Math" for example.
Let's say we would like to know if the courseName variable has the value we assigned to it. Just type:
(gdb) print courseName $1 = "Math"
So indeed (on this first iteration at least) variable courseName has the value we entered as an input ("Math"). Now use the step command to move forward up to line 29 ("userAnswer = trackUserAnswer();"). At that point, use the "next" command (or "n") in order to step into and execute the subroutine trackUserAnswer(). When you wish to exit from the trackUserAnswer function you can write (f)inish. (But be aware that this function needs user input. So you should enter finish just after you have entered your input.)
More about breakpoints: Write command break to put a breakpoint on line 29. Now you have two breakpoints set - this new one is number 2. You can disable it by entering "disable 2" (you could also use dis 2). And you can enter "enable 2" (or "ena 2") to enable the breakpoint again, and "delete 2" (or "d 2") to delete the breakpoint.
Other stuff: Enter "help" at the gdb prompt to find out about more commands. One useful one, for example, is "list" - try it.
Ok... Now you have all the tools you need in order to find where is the input bug in this program. Continue stepping until right after the program attempts to get the second course name from the user. Not what you expected? Exit gdb after you have stepped to the point where the program is not working properly.
The problem (typical in programs that use cin to get answers from the user) is that the input stream is not flushed after the user enters 'y' or 'n' in response to the "Do you want ..." question - there is still a newline character in the stream, so just that newline character is read as the course name in the next iteration. Add the following line of code in an appropriate place to solve the problem:
cin.ignore(1000, '\n');That line will flush up to 1000 characters from the input stream, including the newline character. Afterwards the stream will be empty and ready for new user input.
Compile (still with -g option) again, and run it to make sure it works properly now. Be ready to demonstrate the corrected version to the TA.
Get your TA's attention to inspect your work, and to record completion of your lab work. |
Don't leave early though ... begin the after-lab work below.
Step 6a. ONLY IF YOU RAN OUT OF TIME TO HAVE THE TA INSPECT YOUR WORK
If you must complete this assignment at CSIL, then you must turn in some of your work with the turnin program - but do NOT turn it in if the TA already checked you off. You MUST have both your name and your partner's name in appropriate comments in your corrected version of buggy1.cpp to receive credit. Use turnin to turn in buggy1.cpp as follows:
turnin Lab03@cs24 buggy1.cpp
You must accomplish the following to earn full credit [50 total points] for this lab:
ONLY IF STEP 6a IS NECESSARY - buggy1.cpp has been turned in, and it contains the comments specified in Step 6a.
Deadline for after-lab submission: tonight at 11:59pm. Note that to be eligible for late turn-in with credit, you MUST have attended your entire lab period.
Just to be clear: there is nothing to submit to submit.cs for this lab. And you should not turn in anything if the TA has checked off your work today.
It turns out that buggy1.cpp also has another problem. After inserting more that 5 courses, a segmentation fault (eventually) takes place. It's obvious why, but every bug in your program is a chance to learn more useful commands to improve your programming life! Start gdb again, and enter more than 5 courses until the fault occurs. Then try the gdb command backtrace (or bt) to see the sequence of commands that produce the crash. Use the x command (to examine memory), and list (or l). By the way, what are your options for fixing this bug?
By the way, here is a GDB Quick Reference Card that shows more gdb commands. And there are many online tutorials for gdb - google "gdb tutorial" for instance.
You may work on PA2 if you need more work to do. Or like usual, help other students who might be struggling. Did you know that trying to teach others is the very best way for you to learn something yourself?
Prepared by Michael Costanzo as an adaptation of a lab created for a different class in 2011 by Stratos Dimopoulos.