Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C++ For Mathematicians (2006) [eng]

.pdf
Скачиваний:
194
Добавлен:
16.08.2013
Размер:
31.64 Mб
Скачать

366

C++ for Mathematicians

A typical C++ program has several files: .h header files and .cc code files. Save them all in the working directory you created.

The emacs editor takes some practice because its conventions are different from other (non-UNIX) programs. The mouse-driven menus in emacs (or Xemacs) make getting started easier, but it is worth learning the keyboard shortcuts for common tasks including creating, opening, and saving files. I recommend you work through the tutorial that comes built in to emacs (available from the Help menu).

A.1.3 Compiling and running your program

By now you have created and saved your program in its own directory (folder) on your computer and you are ready to see if it works. Open a shell window2 and navigate to your working directory. The shell command you need to change directories is cd (for change directory). Typing cd (and then pressing return) places you in your “home” directory. To move to a subdirectory type cd subdir where “subdir” is the name of the subdirectory. To move up a level in the hierarchy, type cd .. or to move to a directory directly, type the full path, like this:

cd /home/barney/programming/twin-primes

If you lose track of where you are in your computer’s file structure, the command pwd (for print working directory) reports your current location.

One more useful shell command is ls. The ls command lists all the files in the current directory.

Single file programs

If your program consists of just a single file (e.g., poem.cc) then compile your program with this command:

g++ poem.cc

After a brief pause, one of two things happens.

With luck, the computer responds by typing the shell prompt (typically a single dollar sign $). This means that the program compiled without errors and created a ready-to-run program. Depending on your system, the program is named a.out (most UNIX systems) or a.exe (on Windows/Cygwin).

Or, more likely, the compiler types warnings and error messages. Unfortunately, these can be difficult to understand. The error messages usually report line numbers where the compiler got into trouble. However, that is not necessarily the line number where the programming error resides. You will quickly learn that an error reported for line n is often caused by a missing semicolon on line n − 1. Another common error is to forget the using namespace std; statement.

2Use a bash or similar shell window. If you are using Windows, do not open a DOS command window; instead, use the bash command terminal supplied by Cygwin.

Your C++ Computing Environment

367

When you get error messages, go back to your editor and figure out what’s wrong. Make some changes and try compiling again.

When compiling is successful, the program is ready to run. In the shell window type

./a.out

(or ./a.exe for Windows/Cygwin). The program runs and the output appears on the screen.

If nothing appears to be happening and the shell prompt has not returned, your program may be stuck in an infinite loop. To force the program to stop, type a Control-C (while holding down the Control key on your keyboard, press the C key).

You might not want to name your program a.out (or a.exe). You may either compile as described above and change the name of the executable, or you can compile with the following command,

g++ poem.cc -o poem

The -o poem option causes the compiler to write the executable program to a file named poem. (Use poem.exe for Windows.)

Multiple file programs

In most cases, your program spans multiple files. Typically, each class has a .h file and a .cc file. The main() procedure is in yet another file. For example, suppose your project consists of the following files: gcd.h, gcd.cc, Mod.h, Mod.cc, and main.cc.

The simplest way to compile your program is with this shell command:

g++ gcd.cc Mod.cc main.cc

This compiles the program in these three source files and writes the executable to a.out (or a.exe). Alternatively, the shell command g++ *.cc compiles all files that end with .cc in the current directory.

The .h files do not need to be mentioned in the compile command; they are automatically absorbed into the appropriate .cc files by the #include directives.

If your project has just a few .cc files, then compiling them all with a single command works well. Imagine, however, that your project has ten different .cc files. You compile with the convenient g++ *.cc command, and the compiler complains about an error in one of the files. You fix the offending .cc file and compile again. The annoyance with this approach is that C++ compiling can be time consuming. So, rather than compiling all the .cc files at once, it is possible to compile them one at a time (fixing errors in each as you go), and then combine the pieces into a single executable program. Here is how you do this.

Suppose the .cc files are named one.cc, two.cc, . . . , nine.cc, and finally main.cc (which is the only source file that contains a main() procedure). To compile just the file one.cc type this:

g++ -c one.cc

368

C++ for Mathematicians

The -c option tells the compiler that this is just a piece of the whole program, so don’t try to build a full executable a.out. Instead, the compiler creates a file named one.o. If there is a problem while compiling one.cc, we fix the error and compile this part again. This is much faster than recompiling all the files every time you modify just one of them.

Of course, you also need to compile two.cc with the command g++ -c two.cc, and so on for all of the .cc files.

Finally, you have collected several .o files that the computer needs to stitch together to make the executable a.out (or a.exe). This phase is known as linking the object (.o) files. To link the .o files together, type this:

g++ *.o

Alternatively, you may name all of the .o files on the command line instead of using the wildcard *.o. You may also use the -o option to write the executable file to a different file name.

If your project has just a few .cc files, then this technique is quite manageable. If you have many .cc files, then it is easy to lose track of which .o files are up to date with their corresponding .cc files. Fortunately, there is a good way to automate this procedure using the make program. We describe this tool in a moment (see Appendix A.1.5) but first we introduce additional options you can give to the compiler.

A.1.4 Compiler options

In the previous section we introduced two options that can be used with the g++ compiler. The -o option is used to specify the name of the final executable program and the -c option is used to compile a .cc file just to a .o file and not to try to link the object files into an executable program.

Here are some other options you can give to the g++ compiler.

-Wall: This tells the compiler to issue all warnings. There are C++ statements that are not exactly errors, but are not fully legitimate C++. The compiler can proceed, but something is not quite right. With this option specified, all possible warnings are issued.

It’s a good idea to use this option and fix your code so that all warnings disappear.

-ansi and -pedantic: These options disable nonstandard C++ features. Compiler manufacturers often include extensions to the standard C++ language. If you plan to use your program only on your own computer, then there is no serious problem in taking advantage of these nonstandard options. However, if you try to compile your program on another computer with a different C++ compiler, these special features might not be available. By including these options you ensure the portability of your code.

Your C++ Computing Environment

369

-O, -O2, and -O3: These options engage the compiler’s optimizer. (Note: The O is the uppercase letter oh, and not the numeral zero.) Without these options, the computer compiles your code as quickly as possible, but does not produce the best possible (i.e., fastest) executable code. There are several “tricks” the compiler can apply during the compilation process and these options request greater and greater levels of optimization. The basic optimization is -O (which is equivalent to -O1). Each of -O2 and -O3 employ additional optimization techniques.

-g: This causes the compiler to produce a program that can be run using a debugging tool (gdb). The gdb debugger is complicated to use and beyond the scope of this book. Some advice on debugging your code is presented in Appendix A.3.

-p and -pg: This causes the compiler to insert extra code in your program so that it can be analyzed with a profiling tool (prof and gprof, respectively). These tools measure how much time your program spends in each of its procedures and how many times each procedure is called. If you need to make your program run faster, then this information tells you where to concentrate your efforts.

-I directory: This option tells the compiler where to look for nonstandard header files. If you have downloaded and installed some special C++ package, you may need a #include directive for its header files. These header files might not be installed in a location that the C++ compiler knows to check. One solution to this problem is to copy the header files into your project’s directory. A better alternative is to tell the compiler where to find the special header files and for that you use the -I option. (See the next bullet for more details.)

-L and -l options: Packages that you download from the Internet may contain libraries—these are similar to .o files in that they contain compiled programs that can be linked into your final program. For example, the Gnu Multiprecision Package (GMP) is distributed with .h header files and libraries. One uses the -I option (described above) to specify the location of the .h files and the -L and -l options to specify the location and name of the library.

For example, on my computer the GMP header files are located in the directory /sw/include so if I have #include <gmpxx.h> in my program, I need the -I /sw/include option when I compile my programs.

Furthermore, because the GMP procedures are embedded in a library, I need to tell the computer where the library is located (because on my computer, it’s in a nonstandard location) and the name of the library I need. The -L option specifies the location of the library. For my situation, I would type -L/sw/lib to specify the location. The names of the GMP libraries are gmp and gmpxx (we need both when working in C++). To tell g++ to use these libraries, we use the options -lgmp -lgmpxx.

370

C++ for Mathematicians

More details: The -I option is information for the compiler whereas the -L and -l options are used by the linker. So if I were to compile the various source files of my program separately, the commands I use would look like this:

g++ -c alpha.cc -I /sw/include g++ -c beta.cc -I /sw/include g++ -c gamma.cc -I /sw/include g++ -c main.cc -I /sw/include

g++ *.o -L/sw/lib -lgmp -lgmpxx -o my_program

While writing and debugging your program, I recommend you use the -Wall, -ansi, and -pedantic options. Once the program is working properly, do a final compilation with -O3.

A.1.5 Introduction to make

A program with just a few .cc files is easy to compile with a single command, either g++ *.cc or

g++ alpha.cc beta.cc gamma.cc main.cc -o myprog

Alternatively, each piece can be individually compiled and the .o files linked with these commands:

g++ -c alpha.cc g++ -c beta.cc g++ -c gamma.cc g++ -c main.cc

g++ alpha.o beta.o gamma.o main.o -o myprog

However, if the project involves more .cc files, it is annoying to need to compile each .cc file separately, and difficult to remember which .cc files have been successfully compiled into the corresponding .o files. One is tempted to type g++ *.cc and endure the slow compilation process.

Happily there is an alternative: The program make automates the entire process. To use make you create a file in your project directory named Makefile and then type make at the shell command prompt.

A basic Makefile for compiling a program based on four .cc files (alpha.cc, beta.cc, gamma.cc, and main.cc) is presented in Program A.1.

Program A.1: A basic Makefile.

1 OBJS = alpha.o beta.o gamma.o main.o

2CXXFLAGS = -Wall -pedantic -ansi

3

4myprog: $(OBJS)

5g++ $(OBJS) -o myprog

6

7clean:

8rm -f *.o

Your C++ Computing Environment

371

Line 1 defines the object (.o) files that need to be created. Line 2 sets the options that g++ is to use when compiling the .cc files.

Line 4 indicates that the program myprog depends on the files listed in OBJS. The syntax $(OBJS) tells make that the variable OBJS is to be replaced by its value (i.e., the list of .o files on line 1). The program myprog is called a target and line 4 means that before myprog can be made, each of the files after the colon (i.e., the four .o files) must be made.

Line 4 tells make on which files myprog depends, whereas line 5 tells make how to create the program myprog from the list of .o files. Notice that line 5 is indented; what you can’t see is that this indentation is caused by entering a TAB character into the file (and not by typing several spaces). The TAB is mandatory. Line 5 is equivalent to this:

g++ alpha.o beta.o gamma.o main.o -o myprog

In other words, line 5 tells make how to link the object files into the executable program named myprog.

(Ignore lines 7 and 8 for now.)

Interestingly, there is no indication in the Makefile of how to create the .o files from the .cc files. That’s because make is smart enough to figure that out. It knows that if there is a file named alpha.cc in the working directory, then it is compiled into alpha.o using the command g++ -c alpha.cc.

However, by setting the special make variable CXXFLAGS on line 2, make adds

-Wall, -pedantic, and -ansi.

 

 

With this Makefile in the same directory as alpha.cc, beta.cc, gamma.cc,

 

 

 

 

 

and main.cc, we type make at the shell prompt. Here is what we see.

 

 

 

 

 

$ make

 

 

 

 

 

 

 

 

 

g++ -Wall -pedantic -ansi

-c -o alpha.o alpha.cc

 

 

 

 

 

g++ -Wall -pedantic -ansi

-c -o beta.o beta.cc

 

 

 

 

 

g++ -Wall -pedantic -ansi

-c -o gamma.o gamma.cc

 

 

 

 

 

g++ -Wall -pedantic -ansi

-c -o main.o main.cc

 

 

 

 

 

g++ alpha.o beta.o gamma.o main.o -o myprog

 

 

 

 

 

Now suppose we want to make a change to, say, beta.cc. If we type make again,

 

 

 

 

 

 

 

 

 

this is what happens:

 

 

 

 

 

 

g++ -Wall -pedantic -ansi

-c -o beta.o beta.cc

 

 

 

 

 

 

 

g++ alpha.o beta.o gamma.o main.o -o myprog

 

 

 

 

 

Notice that only beta.cc is recompiled and then myprog is relinked. The other .o

 

 

 

 

 

 

 

files do not need to be rebuilt, and they weren’t.

If your project has many .cc files, they might not fit conveniently on a single line in the Makefile. You can use a backslash character \ to show that the definition of OBJS continues onto the next line, like this:

OBJS = one.o two.o three.o four.o five.o six.o \ seven.o eight.o nine.o main.o

Now let’s return to lines 7 and 8. Line 7 defines a target named clean with no dependencies and line 8 tells make what to do when we want to make clean. Line 8

372 C++ for Mathematicians

causes the shell command rm (for remove) to delete all files that end with .o, that is, the object files. Once the program is functioning properly, we do not need the .o files and they can be removed. To cause this to happen, we simply type make clean.

(Note: The standard way to run the make program is to type make target-name at the command prompt. However, if target-name is omitted, the first target found in the Makefile is built. Hence typing make or typing make myprog are the same for this Makefile. However, to cause make to run the commands for clean, we need to type make clean.)

There are two other reasons why we might want to remove all the .o files.

Once the program is working properly, we may wish to recompile with -O3 added to line 2 of the Makefile. To force make to compile everything again, we type make clean and then make.

This simple Makefile cannot determine that .cc files may depend on .h files. A change to a .h file should be followed by a recompilation of all .cc files that #include it. It is possible to construct a more elaborate Makefile that deals with this issue, but a simpler solution is this: if you change header files, do a make clean followed by a make.

A.2 Programming with an integrated development environment

An integrated development environment (IDE for short) is a computer application that provides all the tools you need to write programs. It includes a text editor (for creating and modifying your .h and .cc files), a compiler, a debugger, and many other tools. They are extremely powerful and can be used to work on massive software engineering projects involving teams of programmers. These tools provide a dizzying array of menus and control buttons that can intimidate a novice programmer.

Fortunately, you can begin by using only a small fraction of the IDE’s capabilities. As your familiarity with the tool grows, you can gradually add more of its features to your repertoire.

In this section we give a brief introduction to Microsoft Visual Studio (for Windows) and Xcode (for Macintosh OS X, available for free from Apple).

Please note that software manufacturers regularly release new versions of their products. The new versions may have different features and the locations of menus and other controls may vary from version to version.

Common features

Visual Studio and Xcode share some basic organizing principles. In both you create projects. A project houses all the pieces you need for your program such as

Your C++ Computing Environment

373

.cc and .h files.3 You either create these component files using the IDE’s built-in editor or you add files that have already been created elsewhere (via an Add menu item). Once the files are written and incorporated into the project, one builds the project; this means compiling the various source files and creating an executable program. Next, we run the program (within the IDE environment) and observe the action in a terminal window. If any errors crop up during the build phase, the IDE provides a convenient way to jump to the problematic portion of the source file.

Both IDEs provide a debugger. You set break points in the program; the break points cause the program to pause at the statement where they are set. While the program is suspended, you can inspect the values held in variables. You can then start the program running again. If a subsequent break point is reached (or the same break point is met again), you have another chance to inspect the contents of variables.

Both IDEs have a concept of a build configuration. The different configurations set different options for how the project is to be built. By default, both provide configurations named Debug and Release. Typically, one works in the Debug configuration until the program is working as expected. Then one switches to Release for a final build.

A.2.1 Visual C++ for Windows

Visual Studio is a integrated development environment sold by Microsoft. Install this on your computer using the disks provided.

Starting a new project

Launching Visual Studio4 brings you to a start page with a button labeled New Project that you should click.

A “New Project” window opens with a list of project types (on the left) and templates (on the right). Follow these steps.

1.Select Visual C++ Projects from the left and then Win32 Console Project from the right. Type in a name and a location for your project in the lower portion of the box: the location is the full path name of a folder on your hard drive (e.g., C:\research\math-programs\). The name is the name you want to give to your project (e.g., twin-primes). Fill these in and click OK.

2.A dialogue box appears; in the left of this box click on Application Settings. On the right, check Empty Project under Additional Options. Click Finish.

3More complicated projects can include images, sounds, video, and the like.

4You can launch Visual Studio from your Start menu.

374

C++ for Mathematicians

Adding files to the project

You are now ready to create your program files. From the File menu5 select Add New Item. A dialogue box opens from which you select either C++ File or Header File (and you can safely ignore all the other options). Choose the type of file and type in its name in the text box at the bottom of the window. Note that Visual Studio expects the name of your C++ source file to end with .cpp (and not .cc as we have been using in this book). Feel free to use whichever naming convention you prefer. Click OK.

You can now start typing your program. Create additional source files by using Add New Item until you have created all the files you need.

If there is a file already on your computer that you want to add to this project (perhaps a file you created in a different project), use the Add Existing Item menu6 option.

If you want to edit a file that is already in your project, double-click on its icon in the Solution Explorer on the right side of your screen.

Compiling

Once your source files are collected in your project, it’s time to compile. Under the Build menu, select Build Solution. This engages the compiler. As the compiler runs, it reports its progress in a window at the bottom of your screen.

When the compiler finishes you see, with luck, a message that the build succeeded. More likely, however, there are errors in your program in which case a “Build Error Task List” appears in the bottom of your screen. Read the first error carefully and then double-click on it. Visual Studio opens the source file containing the error and takes you directly to the line at which it ran into trouble (which might not be the faulty line in your program).

After you fix the first error on the Task List, you have two choices. You can proceed to fixing the second error or you can try to compile again. Sometimes fixing the first error also fixes several subsequent errors (e.g., if you forgot to declare a variable). In this case, rebuilding removes several errors from the Task List and helps you move efficiently to the next trouble spot.

Iterate this process of building the project and fixing errors until no errors or warnings remain.

Running and debugging your program

Once the program compiles without errors, it’s time to start it running. To do this, go to the Debug menu and choose Start Without Debugging. When the program starts running, a terminal window opens showing the program’s output and provides a place for you to type any input the program requires. When the program finishes, the

5Or from the Project menu.

6Available either in the File or Project menu.

Your C++ Computing Environment

375

message Press any key to continue is typed in the terminal window—press a key and the window closes.7

If your program is not behaving as you expected, then you need to find the error in your program. This is more difficult than fixing problems that arose during the compilation process. Fortunately, the debugger in Visual C++ is extremely helpful (also see the advice in Section A.3 later in this appendix). Open any of your C++ code files. To the immediate left of your program is a gray strip; click next to a statement in your program and a large red dot appears in this strip. You have just set a break point in your program. Place these break points wherever you want your program to pause. Now select Start from the Debug menu. The program runs until it reaches the first break point and then pauses. In the lower portion of the screen you can find a list of variables and their values. You can check these to be sure they are what you expect. When you are ready to continue, select Continue from the Debug menu. The program resumes, pausing again at the next break point.

When running under the debugger, the terminal window closes immediately after the program ends (so you can’t see what was written to that window). To prevent this from happening either set a break point at the return 0; statement in main() or add these lines to the end of your main():

cout << "Program finished" << endl; cin.get();

Now when you run the terminal window does not disappear until you type a return after seeing your Program finished message.

Final version

Once your program compiles and runs without errors it is time to perform one final build. When Visual Studio starts a new project, the project is in a Debug configuration. Now that our program is working, we switch to a Release configuration by pulling down the Build menu and selecting Configuration Manager. This opens a dialogue box whose topmost control is a pull-down menu of configurations. Switch this from “Debug” to “Release” and click the Close button.

Having switched the configuration to Release, build the project again. Visual Studio uses different compiler options for different configurations and, by default, the Release configuration has compiler optimizations enabled so the executable program runs faster.

(Note: You can modify the compiler options for the Release configuration. Pull down the Project menu and select the Properties item for your project (the last item in this menu). Select the C/C++ tab on the left and then the Optimization subtab. Now, on the right, you find the various options that can be set to adjust the optimization. In general, choose optimizations that favor higher speed as opposed to smaller size.)

7Note: If you choose Start from the Debug menu, then the terminal window closes immediately after the program finishes running.