Jim Stackhouse Graduate Student Computing Sciences Summer 1996

The C Programming Language

The C Programming Language

The History of C
Language Structure and Semantics
Preprocessor Directives
Data Types and Variables
The ANSI C Header Files
Aphabetic Index to ANSI C Functions

The History of C

ALGOL (1960's, One of the first block-structured languages)
BCPL (Martin Richards)
B (Ken Thompson, Bell Labs)
C (Dennis Ritchie, Bell Labs)

Dennis Ritchie developed C essentially to write operating systems. Ritchie and Thompson along with co-worker Brian Kernighan, developed and rewrote the UNIX operating system kernel using Ritchie's C compiler on a DEC PDP-11/20 in 1973. It flourished from there, and today, C is the basis for object oriented languages like C++ and Java.
Go to top


The C programming language is acknowledged by many as being one of the most flexible and powerful languages for allowing a programmer to interact with a particular computing platform and operating system. This can occur on several levels of abstraction. By this I mean that there exists a robustness in the available tools provided to the C programmer, both in the form of libraries, as well as lower level elements of the language. On the second point, C allows bit manipulation and flexible interpretation of memory contents or variables through the use of pointers and type associations. Few restrictions are placed on the programmer relative to other languages. While providing tight and compact syntax for many operations, C can be quite challenging to interpret. That is to say, C code may be easier to write than to read. Much of this is up to the programmer's style.
Go to top

Language Structure and Semantics

C is a block structured programming language, consisting of one or more files making up the project. Contained in a given file you will find: Preprocessor directives are not C statements, but instructions for the C preprocessor, an entity that essentially expands macro definitions, conceptually glues together separate files that you have specified and can also hide sections of your program from the compiler using conditional compilation switches and statements.

Data in C, consists fundamentally of bytes stored in memory, which are not instructions for the cpu, but have some informational content for the program or programmer. These bytes can have more than one interpretation by the code referencing it depending on the context of the reference. The visibility or scope of a particular data(variable) item, to all of the code in a users program, can vary depending on it's physical placement in the program source code file, and by it's definition. Data also has a lifetime characteristic. Some variables are always in existance and others are created and destroyed dynamically as the program/programmer needs them.

Code in C, represents the actual high-level instructions that you want the processor to execute. You can have it interact with the user through devices like the keyboard, screen or printer. The code can and will also interact with different forms of storage, such as ram, rom and external disk, tape or optical media. C is a block structured language, based on the use of code functions. A function is a grouping of processor instructions, following defined rules as defined by C. Functions are called, when their name appears in a C line of code. They may have data passed to them in a pre-defined format, and can return a result of a pre-defined type. Furthermore, several functions relating to a common task, may be grouped together in a separate file for project organization purposes, or for inclusion in some future project. There is one special purpose function that all programs must have. It is the 'main()' function. This is the starting point for the computer when it begins executing your programs code. You may put whatever code or function calls you wish in 'main()'.
Go to top

Preprocessor Directives

	#define name string	:performs macros substitutions
		#define	TRUE	1
		#define	FALSE	0

	#error message		:compiler stops and displays line number and message

	#include filename	:compiler will also compile filename at this point
		#include <stdio.h>	searches standard include directory for stdio.h
		#include "stdio.h"	searches working directory for stdio.h

				:selectively compiles enclosed code sections 
				 based on specified condition
		#if constant expression
		  C code...
		#elif 		NOTE: #elif can only be used with #if blocks
		  C code...
		  C code...

	The following can also be used to start an #ifxxx block:
		#ifdef name	:if name is defined by previous #define
		#ifndef name	:if name is not defined by previous #define 

Go to top

Data Types and Variables

There are 5 basic built-in data types in C. They are:
	character		char
	integer			int
	floating point		float
	double floating point	double
	valueless		void
All of these except for void, may be modified by the following keywords:
Variables in C are case sensitive, so X and x are two different variables. Variable names may contain several characters including underscores("_") to form meaningful names. All variables must have some form of declaration before being used. The compiler needs to know what data type you wish to have associated with the memory contents for this variable, and appropriately allocate enough bytes of storage for it. Declaring a variable takes the following form...
	[modifier]	type	variable_name;
eg.	unsigned	int	myinteger;
	long		int	mylongint;
			char	mychar;
			float	myfloat;
A structure is a method for grouping several related data types together, such as someones name and address. The elements of the structure are referenced using the "." dot operator. If using a pointer to a structure, than the desired element can be referenced using the "->"" arrow operator, which dereferences the pointer to the structure.
	struct	AddressRec {
		char	first[15];
		char	last[20];
		int	housenum;
		char	street[20];
	} myaddr, *youraddr;

	myaddr.housenum		:would be the house number.

	youraddr = &myaddr;	(assign pointer to existing myaddr struct)
	youraddr->housenum	:would also be the house number.
If you take the definition of a structure, but overlap all the variables onto the same memory starting location, you end up with a union. Unions allow one to reference data bytes in any form outlined in the union's definition. The size of the union entity in memory will be that of the longest type listed in the union. Again, the dot operator is used to select the desired component.
	union multi {
		char	c;
		int	i;
		float	f;
	} myunion;

	myunion.c		:is the character representation 
				using the first byte
	myunion.i		:is the integer representation
				using the first 2/4 bytes depends on system
	myunion.f		:is the floating point representation
				using the first 4 bytes
An enumeration is a list of objects, which are sequentially assigned integer values, unless you overide the value in the definition.
	enum fruit {Apples, Oranges, Peaches};	:Apples=0, Oranges=1, Peaches=2
	enum fruit myfavfruit;
	myfavfruit = Apples
Arrays of a data type can be created using brackets after the variable name definition, including the size or number of elements of that type to allocate space for. The components of the array can then be accessed using the bracket and subscript notation. The array variable name when referenced without the brackets, refers to the address of the array in memory. Arrays are zero-based, and therefore have a maximum subscript of one less than the declared size number.
	int x[10];		:creates an array 0 to 9, of ints.
	x[3]			:references the 4th integer in the array.
	x[0]			:references the 1st integer in the array.
Strings in C, are simply character arrays, with a terminating null character, ascii '0'. This protocol for strings is accomodated where appropriate in the library functions defined by C. When defining string variables, space can be allocated and the terminating null character implicitly applied for you as follows:
	char mystring[] = "This is my string.";

Pointers play a major role in C programming. Many of the library functions require pointers to variables to be passed, so that they can be directly read or manipulated. Variables declared to be pointers, in C, are also required to be associated with a given data type, like pointer to int, or pointer to char array. In this way, pointer arithmetic can be supported by the C compiler, without the programmer needing to know the proper increment value to advance to another position in memory, of a given data type. Adding one to a float pointer, will actually cause the pointer to advance 4 bytes, the size of a float. The * dereference and & address operators are used in dealing with pointer types.

Storage Class Modifiers
These keywords are used with variable declarations, to specify specific needs or conditions associated with the storage of the variables in memory.
	extern		:used to indicate a variable is
			declared in another file
	auto		:variables declared inside
			of functions get this by default
	register	:requests placement in a cpu register
			for speed
	const		:variable may not be changed by
			the program during execution
	volatile	:the variable may be affected/changed
			by outside influences(clock, etc.)
	static		:keeps local variables in existance
			for duration of program and maintains
			its value, but restricts visibility
			to block where it was defined.
Scope and Lifetime of Variables
The scope or visibility of a variable is determined by it's placement within the C source code file. This, along with modifiers above, also determine when and how long a variable will exist for use within the program. Any variables declared outside of a function are considered global, and can be used by any line of code below the line it was declared. Typically, global variables are places at the top of the file. Local variables, however are those which are declared within the block structure of functions. They are created when the function is called and destroyed when the function exits, unless the static keyword is used in the variables definition.
Go to top


The most important function definition in C is the main() function. It is where program execution begins. When the program completes, it can return an integer code to the calling program, usually the operating system. The main function can be declared with arguments or without. The arguments that are declared, give the programmer access to the number of, and to the command line arguments passed to the program when it was executed. It is typically declared like this:
	int main(int argc, char *argv[]) {
		...your code here...

	argc		:number of arguments
	*argv[1]	:first character of first cmdline argument,
			null terminated
	*argv[0]	:is always the name of the program invoked,
			null terminated
Other users defined functions are what make up the bulk of a C program, along with the standard C library functions. Functions are usually given a prototype line either at the top of the source file, or in a separate file called a header file(.h extension). The header file, along with any other preprocessor directives, is then #include'd at the top of the source file. The prototype helps the compiler to resolve names, arguments and types associated with a function. Here is simple function definition:
	int	myadd(int a, int b) {
	  int tmp;
	  tmp = a + b;
Function parameters in C are passed by value to the function. This means that a copy of the variable being passed it pushed onto the stack for the function to work with. In many instances the programmer will want the actual variable being passed to be altered directly by the function. To accomplish this, pointers to variables can be passed to the function, allowing it to be directly referenced.
Go to top


C provides a rich set of operators for the programmers use. In order to avoid undesirable results from combinations of these operators, the programmer must understand the rules of evaluation that C uses. Operators are grouped into categories of precedence. An operator in an expression, whose precedence is 1 will have it's arguments evaluated before any other lower precedence operator. In addition, a rule of associativity is in effect for operators, which determines the logical direction of argument grouping around similar precedence operators. Here is a table which shows all of C's operators along with their precedence, associativity and the class of the operator.

1 ( ) subexpression and function call left to right primary expressions and postfix operators
[ ] array subscript
-> structure pointer
. structure member
++ increment(postfix)
- - decrement(postfix)
2 ! logical negation right to left unary
~ one's complement
++ increment(prefix)
- - decrement(prefix)
- unary negation
+ unary plus
(type) type cast
* pointer indirection
& address of
sizeof size of
3 * multiplication left to right multiplicative
/ division
% modulus(remainder)
4 + addition left to right additive
- subtraction
5 < < bitwise left shift left to right bitwise shift
>> bitwise right shift
6 < less than left to right relational
< = less than or equal
> greater than
>= greater than or equal
7 == equal test left to right equality
!= not equal test
8 & bitwise AND left to right bitwise
9 ^ bitwise exclusive OR left to right bitwise
10 | bitwise inclusive OR left to right bitwise
11 && logical AND left to right logical
12 || logical inclusive OR left to right logical
13 ?: conditional test right to left conditional
14 = plain assignment right to left assignment
+= add
-= subtract
*= multiply
/= divide
%= remainder
< < = bit left shift
>>= bit right shift
&= bit AND
^= bit exclusive OR
|= bit inclusive OR
15 , comma left to right sequence

Go to top


The ANSI C standard is comprised of 32 keywords, or reserved words. Many compilers have extended and added to these for different computing platforms. The keywords are as follows:
   auto		break		case		char
   const	continue	default		do
   double	else		enum		extern
   float	for		goto		if
   int		long		register	return
   short	signed		sizeof		static
   struct	switch		typedef		union
   unsigned	void		volatile	while
auto is used to create temporary or local variables that are created when entering a block and is destroyed on exit from the block. This is the default and is usually not needed explicitly.
	auto int j;

break is used to exit a do, for or while loop, and to exit from a switch statement preventing fallthrough to the next case item.
	while (j>0) {
	  if (i==10) break;

case is part of the switch statement contstruct, and specifies a condition to test. If true the statement block following the case keyword testing true, is executed until a break statement is reached.
	switch (i) {
	  case 1:	i++;
	  case 2:	i--;
	  default:	i=0;

char is a data type used to declare character variables.
	char ch;

const tells the compiler that the variable following, may not be modified.
	const char ch;

continue causes the rest of the code in a loop to be skipped, and control immediately goes to the condition statement of the loop to be tested for repeat.
	while (j>0) {
	  if (j==7) continue;

default is used in switch statements at the end, below case options, to tell the compiler to execute the code there if none of the case conditions returned true.
	switch (i) {
	  case 1:	i++;
	  case 2:	i--;
	  default:	i=0;

do is part of a do ... while looping construct, that has the test condition at the end of the loop.
	do {
	} while (i<10);

double is a data type specifier for double precision floating point variables.
	double mypaycheck;

else is part of an if construct, and specifies and alternate path of code execution when the condition in the if part returns false.
	if (i>=0) {
	else {

enum is a data type specifier, which creates sequentially assigned labels, and has a tag name created for the set of labels listed, which subsequently can be used to declare variables of that type.
	enum fruit {apples,peaches,oranges};
	enum fruit myfruit;

extern is a data type modifer used to indicate to the compiler that the variable is defined in another source file, and will also be used in the current source file.
	extern int varfromotherfile;

float is a data type for declaring floating point variables.
	float icecreamcost;

for is part of an interation or looping construct that provides an initialization statement, a condition statement for continuing the iterations, and and increment statement for advancing a counter in most instances.
	for (j=1; j<=10; j++) {
	  sum += j;

goto allows program execution to jump to a labeled line in the program. The label being a name followed by a colon.
	goto mylabel;

if part of a conditional construct, that checks the specified condition. If true (non-zero), the first statement block immediately following the if, is executed. If false(zero), an optional statement block beginning with the keyword else will be executed.
	if (k==0) {
	else {

int is a data type specifier used to declare integer value variables.
	int j;

long is a data type modifier used to declare double sized integer variables.
	long int k;

register is a declaration modifier that tells the compiler to use a cpu register for speed, instead of slower ram for the variable. This is typically done for implementing fast loop counter variables.
	register int loopcntr;

return causes execution to return to the function that called this one, and provides a mechanism for passing a return value associated with the function.
	int add(int a, int b) {

sizeof is a compile time operator that returns the length of a variable in bytes.
	int intsize;

signed is a data type modifier that indicates that the high bit of a data type is to be interpreted as a sign bit, allowing negative and positive number representations.
	signed int sint;

short is a data type modifier that declares integers to be only one byte long.
	short int shint;

static is a data type modifier that is used tell the compiler that a local variable is to have permanant storage, maintaining it's value between calls to the function where it is defined.
	static int j;

struct is a keyword used to create a data type called a structure, which is a grouping of other variable types, sequentially stored in memory and accessed through one structure variable using member operators.
	struct demostruct {
	  int j;
	  char ch;
	} mystruct;

switch is part of a multi-conditional statement tree, that tests a condition against a series of cases, using the case keyword to delineate the conditions. Upon locating a true condition, the statement block at that location is executed. If none pass, then an optional default branch can receive execution.
	switch (i) {
	  case 1:	i++;
	  case 2:	i--;
	  default:	i=0;

typedef is used to create new labels for existing data types.
	typedef float price;
	price coat;

union is used to assign two or more variables to the same memory location. It is syntactically similar to a struct statement.
	union numunion {
	  short int si;
	  long int li;
	  float f;
	} myunion;

unsigned is a data type modifier that tells the compiler to not interpret the high bit as a sign bit. Numbers are restricted therefore to only positive values.
	unsigned int uint;

void is a type specifier used to tell the compiler that nothing is returned by a given function. It is also used in declaring generic pointers, having no type associated with them.
	void *vptr;

volatile is a modifier used to tell the compiler that the contents of a variable may change due to outside influences, such as a clock tick updated by hardware interrupts.
	volatile int clocktick;

while is part of a looping construct the repeats it's enclose statements as long as the supplied condition evaluates to true.
	while (i>0) {

Go to top

The ANSI C Header Files

	assert.h	Diagnostics
	ctype.h		Character Handling and Conversion
	float.h		Floating Point Support
	limits.h	Maximum and Minimum Limits
	locale.h	International Support
	math.h		Mathematics
	setjmp.h	Jumps
	signal.h	Signal Handling
	stdarg.h	Variable Number of Arguments
	stddef.h	Standard Definitions
	stdio.h		Input and Output Support
	stdlib.h	General Definitions and Utilities
	string.h	String Handling
	time.h		Time and Date Support

Go to top

Alphabetic Index to ANSI C Functions

void abort(void); stdlib
int abs(int j); stdlib
double acos(double x); math
char * asctime(struct tm *timeptr); time
double asin(double x); math
void assert(int expression); assert
double atan(double x); math
double atan2(double y, double x); math
int atexit(void (*func)(void)); stdlib
double atof(char *nptr); stdlib
int atoi(char *nptr); stdlib
long atol(char *nptr); stdlib
void* bsearch(void *key, void *base, size_t nbr, size_t size, int (*compar)(void*, void*)); stdlib
BUFSIZ stdio
void * calloc(size_t nbr, size_t size); stdlib
double ceil(double x); math
void * clearerr(FILE *stream); stdio
CLK_TCK time
clock_t clock(void); time
clock_t time
double cos(double x); math
double cosh(double x);/TD> math
char * ctime(time_t *timer); time
double difftime(time_t time1, time_t time0); time
div_t div(int numer, int denom); stdlib
div_t stdlib
EDOM math
EOF stdio
ERANGE stdlib
errno stddef
void exit(int status); stdlib
double exp(double x); math
double fabs(double x); math
int fclose(FILE *stream); stdio
int feof(FILE *stream); stdio
int ferror(FILE *stream); stdio
int fflush(FILE *stream); stdio
int fgetc(FILE *stream); stdio
int fgetpos(FILE *stream, fpos_t *pos); stdio
char * fgets(char *s, int n, FILE *stream); stdio
FILE stdio
double floor(double x); math
double fmod(double x, double y); math
FILE * fopen(char *filename, char *mode); stdio
fpos_t stdio
int fprintf(FILE *stream, char *format, ...); stdio
int fputc(int c, FILE *stream); stdio
int fputs(char *s, FILE *stream); stdio
size_t fread(void *ptr, size_t size, size_t nbr, FILE* stream); stdio
void free(void *ptr); stdlib
FILE * freopen(char *filename, char *mode, FILE *stream); stdio
double frexp(double value, int *exp); math
int fscanf(FILE *stream, char *format, ...); stdio
int fseek(FILE *stream, long offset, int whence); stdio
int fsetpos(FILE *stream, fpos_t *pos); stdio
long ftell(FILE *stream); stdio
size_t fwrite(void *ptr, size_t size, size_t nbr, FILE *stream); stdio
int getc(FILE *stream); stdio
int getchar(void); stdio
char * getenv(char *name); stdlib
char * gets(char *s); stdio
struct tm * gmtime(time_t *timer); time
HUGE_VAL stdlib
int isalnum(int c); ctype
int isalpha(int c); ctype
int iscntrl(int c); ctype
int isdigit(int c); ctype
int isgraph(int c); ctype
int islower(int c); ctype
int isprint(int c); ctype
int ispunct(int c); ctype
int isspace(int c); ctype
int isupper(int c); ctype
int isxdigit(int c); ctype
jmp_buf setjmp
L_tmpnam stdio
long labs(long j); stdlib
double ldexp(double x, int exp); math
ldiv_t ldiv(long numer, long denom); stdlib
ldiv_t stdlib
struct tm * localtime(time_t *timer); time
double log(double x); math
double log10(double x); math
void longjmp(jmp_buf env, int val); setjmp
void * malloc(size_t size); stdlib
void * memchr(void *s, int c, size_t n); stdlib
int memcmp(void *s1, void *s2, size_t n); stdlib
void * memcpy(void *s1, void *s2, size_t nbr); stdlib
void * memmove(void *s1, void *s2, size_t nbr); stdlib
void * memset(void *s, int c, size_t n); stdlib
time_t mktime(struct tm *timeptr); time
double modf(double value, double *iptr); math
NDEBUG assert
NULL stddef
offsetof(structure, member) stddef
OPEN_MAX stdio
int perror(char *s); stdio
double pow(double x, double y); math
int printf(char *format, ...); stdio
ptrdiff_t stddef
int putc(int c, FILE *stream); stdio
int putchar(int c); stdio
int puts(char *s); stdio
void qsort(void *base, size_t nbr, size_t size, int (*compar)(void *, void*)); stdlib
int raise(int sig); signal
int rand(void); stdlib
RAND_MAX stdlib
void * realloc(void *ptr, size_t size); stdlib
int remove(char *filename); stdio
int rename(char *oldfname, char *newfname); stdio
void rewind(FILE *stream); stdio
int scanf(char *format, ...); stdio
SEEK_CUR stdio
SEEK_END stdio
SEEK_SET stdio
void setbuf(FILE *stream, char *buf); stdio
int setjmp(jmp_buf env); setjmp
char * setlocale(int category, char *locale); locale
int setvbuf(FILE *stream, char *buf, int mode, size_t size); stdio
sig_atomic_t signal
SIG_DFL signal
SIG_ERR signal
SIG_IGN signal
SIGABRT signal
SIGFPE signal
SIGILL signal
SIGINT signal
void (*signal(int sig, void(*func)(int)))(int); signal
SIGSEGV signal
SIGTERM signal
double sin(double x); math
double sinh(double x); math
size_t stddef
int sprintf(char *s, char *format, ...); stdio
double sqrt(double x); math
void srand(unsigned seed); stdlib
int sscanf(char *s, char *format, ...); stdio
stderr stdio
stdin stdio
stdout stdio
char * strcat(char *s1,char *s2); stdlib
char * strchr(char *s, int c); stdlib
int strcmp(char *s1, char *s2); stdlib
size_t strcoll(char *to, size_t maxsize, char *from); stdlib
char * strcpy(char *s1, char *s2); stdlib
size_t strcspn(char *s1, char *s2); stdlib
char * strerror(int errnum); stdlib
size_t strftime(char *s, size_t maxsize, char *format, struct tm *timeptr); time
size_t strlen(char *s); stdilb
char * strncat(char *s1, char *s2, size_T n); stdlib
int strncmp(char *s1, char *s2, size_t n); stdlib
char * strncpy(char *s1, char *s2, size_ n); stdlib
char * strpbrk(char *s1, char *s2); stdlib
char * strrchr(char *s1, int c); stdlib
size_t strspn(char *s1, char *s2); stdlib
char * strstr(char *s1, char *s2); stdlib
double strtod(char *nptr, char **endptr); stdlib
char * strtok(char *s1, char *s2); stdlib
long strtol(char *nptr, char **endptr, int base); stdlib
unsigned long strtoul(char *nptr, char **endptr, int base); stdlib
int system(char *string); stdlib
double tan(double x); math
double tanh(double x); math
time_t time(time_t *timer); time
time_t time
struct tm time
TMP_MAX stdio
FILE * tmpfile(void); stdio
char * tmpnam(char *s); stdio
int tolower(int c); ctype
int toupper(int c); ctype
int ungetc(int c, FILE *stream); stdio
type va_arg(va_list ap, type); stdarg
void va_end(va_list ap); stdarg
va_list stdarg
void va_start(va_list ap, parmN); stdarg
int vfprintf(FILE *stream, char *format, va_list arg); stdio
int vprintf(char *format, va_list arg); stdio
int vsprintf(char *s, char *format, va_list arg); stdio
_IOFBF stdio
_IOLBF stdio
_IONBF stdio

The following texts were consulted in preparing this web page:
Defenbaugh, JR., Smedley, Richard, C Through Design: An Introduction to ANSI C, Franklin, Beedle & Associates, 1988.
Schildt, Herbert, C: The Pocket Reference, Osborne McGraw-Hill, 1988.
Kernhigan, Brian W., Pike, Rob, The UNIX Programming Environment, Prentice Hall Software Series, 1984.
Borland C++ Programmers Guide, Borland International, 1991,92.

Also, see...
What is ANSI C?
Selected help on ANSI C
C Standard Library
Programming in C
Computers and Internet:Languages:C/C+
Douglass Mauro's 'C' Programming Links

Jim Stackhouse
Created Summer 1996