-->

Previous | Table of Contents | Next

Page 472

  1. The compiler parses the modified code for correct syntax. This builds a symbol table and creates an intermediate object format. Most symbols have specific memory addresses assigned, although symbols defined in other modules, such as external variables, do not.
  2. The last compilation stage, linking, ties together different files and libraries and links the files by resolving the symbols that hadn't previously been resolved.

Executing the Program

The output from this program appears in Listing 23.8.

Listing 23.8. Output from the sample.c program.


$ sample

 1.   1    1 1.00000

 2.   4    8 1.41421

 3.   9   27 1.73205

 4.  16   64 2.00000

 5.  25  125 2.23607

 6.  36  216 2.44949

 7.  49  343 2.64575

 8.  64  512 2.82843

 9.  81  729 3.00000

10. 100 1000 3.16228



NOTE
To execute a program, just type its name at a shell prompt. The output will immediately follow.

Building Large Applications

C programs can be broken into any number of files, as long as no single function spans more than one file. To compile this program, you compile each source file into an intermediate object before you link all the objects into a single executable. The -c flag tells the compiler to stop at this stage. During the link stage, all the object files should be listed on the command line. Object files are identified by the .o suffix.

Making Libraries with ar

If several different programs use the same functions, they can be combined in a single library archive. The ar command is used to build a library. When this library is included on the compile line, the archive is searched to resolve any external symbols. Listing 23.9 shows an example of building and using a library.

Page 473

Listing 23.9. Building a large application.


gcc -c sine.c

gcc -c cosine.c

gcc -c tangent.c

ar c libtrig.a sine.o cosine.o tangent.o

gcc -c mainprog.c

gcc -o mainprog mainprog.o libtrig.a

Large applications can require hundreds of source code files. Compiling and linking these applications can be a complex and error-prone task of its own. The make utility is a tool that helps developers organize the process of building the executable form of complex applications from many source files.

Debugging Tools

Debugging is a science and an art unto itself. Sometimes, the simplest tool—the code listing—is best. At other times, however, you need to use other tools. Three of these tools are lint, gprof, and gdb. Other available tools include escape, cxref, and cb. Many UNIX commands have debugging uses.

lint is a command that examines source code for possible problems. The code might meet the standards for C and compile cleanly, but it might not execute correctly. lint checks type mismatches and incorrect argument counts on function calls. lint also uses the C preprocessor, so you can use similar command-like options as you would for gcc. The GNU C compiler supports extensive warnings that might eliminate the need for a separate lint command.

The gprof command is used to study where a program is spending its time. If a program is compiled and linked with -p as a flag, when it executes, a mon.out file is created with data on how often each function is called and how much time is spent in each function. gprof parses and displays this data. An analysis of the output generated by gprof helps you determine where performance bottlenecks occur. Whereas optimizing compilers can speed your programs, gprof's analysis will significantly improve program performance.

The third tool is gdb—a symbolic debugger. When a program is compiled with -g, the symbol tables are retained, and a symbolic debugger can be used to track program bugs. The basic technique is to invoke gdb after a core dump and get a stack trace. This indicates the source line where the core dump occurred and the functions that were called to reach that line. Often, this is enough to identify the problem. It is not the limit of gdb, though.

gdb also provides an environment for debugging programs interactively. Invoking gdb with a program enables you to set breakpoints, examine variable values, and monitor variables. If you suspect a problem near a line of code, you can set a breakpoint at that line and run the program. When the line is reached, execution is interrupted. You can check variable values, examine the

Page 474

stack trace, and observe the program's environment. You can single-step through the program, checking values. You can resume execution at any point. By using breakpoints, you can discover many of the bugs in your code that you've missed.

There is an X Window version of gdb called xxgdb.

cpp is another tool that can be used to debug programs. It performs macro replacements, includes headers, and parses the code. The output is the actual module to be compiled. Normally, though, cpp is never executed by the programmer directly. Instead it is invoked through gcc with either an -E or -P option. -E sends the output directly to the terminal; -P makes a file with an .i suffix.

Introduction to C++

If C is the language most associated with UNIX, C++ is the language that underlies most graphical user interfaces available today.

C++ was originally developed by Dr. Bjarne Stroustrup at the Computer Science Research Center of AT&T's Bell Laboratories (Murray Hill, NJ), also the source of UNIX itself. Dr. Stroustrup's original goal was an object-oriented simulation language. The availability of C compilers for many hardware architectures convinced him to design the language as an extension of C, allowing a preprocessor to translate C++ programs into C for compilation.

After the C language was standardized by a joint committee of the American National Standards Institute and the International Standards Organization (ISO) in 1989, a new joint committee began the effort to formalize C++ as well. This effort has produced several new features and has significantly refined the interpretation of other language features, but it hasn't yet resulted in a formal language standard.

Programming in C++: Basic Concepts

C++ is an object-oriented extension to C. Because C++ is a superset of C, C++ compilers will compile C programs correctly, and it is possible to write non_object-oriented code in C++.

The distinction between an object-oriented language and a procedural one can be subtle and hard to grasp, especially with regard to C++, which retains all of C's characteristics and concepts. One way to describe the difference is to say that when programmers code in a procedural language, they specify actions that process the data, whereas when they write object-oriented code, they create data objects that can be requested to perform actions on or with regard to themselves.

Thus a C function receives one or more values as input, transforms or acts on them in some way, and returns a result. If the values that are passed include pointers, the contents of data variables can be modified by the function. As the standard library routines show, it is likely that the code calling a function won't know, or need to know, what steps the function takes when it is invoked. However, such matters as the datatype of the input parameters and the

Previous | Table of Contents | Next