This guide is intended to provide the student with examples of the C programming style expected in my classes. All labs will follow this style guide with a single exception. If a
student provides me
with a copy of the style guide adopted by that student's place of employment,
the student may use their employer's style guide.
Style in contemporary
programming is very important. You
should look on your programs in a Computer Science class as fulfilling two
functions:
1)
A working program written to solve a particular problem or set of problems.
2) A communication of your ideas and thoughts to your
instructor and fellow students. If your
instructor can't read your work, that will be reflected in your grade.
The guide will
include sections for capitalization, indentation, and each of the program
control structures allowed in C. There
will also be sections for user defined data types, including structured data
types.
Capitalization
Programs should
employ the capitalization style illustrated in Kernighan and Ritchie. Programs typed in all capital letters are
totally unacceptable.
A sample program
illustrating the "normal" C capitalization style:
/*
programmer:
D.R. Heckman
date:
3/9/91
fileid:
INTRO13B.C
purpose:
Example from Chapter 4 of Getting
Started modified slightly.
*/
#include
<conio.h>
#include
<stdio.h>
#include
<ctype.h>
#define QUIT
'Q'
typedef char
Commands;
int main()
{
Commands
command;
printf(
"Chart desired: Pie Bar Scatter
Line Three-D" );
printf(
"\nPress enter first letter of the chart you want or "
" Q to quit!\n" );
do {
command
= toupper( getchar() );
if(
command == 'P' )
printf( "Doing pie chart\n" );
else if
(command == 'B')
printf( "Doing bar chart\n" );
else if
(command == 'S')
printf( "Doing scatter chart\n" );
else if
(command == 'L')
printf( "Doing line chart\n" );
else if
(command == 'T')
printf( "Doing 3-D chart\n" );
else
printf( "Invalid choice.\n" );
} while(
command != QUIT );
return( 0
);
}
Indentation of data
structures and program structures is very important in communicating your
design. Proper use of indentation should
be observed in writing C code and pseudo code.
Select the
indentation option in your IDE to be three or four spaces. Do not use tab characters to perform this
indentation. If your editor inserts tabs
into your source code files, remove the tabs before saving the files.
See the remaining
sections for examples of indentation with data structures and program
structures.
Identifiers:
All user defined
identifiers should be composed of one, two, or three whole words and/or
standard abbreviations. Examine the code
segments in this guide for examples.
Variables
and function names should start
with a lower case alphabetic character.
Type names should start with an upper case alphabetic character.
Named constants and symbolic
constants should be composed of all upper case
alphabetic characters and the under_score.
Parentheses:
Parentheses should be
used liberally to aid in the "readability" of your programs and
pseudo code. Examine the code segments
in this guide for examples in the use of parentheses.
One-Tailed Alternation
Specification
IF THEN
The if then
structure, also called the one-tailed alternation or specification structure,
allows a sequence of operations to be performed if a condition is true.
Example with one
resulting operation:
if( result == 0.0
) then
puts(
"\nResult is zero!" );
Example with multiple
operations:
printf(
"Result is %lf \n", result );
if( count > 0 )
{
average =
result / count;
printf(
"Average is %lf \n", average );
} /* endif */
Another acceptable
style of placing the { and } is:
printf(
"Result is %lf \n", result );
if( count >
0 )
{
average =
result / count;
printf(
"Average is %lf \n", average );
} /*
endif */
Note use of
semi-colons ( ; ) to TERMINATE statements.
Note use of
indentation and placement of /* endif */
Two-Tailed Alternation
Selection
IF THEN ELSE
The if then else
structure, also called the two-tailed alternation or selection structure,
allows a sequence of operations to be performed when a condition is true and
another sequence when the condition is false.
Example with one
resulting operation:
if( result == 0.0
)
puts(
"Result is zero!" );
else
puts(
"Result is non-zero!" );
Example with multiple
operations:
if( count > 0 )
{
printf(
"Result is %lf \n", result );
average =
result / count;
printf(
"Average is %lf \n", average );
flag = OK;
}
else {
puts(
"There are no valid entries!" );
flag = ERROR;
} /* endif */
Another acceptable
style of placing the { and } is:
if( count > 0 )
{
printf(
"Result is %lf \n", result );
average =
result / count;
printf(
"Average is %lf \n", average );
flag = OK;
}
else
{
puts(
"Result is bad!" );
flag = ERROR;
} /* endif */
Note use of
semi-colons ( ; ) to terminate statements.
Note use of
indentation and placement of /* endif */.
Multi-Tailed Alternation
Multi-Way Decision
CASE
The case structure,
also called the multi-tailed alternation or multi-way decision making, allows
different sequences of operations to be performed when various possible
outcomes of a condition are true.
Example with one
resulting operation:
switch( selector )
{
case 0 : puts(
" 0 is the case!" ); break;
case 1 : puts(
" 1 is the case!" ); break;
case 2 : puts(
" 2 is the case!" ); break;
case 3 :
case 4 :
case 5 : puts(
"between 3 and 5 is the case!" );
break;
} /* end cases */
or, another acceptable style is
switch( selector )
{
case 0 : puts(
" 0 is the case!" ); break;
case 1 : puts(
" 1 is the case!" ); break;
case 2 : puts(
" 2 is the case!" ); break;
case 3 :
case 4 :
case 5 :
puts(" between 3 and 5 is the case!" );
break;
} /* end cases */
Multiple statements
may also be invoked for specific outcomes:
switch( selector )
{
case 0 : puts(
" 0 is the case!" ); value = A;
break;
case 1 : puts(
" 1 is the case!" ); value = E;
break;
case 2 : puts(
" 2 is the case!" ); value = I;
break;
case 3 : puts(
3 is the case! ); value = O;
break
case 4 : puts(
" 4 is the case! ); value = U;
break;
} /* end cases */
CASE continued
An "otherwise
clause" may also be used to ensure the outcomes are exhaustive:
switch( selector )
{
case 0 : puts(
" 0 is the case!" ); break;
case 1 : puts(
" 1 is the case!" ); break;
case 2 : puts(
" 2 is the case!" ); break;
default : puts(
" more than 2 is the case!" ); break;
} /* end cases */
When the
"Selector Variable" is not ordinal, the IF THEN ELSE must be used in
a "Block If" construction.
if( selector ==
0.0 ) {
puts( " 0
is the case!" );
value = 0;
}
else if( selector
== 1.0 ) {
puts( " 1
is the case!" );
value = 1;
}
else if( selector
== 2.0 ) {
puts( " 2
is the case!" );
value = 2;
}
else {
puts( "
more than 2 is the case!" );
value = -1;
} /* endif */
Notice that the Block
If construction may be terminated with an else to ensure the conditions tested
are exhaustive.
CASE continued
The Block If
construction should not be confused with nested alternation ( nested ifs
). Nested alternation is used when there
are several different conditions to be tested. Look carefully at the following example of
selecting the largest of three values.
Note that the conditions used in the if statements employ different
conditions, and the conditions are dependent upon one another.
if( first >
second )
if( first >
third ) {
puts( "
First was larger!" );
big = first;
}
else {
puts( "
Third was larger!" );
big = third;
} /* endif */
else
if( second >
third ) {
puts( "
Second was larger!" );
big =
second;
}
else {
puts( "
Third was larger!" );
big = third;
} /* endif */
/* endif */
CASE continued
The "block
style" placement of { and } is also acceptable:
if( first >
second )
if( first >
third )
{
puts( "
First was larger!" );
big = first;
}
else
{
puts( "
Third was larger!" );
big = third;
} /* endif */
else
if( second >
third )
{
puts( "
Second was larger!" );
big =
second;
}
else
{
puts( "
Third was larger!" );
big = third;
} /* endif */
/* endif */
FOR Loop
Counter Controlled Loop
Repetition a Known
Number of Times
The For Loop
structure is also called repetition a known number of times, because, that is
exactly what it allows. Repetition of a
sequence of operations a specific numbers of times.
Example with one
repeated operation:
sum = 0.0;
for( i = 0; i <
100; i++ )
sum += x[i];
Example with multiple
repeated operations:
sum = 0.0;
for( index = 0;
index < 100; index++ ) {
printf(
" adding element %d \n", index
);
sum += data[index];
} /* endfor */
or with the block
style, which is also acceptable:
sum = 0.0;
for( index = 0;
index < 100; index++ )
{
printf( "
adding element %d \n", index );
sum +=
data[index];
} /* endfor */
Note use of
indentation and placement of /* endfor */
Rules for use of the
index or counter variable and variables used for the upper and lower bounds of
a for do loop.
1) The for loop index
or counter variable may not be altered within the body of the for loop. The for loop will increment the index or
counter as required.
2) Variables used for
the upper and lower bounds of a for loop may not be altered within the body of
the loop.
WHILE Loop
Pre-Test
Loop
Repetition an Unknown Number of
Times
The While structure
is also called repetition an unknown number of times, perhaps not at all. The While structure is also called a pre-test
loop. Since the while test is performed
before the body of the loop is executed.
The steps within the body of the loop may never be executed. There are three fundamental rules for use of
the While Loop.
1)..The While Loop is used when it is not known initially
how many repetitions of the loop will be necessary.
2) During execution
of the statements in the body of the while loop, an action will eventually take
place that causes the While Loop to stop repetition.
3) The condition used
by the While Loop must be initialized to some meaningful value before the While
Loop is executed. Or, in other words,
the While Loop must be "primed".
Example with one
repeated operation:
FILE * filePointer;
char ch;
filePointer =
fopen( "TRIALS.DAT", "rt" ));
if( filePointer ==
NULL ) {
fprintf(
stderr, "Cannot open input file. \n" );
return( 1 );
}
while( !feof(
filePointer ))
ch = fgetc(
filePointer );
fclose(
file_pointer );
Example with multiple
repeated operations:
count = 0;
while( !feof(
filePointer )) {
fgets( line,
127, filePointer );
printf(
"%s \n", line );
count++;
} /* endwhile */
fclose( filePointer
);
WHILE continued
The block style of
placing the { and } is also acceptable:
count = 0;
while( !feof(
filePointer ))
{
fgets( line,
127, filePointer );
printf( "%s
\n", line );
count++;
} /* endwhile */
fclose( filePointer );
Note use of
indentation and placement of /* endwhile */.
DO WHILE
Post-Test Loop
Repetition an Unknown Number of
Times
The Do While
structure is also called repetition an unknown number of times, at least
once. The Do While structure is also
called a post-test loop. Since the Do
While test is
performed after the body of the loop is
executed, the steps within the body of the loop are executed at least one
time. There are two fundamental rules
for use of the Do While Loop.
1) The Do While Loop
is used when it is not known initially how many repetitions of the loop will be
necessary.
2) During execution
of the statements in the body of the Do While Loop, an action will eventually
take place that causes the loop to stop repetition.
Example with one
repeated operation:
do
printf( "
INFINITE LOOP " );
while( -1 );
DO WHILE continued
Example with multiple
repeated operations:
do {
printf(
"Please enter a value between 1 and 5: " );
scanf(
"%d", &entry );
if( entry < 1
|| entry > 5 )
puts(
"Invalid entry, please try again!" )
} while( entry <
1 || entry > 5 );
or the block style is also acceptable:
do
{
printf( "Please enter a value between
1 and 5: " );
scanf(
"%d", &entry );
if( entry < 1
|| entry > 5 )
puts(
"Invalid entry, please try again!" )
}
while( entry < 1
|| entry > 5 );
USER DEFINED DATA TYPES
User-defined data
types fall into two groups, 1) scalar data and 2) structured data.
ENUMERATED DATA
The user defined
scalar data type allowed in ANSI C is the enumerated type. Rules for using enumerated types are as
follows:
1) The limitation on enumerated type use is the focus of
this rule -- variables of enumerated types display unsigned integers when
written to text oriented devices (i.e. read from the keyboard or written to the
display ).
2) If enumerated types are to be used, input from the
external world must be encoded into an enumerated type variable and output in
the form of an enumerated type variable must be decoded before it can be
written.
3) The "data objects" used within the definition
of an enumerated type should be treated like the identifiers of named
constants.
ENUMERATED DATA
continued
Example:
typedef enum {
SAMSTAG, SAMSTAGABEND } PartyDays;
typedef enum {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY } Days;
int main()
{
PartyDays
myDayOff;
Days singleDay;
int entry;
/* prompt for
and read entry, encode into enumerated type */
printf( "
When do you want to party? " );
scanf(
"%d", & entry );
if( entry == 1 )
{
myDayOff = SAMSTAG;
puts(
"SAMSTAG selected!" );
}
else {
myDayOff =
SAMSTAGABEND;
puts(
"SAMSTAGABEND selected!" );
} /* endif */
/* prompt for and
read entry, encode into enumerated type */
do {
printf(
"Enter day number ( 0..6 ):" );
scanf(
"%d", & entry );
switch( entry
) {
case 0 :
theDay = SUNDAY; break;
case 1 :
theDay = MONDAY; break;
case 2 :
theDay = TUESDAY; break;
case 3 :
theDay = WEDNESDAY; break;
case 4 :
theDay = THURSDAY; break;
case 5 :
theDay = FRIDAY; break;
case 6 :
theDay = SATURDAY; break;
default :
puts( Invalid entry, try again! ); break;
} /* end
cases */
} while( entry
< 0 || entry > 6 );
/* code continued
on next page */
USER DEFINED DATA
TYPES continued
/* code from the
previous page continued */
/* evaluate encoded
enumerated type */
switch( theDay )
{
case SUNDAY :
case SATURDAY
:
puts(
"That is not a work day\\\1" );
break;
case MONDAY :
case TUESDAY
:
case
WEDNESDAY :
case THURSDAY
:
case FRIDAY :
puts(
"Yes, that is a work day!" );
break;
default :
puts(
"Error condition, invalid day entry!" );
break;
} /* endcases */
return( 0 );
}
ARRAYS
Arrays and their
subscript variables should always be implemented with type declarations. Identifiers for the array type declarations
and actual array variables should be chosen carefully.
Rules about types:
1) You can't put data
in a type, so chose the type identifier carefully so there is no conflict with
the variable identifier. I usually
recommend that beginning students include the word "type" in all type
identifiers.
2) You are going to
use the array variable identifier many times in your program, so chose a
concise representative name.
Examples:
#define MAX_ITEMS
100
#define MAX_CELLS
200
#define STRING_LEN
256
#define PATH_LEN
127
typedef int
Frequency[MAX_CELLS];
typedef float
ArrayType[MAX_ITEMS];
typedef char
String[STRING_LEN +1];
typedef char
DosPath[PATH_LEN];
Frequency
frequency;
ArrayType array;
String string;
DosPath fileName;
STRUCTS
structs allow the
encapsulation of several data fields of different data types. structs should always be implemented with type
declarations. Identifiers for struct
types and actual struct variables should be chosen carefully.
Rules for using
structs:
1) You can't put data
in a type, so chose the type identifier carefully so there is no conflict with
the variable identifier. I usually
recommend that beginning students include the word "type" in all type
identifiers.
2) Choose the
identifiers for fields within a struct just as carefully as you choose the type
identifier itself.
3) structs can not be defined within structs but
they may be used within structs.
Example:
#define KEY_LEN 8
#define MAX_ITEMS
100
#define FIRST_LEN
20
#define LAST_LEN 25
typedef char
KeyType[KEY_LEN +1];
typedef char
FirstName[FIRST_LEN +1];
typedef char
LastName[LAST_LEN +1];
typedef struct {
KeyType key;
LastName
lastName;
char
middleInitial;
FirstName
firstName;
int age;
} PersonnelRecord;
/* code continued
on next page */
/* code continued
from previous page */
int
readPersonnelRecord( FILE * fp,
PersonnelRecord * pPR )
{
char initial;
int age;
fscanf( fp,
%8s, pPR->key );
if( feof( fp ))
return( EOF
);
if( strlen(
pPR->key ) == 0 )
return(
IO_FAIL );
fscanf( fp,
%25[^\n]%20[^\n]%c%d%*c,
pPR->lastName, pPR->firstName, & initial, & age );
pPR->middleInitial = initial;
pPR->age =
age;
return( 0 );
}
Unions may be used to
"redefine" an area in memory to be used with two different types of
data. More commonly, unions are used
together with struct to create a "Variant Record". Records are classed as variant records when
they have a common beginning part but different endings. This is a common requirement in the real
world.
Rules for using
unions:
1) All the rules cited for structs apply
2) That field identifiers in the variations of a
variant record must be unique.
3) A Variant Record
commonly employs a "tag field" to tell the program which ending is
being used.
UNIONS continued
Example
#define NAME_SIZE
20
#define NUMBER_SIZE
9
#define REASON_SIZE
80
#define
MAX_EMPLOYEES 10
typedef struct {
char month[3]; char day[3]; char year[3];
} Date;
typedef struct {
Date fired; char reason[REASON_SIZE +1];
} Fired;
typedef struct {
Date left; char reason[REASON_SIZE +1];
} Left;
typedef struct {
Date employed; int max_hours;
} Temp;
typedef struct {
Date hired; float pay_rate; int position_level;
} Full;
typedef union {
Fired fired;
Left left;
Temp temp;
Full full;
} VariantPart;
typedef struct {
char
name[NAME_SIZE +1];
char
number[NUMBER_SIZE +1];
char
status; /* tag field */
VariantPart
varies;
} EmployeeRecord;
TEXT FILES
There are actually
two implementations of files in C: Text
Files and Binary Files.
Text Files are
universal streams of ASCII characters which may be interpreted by fscanf() and
fgetc(), and produced ( output ) by fprintf() and fputc().
Text Files require
some external linkage with the operating system. This is done with the functions fopen(),
feof(), and fclose(), all of which work with a "File Pointer".
Example:
#define KEY_LEN 8
#define MAX_ITEMS
100
#define FIRST_LEN
20
#define LAST_LEN 25
typedef char
KeyType[KEY_LEN +1];
typedef char
FirstName[FIRST_LEN +1];
typedef char
LastName[LAST_LEN +1];
/* code
compressed to fit on this page */
typedef struct {
KeyType key;
LastName lastName; char middleInitial;
FirstName
firstName; int age;
} PersonnelRecord;
int listRecords(
char fileName[] )
{
FILE * fp;
PersonnelRecord
pR;
char initial;
int age;
fp = fopen(
fileName );
if( fp == NULL )
return( 1 );
while( !feof( fp
)) {
fscanf( fp,
%8s, pPR->key );
if( feof( fp
)) continue;
if( strlen(
pPR->key ) == 0 ) continue;
fscanf( fp,
%25[^\n]%20[^\n]%c%d%*c,
pR.lastName, pR.firstName, & initial, & age );
pR.middleInitial = initial; pR.age = age;
printf(
"%25s %20s %c %3d \n",
pR.lastName, pR.firstName, pR.middleInitial, pR.age );
}
fclose( fp );
return( 0 );
}
BINARY FILES
Binary Files are only
readable on the system on which they were written. You may find that some implementations of C
do not support Binary Files.
Both Text Files and
Binary Files require some external linkage with the operating system and
positioning of the file pointer.
External linkage is accomplished with the fopen() for Text Files and
open() for Binary Files. The Binary File
handling functions open(), close(), and eof() all work with "File
Handles".
Example:
#define KEY_LEN 8
#define MAX_ITEMS
100
#define FIRST_LEN
20
#define LAST_LEN 25
/* code
compressed to fit on this page */
typedef char
KeyType[KEY_LEN +1];
typedef char FirstName[FIRST_LEN
+1];
typedef char
LastName[LAST_LEN +1];
typedef struct {
KeyType key;
LastName lastName; char middleInitial;
FirstName
firstName; int age;
} PersonnelRecord;
int main()
{
PersonnelRecord
pR;
FILE* fp;
int fileHandle;
fp = fopen(
"pers.dat", "r" );
fileHandle =
open( "pers.bin", O_WRONLY | O_CREAT | O_APPEND | O_BINARY );
while(
readPersonnelRecord( fp, & pR ) == 0 )
writePersonnelRecord( fileHandle, & pR );
fclose( fp );
close(
fileHandle );
return( 0 );
}
void
writePersonnelRecord( int fh, PersonnelRecord * pPR )
{
int length;
length = write(
fh, pPR, sizeof( PersonnelRecord ));
if( length !=
sizeof( PersonnelRecord )) {
fprintf(
stderr, pers: binary file output failed! \n );
exit( 1 );
}
}