C Programming: Mark Allen Weiss
C Programming: Mark Allen Weiss
Outline
● Overview of C
● Functions
● C-style Pointers
● Preprocessor
● Arrays
● Strings
● structs
● I/O
● Mixing C and C++
C Basics
● High-level assembler language
– basic constructs of high-level languages
– ports to many machines
– allows access to system resources ala assembler
● 1970s language philosophy
– assumes the programmer knows best
● relatively few compiler checks
● relatively few runtime checks
– loose type checking
– not object-oriented or even object-based
Versions Of C
● Original K&R C (1970s)
– Spec is ambiguous in come places
● ANSI C (1988)
– Attempts to clean up original spec
– Attempts to codify some programming tricks
– Adds notion of the function prototype
– The version to program to
● Non-standard C
– many compilers add features; can turn off
extensions with compiler options
● C99
Similarity
● Same set of primitive types
– short, int, long, unsigned, signed, float, double, char
– no boolean in ANSI C (0 is false, non-zero is true)
● Same set of operators
– arithmetic, relational, equality, logical, bitwise, ?:,
and assignment operators all the same
● Same types of loops and statements
– for, while, do, switch, break, continue, return
Functions
● Same ideas as in C++
● Variables must be declared at the start of the
function
– once you have a non-declaration statement, cannot
declare any more local variables
● No overloading allowed
● No inline declarations allowed
● All parameters are passed call-by-value: no
exceptions
● Prototypes, ala C++ are allowed but not
required
Preprocessor Macros
● Preprocessor directives begin with #
– #include, #define, #ifndef, #else, #endif,
#undef, #if, #else, etc.
– long lines can be continued with \
● Same set as in C++, but used much more often
in C
● Preprocessor macros perform textual
substitution (logically before) program is
compiled
Simple Textual Substitution
#define MAX 50
#define SUMAB a + b
this text
if( x == MAX )
c = SUMAB * SUBAB;
becomes
if( x == 50 )
c = a + b * a + b;
● Moral of the story: always overparenthesize
Trivial Functions
● Used in C for speed
int absoluteValue( int x )
{
return x >= 0 ? x : -x;
}
Parameterized Macros
● Macro expansion: textual substitution, with
actual arguments directly substituted for
formal parameters
#define absoluteValue(x) ( (x)>=0 ? (x) : -(x) )
y = absoluteValue(a-3);
z = absoluteValue(--n);
● becomes
y = ( (a-3)>=0 ? (a-3) : -(a-3) );
z = ( (--n)>=0 ? (--n) : -(--n) );
Passing Arrays
● Use either [] or * in function declaration
– [] follows type name
● Use const to indicate that state of the array
will not change
● Pass the array (i.e. the pointer to the start)
● Probably have to pass the number of items too
/* Declarations */
void printItems( const int *arr, int n );
void initialize( int arr[], int n );
● Size in [] is ignored
● Cannot return array objects -- only pointers
Multidimensional Arrays
● Very messy to do fancy stuff
● If you know dimensions, it is easy
int x[4][7]; // declares 4x7 array
● Formal parameters must include all dimensions
except the first may be omitted
void print( int y[][7], int numRows );
Pointer Math
● Given a pointer p, ++p changes the value of the
pointer to point at an object stored one unit
higher in memory
● If p is pointing at an object in an array,
– ++p points at the next object
– p+k points at the object k away
– p1-p2 is the separation distance of two objects in
an array
● Gives a 70s style idiom for traversing an array
● Most optimizing compilers make this idiom
obsolete, but you will see it anyway
Two ways of initializing an array
void initialize1( int arr[], int n )
{
int i;
for( i = 0; i < n; i++ )
arr[ i ] = 0;
}
Characters
● Use putchar to print a single character
● getchar to read a single character
– returns an int, EOF if end of file
● <ctype.h> contains various character testing
routines such as isdigit, isalpha, etc.
These are all macros. Also contains toupper
and tolower.
Strings
● Represented as an array of char
● After last character in string, there is a null
terminator '\0'
– placed there automatically for constants
– placed there automatically by library routines
● String library routines:
– strlen: returns length of string ('\0' not included)
– strcmp: compares two null-terminated strings;
same semantics as Java’s compareTo function
– strcpy: copies second parameter into first; must
be enough array space in first parameter or you can
get in trouble
The Problem With C Strings
● Very common to have buffer overflow
problems
– array allocated for string is not large enough
– need to remember null terminator
– cannot assume limits on input line length, etc.
● Common error
char *copy;
char orig[] = "hello"; // six character array
strcpy( copy, orig ); // crashes: no memory
Arrays of Strings
● Typically declared as char *arr[]
const char *ERRORS[ ] = { "Out of memory",
"Input value out of range", "Format error",
"Premature end of input" };
● Example is parameter to main
#include <stdio.h>
int main( int argc, char *argv[], char *envp[] )
{
int j;
printf( "ENVIRONMENT\n" );
for( j = 0; envp[j] != NULL; j++ )
printf( "%s\n", envp[ j ] );
}
C Pointer Dangers
● Returning a pointer to a static function
variable
– Must use value of object being pointed at prior to
next call to the function or it is overwritten
● Returning a pointer to a local variable
– Always wrong; local variable likely to be destroyed
and you have a stale pointer
● Returning a pointer to a dynamically allocated
local object
– You must take responsibility for calling free or
you have a potential memory leak
Structures (structs)
● Precursor to C++ classes
– no methods or constructors
– no private -- everything is public
– have to say struct when using type
● K&R C: cannot pass or return a struct
● ANSI C: OK to pass or return a struct
– but if struct is large, this is not a good idea, since
it involves a copy
● Structs are almost never passed to or returned
from functions. Instead pointers to structs are
used
Example: time.h
struct tm {
int tm_sec; /* seconds after the minute (0- 61) */
int tm_min; /* minutes after the hour (0- 59) */
int tm_hour; /* hours after midnight (0- 23) */
int tm_mday; /* day of the month (1- 31) */
int tm_mon; /* month since January (0- 11) */
int tm_year; /* years since 1900 (0- ) */
int tm_wday; /* days since Sunday (0- 6) */
int tm_yday; /* days since January 1 (0-365) */
int tm_isdst; /* daylight savings time flag */
};
typedef long time_t;
/* Some functions */
extern time_t mktime(struct tm *);
extern char *asctime(const struct tm *);
Illustration of Passing Structs
/* Find all Friday The 13th birthdays for person born Nov 13, 1973 */
#include <time.h>
#include <stdio.h>
int main( void ) {
const int FRIDAY = 6 - 1; /* Sunday Is 0, etc... */
struct tm theTime = { 0 }; /* Set all fields To 0 */
int year;
theTime.tm_mon = 11 - 1; /* January is 0, etc... */
theTime.tm_mday = 13; /* 13th day of the month */
for( year = 1973; year < 2073; year++ ) {
theTime.tm_year = year - 1900; /* 1900 is 0, etc... */
if( mktime( &theTime ) == -1 ) {
printf( "mktime failed in %d\n", year );
break;
}
if( theTime.tm_wday == FRIDAY )
printf( "%s", asctime( &theTime ) );
}
return 0;
}
Pointers to Functions
● Can pass functions as parameter to other
function
– technically you pass a pointer to the function
– syntax can look clumsy, but in ANSI C can avoid
clumsy syntax
double derivative( double f( double ), double x ) {
double delta = x / 1000000;
return ( f( x + delta ) - f( x ) ) / delta;
}
struct Command {
char *command;
void ( *func )( void );
};
qsort
● Generic sorting algorithm
void qsort( void *arr, int n, int itemSize,
int cmp( const void *, const void * ) );
Important Routines
● fopen and fclose
– open with a mode such as “r” or “w”
– fopen returns FILE *; NULL if error
● fprintf and fscanf
– work just like printf and scanf
– first parameter is a FILE *
● fgetc and fputc
– work like getchar and putchar
– last parameter is a FILE *
– often implemented as a preprocessor macro
More Routines
● fgets and fputs
– Reads/writes strings
– fgets reads a line or input, with a limit on number of
characters
● newline included in string if it was read
● make sure you have enough space for newline and ‘\0’
● feof
– returns true if read has already failed due to EOF
● fread and fwrite
– Allows reading of binary data into a struct or array
● fseek and ftell
– Allows random access of files
Example: File Copy: part 1
int copy( const char *destFile, const char *sourceFile ) {
int charsCounted = 0, ch;
FILE *sfp, *dfp;
fclose( sfp );
fclose( dfp );
return charsCounted;
}
Should I Use C
● Good reasons to not write C code
– have to manage your own memory for arrays and
strings
– variables must be declared at top of function
– I/O is much messier than C
– no overloading
– no classes or templates
– no type checking
● Reason to use C
– might be faster
– might need to interface to C library
Summary
● With C you lose many of C++ conveniences
such as
– strings/vectors
– type safety
– ease of variable declarations
● C is not object-oriented, or even object-based
● If you have to write C, you will miss C++
● If possible, write C++, and minimize use of C-
style logic