C Programming Separate Compilation Variable Lifetime and Scope make

advertisement
C Programming
Separate Compilation
Variable Lifetime and Scope
make
A Simple C Program
/* sample.c */
#include <stdio.h>
typedef double Radius;
#define PI 3.1415
/* given the radius, calculates the area of a circle */
double circleArea( Radius radius )
{
return ( PI * radius * radius );
}
// given the radius, calcs the circumference of a circle
double calcCircumference( Radius radius )
{
return (2 * PI * radius );
}
int main(
{
Radius
double
double
)
radius = 4.5;
area = circleArea( radius );
circumference = calcCircumference( radius );
printf (“Area = %10.2f, Circumference = %10.2f\n”, area, circumference);
return 0;
}
Reusable Functions
• The functions circleArea( ) and calcCircumference( )
are general functions that may be used by multiple
applications.
• To make them available to multiple applications
without duplicating the code we must place them
into a separate .c file.
• However, recall that the compiler requires that we
must provide the function prototypes to the calling
code. We do this by placing the prototypes and
supporting declarations into a header (.h) file which
is then included in .c files that wish to call the
functions.
circleUtils.h
• A header (.h) file is the public interface (function
prototypes) for its corresponding .c file.
• It contains all definitions required by the prototypes
so that a .c file that includes it compiles
/* circleUtils.h*/
// #includes required by the prototypes, if any
/* supporting typedefs and #defines required by the prototypes */
typedef double Radius;
/* function prototypes */
// given the radius, returns the area of a circle
double circleArea( Radius radius );
// given the radius, calcs the circumference of a circle
double calcCircumference( Radius radius );
circleUtils.c
/* circleUtils.c
** Utilites for circle calculations
*/
#include “circleUtils.h”
#define PI 3.1415
// why not in the .h file??
/* given the radius, calculates the area of a circle */
double circleArea( Radius radius )
{
return ( PI * radius * radius );
}
// given the radius, calcs the circumference of a circle
double calcCircumference( Radius radius )
{
return (2 * PI * radius );
}
Sample Code Revisited
/* sample.c */
#include <stdio.h>
#include “circleUtils.h”
int main( )
{
Radius radius = 4.5;
double area = circleArea( radius );
double circumference = calcCircumference( radius );
printf (“Area = %lf, Circumference = %lf\n”,
area, circumference);
return 0;
}
Header Files
• When a file contains functions to be reused in several
programs, their prototypes and important #defines and
typedefs are placed into a header ( .h ) that is then
included where needed.
• Each .h file should be “stand alone”. That is, it should
declare any #define and typedef needed by the
prototypes and #include any .h files it needs to avoid
compiler errors. The .h file should contain everything
needed to successfully compile any .c file that includes it.
• In this example, the prototypes for circleArea() and
calcCircumference( ) are placed into the file
circleUtils.h which would then be included in
circleUtils.c and any other .c file that uses
circleArea( ) and / or calcCircumference( ).
Guarding Header Files
• Because a .h file may include other .h files, there is
the possibility that one or more .h files may
unintentionally be included in a single .c file more
than once, leading to compiler errors (multiple name
definitions).
• To avoid these errors, .h files should be “guarded”
using the compiler directives #ifndef (read as “if
not defined”) and #endif
Guarded circleUtils.h
#ifndef CIRCLEUTIL_H
#define CIRCLEUTIL_H
/* circleUtils.h */
/* #include .h files as necessary */
/* supporting typedefs and #defines */
typedef double Radius;
/* function prototypes */
// given the radius, returns the area of a circle
double circleArea( Radius radius );
// given the radius, calcs the circumference of a circle
double calcCircumference( Radius radius );
#endif
Compiling and linking
• When a program’s code is separated into multiple
.c files, we must compile each .c file and then
link the resulting .o files to create an executable
program.
• The files may be compiled separately and then linked
together. The -c flag in the first two command tells gcc to
“compile only” which results in the creation of .o (object)
files. In the 3rd command, the presence of the.o extension
tells gcc to link the files into an executable
gcc -c -Wall circleUtils.c
gcc -c -Wall sample.c
gcc -Wall -o sample sample.o circleutils.o
• Or if there only a few files, compiling and linking can be
done all in one step
– gcc -Wall -o sample sample.c circleUtils.c
1/20/10
Compiler vs linker
• The compiler translates one .c file into a .o file
– Verifies that all functions are being called correctly
– Verifies that all variables exist
– Verifies language syntax
• The linker combines multiple .o files (and C libraries)
to create an executable
– “Finds” functions called by one .c/.o file, but defined in another
• E.g. printf( ), scanf( )
– “Finds” global variables used by one .c/.o file, but defined in
another (more on this soon)
• Both can be invoked with gcc (see previous lecture)
Linking with C libraries
• By default, the standard C library which includes printf,
scanf and char and string functions is always linked
with your program.
• Other libraries such as the math library must be
explicitly linked with your code.
• The names of the C libraries alll have the form
libxxx.a. The standard C library is libc.a. The C
math library is libm.a.
• Specify libraries to be linked using the -l flag and the
xxx part of the library name.
– E.g. to link the math library with your program, use
gcc -Wall -o sample sample.c circleUtils.c -lm
Project Organization
• main( ) is generally defined in its own .c file and generally just
calls helper functions
– E.g. project1.c
• Project-specific helper functions may be in the same .c file as
main( )
• main( ) comes first
• Helper function order that makes sense to you
• Reusable functions in their own .c file
– Group related functions in the same file
– E.g. circleUtils.c
• Prototypes, typedefs, #defines, etc. for reusable function in a .h
file
– Same file root name as the .c file. E.g. circleUtils.h
Variable Scope and Lifetime
• The scope of a variable refers to that
part of a program that may refer to the
variable.
• The lifetime of a variable refers to the
time in which a variable occupies a
place in memory
• The scope and lifetime of a variable are
determined by how and where the
variable is defined
static and extern
• The keyword static is used to
– Limit the scope of a function or global variable
– Extend the lifetime of a variable
• The keyword extern is used to
– Inform the compiler that a (global) variable is defined in a
different .c file
Function Scope
• All functions not found in the .c file that calls them
are considered external because standard C does
not allow nesting of function definitions.
– So no “extern” declaration is needed
– All functions may be called from any .c file in your
project unless they are also declared as static.
– The linker will “find” external functions called from one
.c file but defined in a different .c file
• static functions may only be called from within the .c
file in which they are defined
• Their scope is limited
Local variables
• Local variables are defined within the opening and
closing braces of a function, loop, if-statement, etc.
(a code “block”). Function parameters are
considered local variables in their function.
– Are usable only within the block in which they are
defined
– Exist only during the execution of the block unless
also defined as static
– Initialized variables are reinitialized each time the
block is executed (if not defined as static)
– static local variables retain their values for the
duration of your program. When used in functions,
they retain their values between calls to the function.
Global Variables
• Global (external) variables are defined outside of any function,
typically near the top of a .c file.
– May be used anywhere in the .c file in which they are defined.
– Exist for the duration of your program
– May be used by any other .c file in your application that declares
them as “extern” unless also defined as static (see below)
– Static global variables may only be used in the .c file that
declares them
– “extern” declarations for global variables should be placed into
a header file
randomInt.c
/* a global variable to be used by code in other .c files.
** This variable exists until the program ends
** Other .c files must declare this variable as " extern“ */
long randomInt;
/* a function that can be called from any other function
** sets randomInt to a value from 1 to max, inclusive */
void getRandomInt( int max )
{
randomInt = getNext( );
}
/* a function that can only be called from functions within
this file */
static long getNext( )
{
/* lastRandom is a local variable that can only be used
inside this function, but persists between calls to this
function */
static long lastRandom = 100001;
lastRandom = (lastRandom * 125) % 2796203;
return (lastRandom % max) + 1;
}
randomInt.h
#ifndef RANDOMINT_H
#define RANDOMINT_H
// global variable in randomint.c
extern int randomInt;
// prototypes for non-static (“public”)
// functions in randomInt.c
void getRandomInt( int max );
#endif
variableScope.c - part 1
#include <stdio.h>
// extern definition of randomInt and prototype for getRandomInt
#include “randomInt.h”
/* a global variable that can only be used
by functions in this .c file */
static int inputValue;
/* a function that can only be called by other functions
in this .c file */
static void inputPositiveInt( char prompt[ ] )
{
/* init to invalid value to enter while loop */
inputValue = -1;
while (inputValue <= 0)
{
printf( "%s", prompt);
scanf( "%d", &inputValue);
}
}
variableScope.c - part 2
/* main is the entry point for all programs */
int main( )
{
/* local/automatic variables that can only be used in this
function and that are destroyed when the function ends
*/
int i, maxValue, nrValues;
inputPositiveInt("Input max random value to generate: ");
maxValue = inputValue;
inputPositiveInt("Input number of random ints to generate: ");
nrValues = inputValue;
for (i = 0; i < nrValues; i++)
{
getRandomInt( maxValue );
printf( “%d: %d\n", i + 1, randomInt );
}
return 0;
}
The make Utility
Typing out the gcc commands for a project gets less appealing as the
project gets bigger.
The "make" utility automates the process of compiling and linking.
With make, the programmer specifies what the files are in the project
and how they fit together. make takes care of the appropriate
compile and link steps.
make can speed up your compiles since it is smart enough to know
that if you have 10 .c files but you have only changed one, then only
that one file needs to be compiled before the link step.
make has some complex features, but using it for simple things is
pretty easy.
This slide and those that follow are adapted from Section 2 of “UnixProgrammingTools.pdf” by Nick Parlante, et al at
Stanford. Used with permission.
How make works
• You tell make what files are in your project, how they
fit together, how to compile, and how to link them by
creating a file that make reads and interprets
– By default, make looks for a file named either “makefile” or
“Makefile”
• At the Unix console, simply type the command make
to compile all necessary files and create a new
executable
unix> make
• make can perform other tasks defined in the
makefile as well (more on this later)
The makefile
• A makefile consists of variable definitions,
dependency/build rules and comments
• Comments begin with the ‘#’ character and extend to
the end of the line
# makefile for project 1
# name, date, etc
The makefile (2)
• Variable definitions
– A variable name is defined to represent a string of text, similar to #define
in C. They are most often used to represent names of files, directories,
and the compiler.
– Variable are defined simply by setting them to some value (string)
– The most common variables are typically
•
•
•
•
CC -- the name of your C compiler
CFLAGS -- the set of flags that you wish to pass to the compiler
LDFLAGS -- the set of flags that you wish to pass to the linker
OBJS -- the set of object (.o) files that are linked together to create your
executable
• To use a variable, use the dollar sign ($) followed by the name of
the variable in parenthesis or curly braces
CC = /usr/local/bin/gcc
CFLAGS = -g -Wall
$(CC) $(CFLAGS) -c main.c
• Variables that are not initialized are set to the empty string
The make file (3)
• A dependency/build rule defines how to make a
target based on changes to the files on which the
target depends.
• The order of the rules is irrelevant, except that the
first rule is the default rule -- the rule that will be
used when make is executed with no arguments
• A dependency/build rule is made up of two parts
– A dependency line followed by one or more command lines
The make file (4)
•
Example dependency/build rule
sample.o : sample.c circleUtils.h
<TAB> $(CC) $(CFLAGS) -c sample.c
•
The first (dependency) line of this rule says that sample.o must be
rebuilt whenever sample.c or circleUtils.h changes. Generally a .o file
depends on its own .c file and any non-library .h files it #includes. In
this example, we would expect that sample.c #includes circleUtils.h
•
The second (command) line tells make how to rebuild sample.o
– Gotcha -- the command line MUST be indented with a TAB character. Spaces
won’t do. This can be a problem when cutting/pasting the contents of a makefile
from a terminal screen. Some editors will automatically change a TAB into
spaces.
Sample makefile
See
/afs/umbc.edu/users/c/m/cmsc313/pub/code/makefile
make features
• The make utility has some built-in default rules.
• In particular, the default rule for C files is
$(CC) $(CFLAGS) -c source-file.c
• Special syntax can be used to create the list of the
.o files
OBJS = $(SRCS: .c=.o)
Download