Ryan Robucci
Have a
name uniquely identifies a function
int MyAddition(int a, int b){ return (a+b); } int MyAddition(char a, char b){ return (a+b); } char MyAddition(int a, int b){ return ((char)(a+b)); }
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; }
.c
file.h
) file which is then included in .c
files that wish to call the functions.A header file is the .h file associated with a .c that typically defines a library of functions
A header (.h) file contains everything necessary to compile any .c file that includes it
#include
s, #define
s, and typedef
sA header acts as as public API and documentation, so detailed documentation is usually placed in the header file alongside the function prototypes
Example
example.c
, the circle functions CircleArea
and Circumference
are to be used.circleUtils.h
the prototypes are referenced./*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; }
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 preprocesssor directives
The C proprocessor facilitates conditional code compilation
#ifndef
“if not defined”#ifdef
“if defined”#else
#elif
“else if”#endif
Header gaurding uses
#ifndef
#define
#endif
must use a globally unique guarding identifer
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
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.
gcc -c -Wall circleUtils.c
(makes a .o)gcc -c -Wall sample.c
(makes a .o)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
project1.c
proj1Utils.c
.c
file
circleUtils.c
typedef
s, #define
s, etc. for reusable functions in a .h
file
circleUtils.h
.c
file.
extern
unless also defined as static
(see below)extern
declarations for global variables should be placed into a header file0
unless another initialization is providedg_
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) { ... } ... }
static
static
static
local variables retain their values for the duration of the program execution
NULL
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); }
.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 definedEmbedded 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
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
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
. This is a suggestion to the compiler that calls to the function should be replaced by the body of the function.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.
-Winline
Warn if a function that is declared as inline cannot be inlined. Even with this option, the compiler does not warn about failures to inline
functions declared in system headers.The compiler uses a variety of heuristics to determine whether or not to inline a function. For example, the compiler takes into account
the size of the function being inlined and the amount of inlining that has already been done in the current function. Therefore, seemingly insignificant changes in the source program can cause the warnings produced by -Winline to appear or disappear.
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
(
)
around all instances of the arguments withint the macro
#define SQUARE( x ) ( (x) * (x) )
A simple macro to square a variable
#define SQUARE( x ) (x * x)
As with all #define
s, 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
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
\
({
})
are provded here only as a convenience to avoid compiler errors when using the macro like a function in various syntax usage contexts#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.
#ifndef
“if not defined”#ifdef
“if defined”#else
#elif
“else if”#endif
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