You are on page 1of 26

Programming in C

Project Organization
Compiler Directives
A Real C Program

• Program that accepts one command line


argument.
• Treats the command line argument as a
string, and reverses the letters in the
string.
• Prints out the the reversed string.

7/28/09
reverse.c - part 1
/** reverse.c
** inputs a single command line argument which is a string
** to be printed with its characters reversed.’
** Use quotes if your string contains spaces
** reverse( ) uses dynamic memory so any size string is ok */
#include <stdio.h> /* printf */
#include <stdlib.h> /* malloc,free */
#include <string.h> /* strlen, strcpy */

/* user error message */


#define USAGE "Usage: reverse \"string to reverse in quotes\""

/* prototype for reverse */


void reverse( char *s);

7/28/09
reverse.c - part 2
/* main checks the number of args
** then just calls the function to do the work */

int main(int argc, char *argv[ ])


{
if (argc < 2) {
printf("%s\n", USAGE);
exit(0);
}
char *string = argv[1];
reverse( string );
printf("%s\n", string);

return 0;
}
7/28/09
reverse.c
// void reverse (char[ ] s)
void reverse( char *s )
{
char *buf;
int i, sLength = strlen( s );

/* allocate memory -- why + 1 ??*/


buf = (char *)malloc( sLength + 1);

/* copy string in reverse order in place */


for (i = 0; i < sLength; i++)
buf[i] = s[sLength - i - 1];
buf[i]='\0'; // null terminate !! */

strcpy(s,buf); // copy string back to s


free(buf); // free the memory
}

7/28/09
Compiler directives

Compiler directives begin with # and are


interpreted by the preprocessor
The most common directives are
– #include
• Used for file inclusion (See K&R 4.11.1)
– #define
• Used for constant/macro definitions (see K&R
4.11.2)
– #if, #ifdef, #ifndef, #else, #elif, #endif
• Used for conditional compilation (see K&R 4.11.3)

7/28/09
#include
The #include directive is used to include the contents of
another file into the file being compiled. It is usually
used to include header (.h) files into the source. #include
files may be nested so that one .h file may #include
another .h file

A line of the form


#include <filename>
causes replacement of that line by the entire contents of
the file filename. The named file is searched for in
implementation specific places, usually /usr/include on
Linux

Similarly, a line of the form


#include “filename”
first searches in the same directory as the source file, and if
that search fails, continues searching as above.
7/28/09
#define
A line of the form
#define identifier token-sequence
Causes the preprocessor to replace all
occurrences of identifier with the token-
sequence (except in quoted strings). Note
that this is a text replacement.
#defines are most commonly used to give
names to “magic constants”
For example
#define MAXSIZE 100
replaces every occurrence of MAXSIZE with
100 as in int table[MAXSIZE];

7/28/09
#define macros
The #define directive may also be used to create a simple macro.
Care must be taken when writing macros and when calling macros
because the arguments are copied literally.

For example, the simple macro below doesn’t work properly in all
instances.
#define SQUARE(x) x * x
Assuming int z = 4, what is the value of y after the assignment
int y = SQUARE( z - 1 );

Even when written correctly as


#define SQUARE(x) (x) * (x)
Calling the macro may result in erroneous side effects.
Assuming again that int z = 4, what is the value of z after the
assignment
int y = SQUARE( ++z )

Nonetheless, macros like this can make code easier to read as we’ll
see later

7/28/09
Importing #defines
• The most common way to #define symbols
is within your .c file
– #define DEBUG
– #define MAX 100
• It is also possible to “import” #defines on
the compiler command line
– gcc -o myProg -DDEBUG -DMAX=100 myProg.c
• You cannot use the command line to
redefine a symbol already defined in
your .c file

7/28/09
assert( )

• The C library provides a macro named


assert( ) which can be very helpful for
debugging
• The parameter to assert is any boolean
expression -- assert( expression );
– If the boolean expression is true, nothing happens
and execution continues on the next line
– If the boolean expression is false, a message is
printed to stderr and your program terminates
• To use assert( ), you must #include
<assert.h>
7/28/09
Using assert( )
• Use assert( ) to verify an array index
int scores[100];
int k = <some complicated calculation >
assert( k >= 0 && k < 1000 );
• Use assert( ) to check for NULL pointers
int *ptr = malloc( 4 * sizeof( int ) );
assert( ptr != NULL);
• Use assert( ) to check functions that
shouldn’t fail
int errorCode = foo ( );
assert( errorCode == 0); /* but NOT assert(foo( ) ==
0; */
7/28/09
Conditional Compilation
• The preprocessor directives #ifdef (read as “if defined”),
#ifndef (read as “if not defined”), #if, #else, #elif (read as
“else if”), and #endif (together with #defines) can be used
to control which lines in a course file are compiled and
which are not. Each directive appears on a separate
line.

• Each #if and subsequent #elif is evaluated as true (1) or


false (0) based on the definition (or lack of definition) of
the identifier that follows. Lines that follow a false (0)
evaluation are skipped.

• Conditional compilation is frequently used to avoid


multiple inclusions of the same header (.h) file, compiling
lines used for debugging, and (in system level .h files) to
compile certain lines based on the type of system on
which the program is being compiled.
7/28/09
Conditional Compilation
for header files
Header (.h) files should only be included once in a source file to avoid
compilation errors. Suppose the file myheader.h is #included into
some source file. The .h files should be “guarded” as follows

#ifndef MYHEADER_H /* same as #if !defined(MYHEADER_H) */


#define MYHEADER_H
....
...
#endif

How does this work?


Whenever the compiler includes myheader.h, the #ifndef checks to see
if the identifier MYHEADER_H has be #defined. The first time
myheader.h is included, MYHEADER_H will not have been defined so
the lines in the header file (in particular #define MYHEADER_H) will be
included into the source file. Thereafter, when the compiler
subsequently tries to include myheader.h, the #ifndef will evaluate
to false (because MYHEADER_H is now defined) and all lines in
myheader.h will be skipped, avoiding duplicate inclusion.

7/28/09
Conditional Compilation for
Debugging
One simple way of debugging a program is to
insert printf( ) statements are strategic
points in the code. When the program is run,
the output of these printf( ) statements
help you debug your program. When the
program is bug free, these printf( )
statements are removed from your code.
Rather than removing the printf( )
statements by editting your code,
conditional compilation can be used to allow
or prevent them from being executed.

7/28/09
debug.c
#include <stdio.h>
static void debug(char *string)
{
#ifdef DEBUG
printf( “%s\n”, string );
#endif
}

int main (int argc, char *argv[] )


{
int i, count, sum = 0;
debug( “Command Line arg is: “);
debug(argv[1]);
count = atoi( argv[1] );
debug( “Entering for loop”);
for (i = 0; i < ; i++)
{
count += i;
}
debug (“exited for loop”);
return 0;
}
7/28/09
Program organization
• main( ) is generally defined in its own .c file and
generally just calls helper functions
• Program-specific helper functions in another .c file
– If there are very few helpers, they can be in the same
file as main( )
• Reusable functions in their own .c file
– Group related functions in the same file
• Each struct or union
– Defined in a separate .h file
– Related functions in a separate .c file
– File names should be indicative of the struct name

7/28/09
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
7/28/09
Global Variables
• Global (external) variables are defined
outside of any function, near the top of
your .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 program
that declares them as “extern” (unless also
defined as static)
– 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

7/28/09
Local variables

• Local variables are defined within the


opening and closing braces of a
function, loop, if-statement, etc.
Function parameters are local to the
function.
– Are usable only within the braces in
which they are defined
– Exist only during the execution of the
block unless also defined as static
– Static local variables retain their values
for the duration of your program. Usually
used in functions, they retain their values
between calls to the function.
7/28/09
Function Scope

• All functions are external because 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 program unless they are also declared as
static.
• Static functions may only be used within
the .c file in which they are defined

7/28/09
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);
}
7/28/09
}
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; /* successful completion */
}

7/28/09
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"
** holds the random number that was generated */
int randomInt;
/* a function that can be called from any other function
** returns a random integer from 1 to max, inclusive */

void getRandomInt( int max )


{
/* 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;


randomInt = (lastRandom % max) + 1;
}

7/28/09
randomInt.h
#ifndef RANDOMINT_H
#define RANDOMINT_H

// global variable in randomint.c


// set by calling getRandomInt( )
extern int randomInt;

// prototypes for function in randomInt.c


void getRandomInt(int max );
#endif
7/28/09
End of Class 6

7/28/09

You might also like