C Functions, Separate Compilation, and Macros

Ryan Robucci

C functions

C Passing of Arguments

By Example:

Example 1:A simple C program demonstrating arugment passing

/* 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 Circumference( Radius radius ) { 
  return (2 * PI * radius ); 
} 

int main( ) { 
  Radius radius = 4.5; 
  double area = CircleArea( radius ); 
  double circumference = Circumference( radius ); 
  printf (“Area = %10.2f, Circumference = %10.2f\n”, area, circumference); 
  return 0; 
} 

Example 2: passing array arguments

/* ages.c */ 
#include <stdio.h> 
int ArraySum( int array[ ], int size) { 
  int k, sum = 0; 
  for (k = 0; k < size; k++)
    sum += array[ k ]; 
  return sum; 
} 

int ArrayAvg( int array[ ], int size) { 
  double sum = ArraySum( array, size ); 
  return sum / size; 
} 

int main( ) { 
  int ages[ 6 ] = {19, 18, 17, 22, 44, 55}; 
  int avgAge = ArrayAvg( ages, 6 ); 
  printf(“The average age is %d\n”, ageSum); 
  return 0; 
}

Function Reuse

Header Files

Example

circleUtils.htypedef: Radiusprototype: Areaprototype: CircumferencecircleUtils.cdefine: PIfunction definition: Areafunction definition: Circumferencemain.cfunction definition: mainincludeincludelinker

/*circleUtils.h*/ 
typedef double Radius; 
/* function prototypes */ 

// given the radius, returns the area of a circle 
double Area( Radius radius ); 
// given the radius, calcs the circumference of a circle 
double Circumference( Radius radius ); 
... 
/*circleUtils.c*/ 
#include "circleUtils.h" 
/* circleUtils.c 
** Utilites for circle calculations 
*/ 
#include “circleUtils.h” 
#define PI 3.1415    // why not in the .h file?? 

/* function definitions*/ 
/* 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 Circumference( Radius radius )  { 
  return (2 * PI * radius ); 
} 
/* sampleMain.c */ 
#include <stdio.h> 
//*********************************
#include “circleUtils.h” 
//*********************************

/*code with function calls*/ 
int main( )  { 
  Radius radius = 4.5; 
  double area = CircleArea( radius ); 
  double circumference = Circumference( radius ); 
  printf (“Area = %d, Circumference = %d\n”, 
                area,      circumference); 
  return 0; 
} 

Header Guard (Include Guard) and Preprocessor Derectives

Example: 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 Area( Radius radius ); 
// given the radius, calcs the circumference of a circle 
double Circumference( Radius radius ); 
#endif

Separate Compilation and linking

When a program’s code is separated into multiple .c files, may compile each .c file separately and then combine the resulting .o files to create an executable program.

The object 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.

Or if there only a few files, compiling and linking can be done all in one step

Program organization

Variable Scope and Lifetime

Global Variables

Example:

/* driver.h */
#ifndef DRIVER_H
#define DRIVER_H
extern int g_device_status;
...
void device_init();
...
#endif
/* driver.c */
#include "driver.h"
int g_device_status;
static int g_device_secret;
...
...
void device_init(){
  ...
  g_device_secret=...
  ...
  if (g_device_secret){
  ...
  }
  ..
  g_device_status= ? a:b;
  ...
};

/* main.c */
#include "driver.h"

int main(){
  ...
  device_init();
  ...
  while (g_device_status==5) {
    ...
  }
  ...
}

Local variables

Static Variables

Initialization of Static Variables

Example of Static Variable Usage

This function can be called upon and acts like a counter.

int count (){   
  static int i=0; 
  i++; 
  return(i); 
} 

This function this function returns true only the first ten times it is called

int trackTenDowntoNothing (){   
  static int i=10;
  if (i>0){
    i--; 
    return(1);
  } 
  return(0); 
} 

Function Scope

Embedded Helper Functions

some languages do allow defining "helper" functions that are defined inside another function and can be used only in the parent function in which it is embedded. While standard C doesn't support such helper functions at the time of this writing, the GNU extensions of gcc will allow it, but such syntax is not portable portable across all C compilers

Code Example for Scope

variableScope.c

#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); 
   } 
} 

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

} 

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 ) { 
/* max is a local variable that may used within this function */ 
   /* 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; 
  randomInt = (lastRandom % max) + 1; 
} 

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 

Recursion

Example:

/* print an integer in decimal 
**  K & R page 87 (may fail on largest negative int) */ 
#include <stdio.h> 
void printd( int n ) { 
  if ( n < 0 ) { 
    printf(-); 
    n = -n; 
  } 
  if ( n / 10 ){        /* (n / 10 != 0) -- more than 1 digit */ 
    printd( n / 10 );    /* recursive call: n has 1 less digit */ 
  }
  printf(%c”, n % 10 +0); /* base case --- 1 digit */ 
} 

Inline Functions

inline bool isEven( int n ) 
  { return n % 2 == 0; } 
inline max( int a, int b ) 
  { return a > b ? a : b; } 

Inline suggestion

Remember to think of inline more like a suggestion and not as a requirement by default
In some cases it is guaranteed, but this is compiler-dependant.

Macros

General macro format:

#define NAME( comma separated parameters if any ) code here

Note: there is NO space between the name and the left paren, this is important.

Error

Unlike much of C syntax, spacing is important when defining macros

Example

Square

A simple macro to square a variable

#define SQUARE( x ) (x * x) 

As with all #defines, the preprocessor performs text substitution. Each occurrence of the parameter is replaced by the argument text.

int y = 5; 

int z = SQUARE( y ); 

NEVER FORGET THE ()

#define DOUBLE_IT( x )  x + x 

…somewhere in the code:

x=DOUBLE_IT(x)*3;  //results are wrong!!  

@line11: expands to x=x+x*3 instead of (x+x)*3

Consider this statement;

int w = SQUARE( y + 1 ); 

Expands to --> (y + 1 * y + 1)which is not the same as (y + 1)*2

A better SQUARE()

This version is safer

#define SQUARE( x ) ( (x) * (x) )
int y = 5; 
int z = SQUARE( y ); 
int w = SQUARE( y + 1 ); 

But still doesn’t work in this case:

int k = SQUARE( ++y );  

--> expands to ((++y)*(++y)) which has two pre-increments

Multi-line macro

#include <stdio.h>

#define LOG(depth, msg) ({                 \
      for (int i;i<depth;i++) printf("."); \
      printf(" msg: %s ", msg);            \
      printf("\n");                        \
      })

void main(){
  LOG(10,"main function");
}

Prints:

.......... msg: main function 

Macros are text-replacement scripts

Be aware of what macros are, that they are just text replacement scripts. Don't think of them as functions or misuse them. Devote attention to what the result of the text substitution is. The can easily generate invalid syntax or, even worse, generate valid syntax that is also malfunctioning code that is typicaly unseen.

Conditional Compilation and Command-line Flags

at the command line, equivilents to basic #defines can be provided

gcc -Dsymbol ...

gcc -Dmacro=defn ...

Combine command-line argument directives with preprocessor directives in the code.

Example 1:

...
#ifdef HARVARDCOMMA
  print('a,b, and c');
#else
  print('a,b and c');
#endif
...

gcc -DHARVARDCOMMA main.c

Example 2:

...
#ifdef DEBUG
  print('variable c:%d',%c);
#endif
...

gcc -DDEBUG main.c

≡