Logged in as: guest Log in
Lab 6 wilkie / Version 14

Lab 6: A Program in Many Parts

Pre-Lab: 

As before, ensure that you can access your CS account on a Linux server.  In this lab, you will be doing more than just C programming, so it is necessary that you use a real system (and not a web app).

Optional: Many of you are probably getting tired of using Pico by now.  Pico is an extremely simple editor, and it is also extremely limited.  This lab is a good opportunity to try out Emacs (or Vim).  These are full fledged editors and would serve you well.  Look up a good tutorial and command reference for one of these and try creating/editing/searching a text file on the server.

Part 1: 

Previously, we've seen some C syntax for data types, control structures, input, output, and available mathematical operations. Some of these functions, such as printf() and fabs(), were available to us through a library.  To access these functions, we had to 1) add a preprocessor directive telling our compiler to load a header file and 2) use a linker flag to tell the compiler to link our code with the library object code.  

Header files are also useful for our own programs.  In essence, a header file is a definition of the interface for some subset of code.  Previously, we used header files to specify the interface to a system library, like "". Now, we'll consider using header files to specify interfaces for our own code.

Having interfaces allows us to split up our code into multiple files while keeping our project managable.  Let's make a program.  First, create a new file with a main function, called "lab6.c".  Now, let us assume that we have some code that we want to put together in a separate file. Create a second file, module.c, and define two functions in it:

    int addOne(int a)          // Returns 1 + a
    float addPointOne(float b) // Returns 0.1 + b

In your main function in lab6.c, call these functions with the arguments 5 and 6 respectively and print the result.

Compile your code using

    gcc lab6.c module.c -o test

Checkoff 1: What is the warning that you get? What are return values of your functions?

What happened is that the compiler did not know what type of value that your functions return.  In main(), these functions had not been seen before.  When the C compiler encounters a function it has never seen, it assumes it will return an integer. Thus, addOne works as we expect and addPointOne has problems, as would any function that does not return an integer.

Part 2:

We next need to include an interface definition in our lab6.c file so that the compiler knows the types returned by the functions we want to use.  First, let's establish some terminology.  A definition of a function creates the specified function in memory.  A declaration of a function states that a function exists, has certain arguments, and the type of the returned value, but does not create it in memory.  A call invokes a function, which moves the program execution to the beginning of the function in memory.  We can only write one definition for a function, otherwise we would be trying to create two blocks of code in memory with the same name.  We can create as many declarations of the function as we want: this only tells the compiler that a certain function exists.  We can also call a function as much as we want.

What we need is for our function, addPointOne(), to have a declaration in our main file.  It would also be good to have a declaration of addPointOne at the beginning of module.c so that we never have to worry about the order in which the functions are defined, and we would want a declaration in any new file that we create where addPointOne() is called.  We obviously do not want to write the same line over and over, so the solution to our problem is to create a header file, which we can then include in other files. 

Create a new file "module.h". In it, write a declaration of addPointOne.  A declaration has only 1) the return type of the function, 2) the function name, 3) the argument types, and 4) a semicolon.

Remove the #include directive for stdio.h from your main file and remove any printf() calls.  Add an #include call that includes your new header file. Compile your program (gcc lab6.c module.c).  You likely received an error.  #include does little more than search for a file of the specified name and copy that file's contents to the top of the program. 

Checkoff 2: Find documentation on the C #include directive.  What is the correct directive to load the header file we wrote?

Part 3: 

As I mentioned, the preprocessor does little more than append the contents of the header file to the begining of your C file.  In fact, we can see precisely what it does.  Compile your code with the -E flag and direct the output to a new C file:

    gcc -E lab6.c > new.c

When we look at new.c, we can see that the contents of the header file have simply been copied to the top of the file.  (Remove the stdio.h include if you have it in.  We'll look at that later) Now let's make a second header file, constant.h. Let's assume this is for another module, and let's include the definition of a constant global variable in the header file.

    const int MY_GLOBAL = 1234;

Include this new header file in lab6.c.  You should now be able to use the value of MY_GLOBAL within lab6.c.  Let's assume you also want to use this constant in your module.  We can then include the new header file inside the old one. Add the include directive for constant.h to module.h.

Checkoff 3: Compile your code. What errors do you get? Use the -E option to see what is being added to lab6.c. Explain the cause of the error.

The C preprocessor has its own IF/ELSE control structure based on the DEFINE macro.  If we use this structure in each of our header files:

    #ifndef __UNIQUE_NAME__
    #define __UNIQUE_NAME__
    (contents of header file)
    #endif

We can ensure that the header file is processed only once.

Part 4:

Now let us add #include <stdio.h> back in our lab6.c file.  Use the -E option to preprocess the directive and save the output to a new file.  

Checkoff 4: What is the declaration of printf that has been added?

Notice the keyword extern before the declaration.  This keyword tells the compiler to carry out a declaration rather than a definition.  By default, extern is assumed for all function declarations.  But you can also use it for variables.  For example, 

    extern int no_mem;

will declare a variable, but it will not be created in memory.




Site built using pyWeb version 1.10
© 2010 Leonard McMillan, Alex Jackson and UNC Computational Genetics