E12: Physical Systems Analysis II
This document is meant to serve as a brief introduction to the C programming language and the Metrowerks Codewarrior C development environment. There is enough information to write fairly involved programs with C, but it is nowhere near a complete treatment of the language.
When you need more information, look into the many books available in the library and bookstore. There are also numerous C resources on the web that are linked from the E12 Labs home page.
Metrowerks Codewarrior is an integrated development environment [IDE] that allows you to manage projects, compile programs, and debug them visually. It is a cross-platform program, which means it's look-and-feel is similar whether you are working on MacOS, BeOS, or some flavor of Windows. It also has the capability to compile Pascal, Java, C, and C++. For this course, we will only be using the C compiler, which is used by default on any file ending with a ".c".
Sart the CodeWarrior IDE by double-clicking on the Metrowerks icon on the desktop. Alternatively, you can select the Codewarrior IDE from the Start menu. The initial Codwarrior screen will be blank: you have to open a project to begin working.
Codewarrior--and most other modern IDE's--organizes files by projects. A project consists of all of the source code (C, Pascal, whatever), libraries (precompiled routines that you can call from your programs), and other resources necessary to make a fully functional, executable application. Therefore, the first thing you have to do when starting to create a program is create a new project.
From within CodeWarrior create a new project by going to the File menu and choosing New Project. This will bring up a window of template projects from which you can choose. Find the project template labeled NIDAQ and from the next menu pick AnalogIO. When the file dialog box appears choose a name for your project such as "hotbox". CodeWarrior will then create a new folder for you with the name of your project. To open the project in the future you can use the Open option from the File menu, or use the Open Recent list, also in the File menu.
Open the C source file "hotboxMain.c" from within the IDE by double clicking on its name in the project window (it should be at the top of the window). If you wish, you can now edit and save this file.
To run the program you can either chose the Run option from the Project menu, or click on the right-facing arrow at the top of the project window. You should do this now -- it takes several minutes to compile the first time through (it has to load the libraries), but only several seconds for each succeeding time.
If, at any time, you want to add new C source files to your project, select New from the File menu. This will create a new text file. Now save the file with the name you want it to have (don't forget the .c suffix). Then you have to add the file to your project by using the Add Window command under the Project window (make sure the file you want to add is the active window before you do this).
You are now ready to start writing and running your own programs. Though you can do everything from scratch, it is usually easier to start with a working program and just delete or comment out the parts you don't need.
#include <stdio.h> // The STanDard Input/Output C library.
/*
This is a block comment, using the slash-asterisk at each end
*/
main() {
printf("Hello world.\n"); // this is a single-line comment
}
This program is quite simple, but has many common features of C programs. The first line tells the compiler to include the input/output header file <stdio.h>, which is necessary when using input and/or output from the keyboard, screen or disk files. The program itself has only three lines. The first of these tells the compiler that this is the main function in the program, and that everything between the opening bracket { and the matching closing bracket } constitutes the main program.
The main program itself is only one line, a call to the printf function, which prints out the specified string. The special two-character symbol \n is a newline character which sends the cursor to the next line. Note that after every program statement there is a semi-colon. This is **very** important. By far the most common mistake is to forget the semi-colon.
After the printf statement there is a comment which is anything that occurs between a /* and the subsequent */, or anything on a line after a double slash, //.
Obviously this is a rather simple-minded program, but it has introduced you to the rudiments of a C program.
Almost all programs will use variables which are pieces of information stored in the computer's memory. The standard C language has a wide variety of variable types available. The types of interest to us are the following.
| C type | Size & Description | Range |
|---|---|---|
| char | 1 byte (8 bits) number or character | [-128, 127] or [0, 255] or ASCII |
| short | 2 byte integer | [-32768, 32767] or [0, 65535] |
| int | 2 (or 4) byte integers | [-32768, 32767] or [0, 65535] |
| long | 4 byte (long) integers | [-231, 231 - 1] or [0, 232-1] |
| float | 4 byte floating point numbers | 6 decimal places of accuracy |
| double | 8 to 12 byte floating point numbers | 10-12 decimal places of accuracy |
Each of the integral types {char, short, int, long} also has an unsigned version {unsigned char, unsigned short, unsigned int, unsigned long}. Unsigned types are always interpreted as positive numbers. The signed types are interpreted in such a way as to allow negative numbers to be represented as well.
The include files that you use may also define other variables types that you may use in your program. In generall these are just different names for one of the above types, but they can also be more complex structures and pointers.
The program below shows how variables are used.
#include <stdio.h> // standard I/O file
/*
main program
*/
main() {
short i, j, k[3]; // declare some short integers and an array
float x1, x2; // declare some floating point variables
i = 3; // assign values to integer variables
j = 4;
// print the sum of two variables
printf("%hd + %hd = %hd\n", i, j, i+j);
k[0] = 3; // assign values to an integer array
k[1] = 4;
k[2] = k[0] + k[1];
// print the sum of two variables
printf("%hd + %hd = %hd\n", k[0], k[1], k[2]);
x1 = 3.0; // assign values to the float variables
x2 = 4.0;
// print the sum of the two variables
printf("%f + %f = %f\n", x1, x2, x1+x2);
}
If we compile and run this program we will get the result:
3 + 4 = 7 3 + 4 = 7 3.0 + 4.0 = 7.0
Again we have a simple program that shows some new features of C. Note the semicolon after every program statement. The program declares 3 shorts: i, j, and an array k. It also declares two floats, x1 and x2. It then assigns values to each of the variables and print the sum. Note that we had to declare all of the variables that we used. This is a necessity in C. If you try to use any undeclared variables you will get errors when you try to compile the program.
Note that C arrays start at location 0. That means that since "k" is a three element array we could access locations k[0], k[1], and k[2]. There is no k[3]! If you want to work with character strings, they are simply arrays of type char.
The above code used the printf statement which is used to perform formatted printing in C. The general format for the printf statement is as follows:
printf("FormatString", arg1, arg2, ..., argn);
The FormatString specifies the format and the argN arguments are the variables to be printed (including none as in the first program example). If there is an variable to be printed its format is given by %_, where the underscore specifies the format. To print an integer use %d, use %f for a float, %lf for a double, %ld for a long integer, %c for a char, %hd for a short integer and %s for a string (which is simply and array of char).
You can also specify the number of digits to be used when printing. To print an integer in a column 3 wide you could use %3d. To print a float with 5 columns and two digits of precision use %5.2f, and so on. Check the rest of this handout for other examples of printf.
C has a variety of built in operations for performing math. Some of these operations are listed below.
| Binary operators (two operands) | |
| Addition | + |
| Subtraction | - |
| multiplication | * |
| Division | / |
| Modulus (remainder after division) | % |
| Unary operators (one operand) | |
| increment (inteers and chars only) | ++ |
| decrement (integers and chars only) | -- |
| negate | - |
In addition to manipulating numbers, C is also capable of handling logical expressions. When using these expressions TRUE is taken to be anything that is not zero, and FALSE is always equal to zero.
| Binary operators (two operands) | |
| Greater | > |
| Less | < |
| Equal | == |
| Greater or equal | >= |
| Less or equal | <= |
| Not equal | != |
| Logical and | && |
| Logical or | || |
| Unary operators (one operand) | |
| Logical complement (ie, not) | ! |
Often a series of instruction must be repeated over and over again. Instead of repeating the same operations repetitively it is useful to use a function that performs the repetitive operations. For instance to add two values and print the result:
#include <stdio.h>
void AddAndPrint(int arg1, int arg2) {
int sumOfArgs;
sumOfArgs = arg1 + arg2;
printf("%3d + %3d = %4d\n",arg1,arg2,sumOfArgs);
}
main() {
// declare two integer variables
int i, j;
// assign values to the variables
i = 3;
j = 4;
// print the sum
printf("%3d + %3d = %4d\n",i,j,i+j);
// Call the subroutine twice
AddAndPrint(i,j);
AddAndPrint(3,4);
}
Compiling and running this program gives:
3 + 4 = 7 3 + 4 = 7 3 + 4 = 7
This program introduces some new constructs. The most obvious is the function AddAndPrint which is at the top. The first line of the function declares that the function returns nothing (void), and has two arguments of the specified types. The body of the function (between the brackets) declares a variable, adds the two arguments and prints the result. Note that variables declared within functions (or within the main program) are not accessible to other parts of the program. Functions are used extensively in the two examples at the end of this document.
In C, when arguments are passed to a function, it is not the arguments themselves that are passed, but copies of the arguments. This is known as call-by-value, because only the value of the variable is passed to the function, not the variable itself. Therefore if you make changes to the variables within a function, their values will not have changed when the function is done and control is returned to the calling program.
If you want the function to change the value of an argument you must pass the address in memory where that variable is stored. This is known as call-by-reference. In this way the function can make changes to the arguments that are permanent; the changes will remain after control is returned to the calling program.
There are two operators used for manipulating memory address, also called "pointers", that are necessary to use functions that employ call-by-reference. These are "*" and "&". The operator "&" is the address operator and is used to get the address of (pointer to) a variable. The operator "*" does the opposite and takes a pointer and returns the value of the varible stored there. If you have a pointer k, the value stored at the memory address indicated by the pointer is *k. If you have a variable j, the pointer to the variable is given by &j. Therefore it should be clear that *(&j)=j.
If you have an array you can get the pointer to the first element of the array in two ways. Assume the array is declared as shown:
int p[30]; /* a 30 element array*/
The pointer to the first element is given by &(p[0]), or simply p. Refer to the examples at the back of the handout, and the next subsection, for a few examples of this.
A good example of the use of the pointer operator is the scanf function which is used to read input from the keyboard. Obviously this function needs to actually change the variables passed to it. For instance to read an integer variable "xx" you could use the following:
scanf("%d";,&xx);
To read a float "ff":
scanf("%f",&ff);
To read a string of characters that has previously been declared with a statement such as:
you would use:char x[80];
scanf("%s",x);
since "x" by itself gives the pointer to the first
element of the array (the beginning of the string).
What you have learned up to this point has been useful but is of limited utility because it does not allow for decision making capabilities by the computer. C has a variety of mechanisms to control the flow of a program. These are listed below.
The while loop
while (logical expression) {
...statements...}
While the logical expression is true, the statements (of which there are an arbitrary number) between the braces are executed. The following code prints the even numbers from 0 to 9 (the variable "a" must have been declared to be an integer elsewhere in the program).
a=0; while (a<10) { printf("a = %d.\n",a); a=a+2; }
The for loop
for (initial condition; logical expression; mathematical
expression) { ...statements...}
Set the initial condition, then while the mathematical expression is true execute the statement between the braces while executing the mathematical expression once at the end of each loop. Print the even numbers from 0 to 9:
for (a=0; a<10; a=a+2) { printf("a = %d.\n",a); }
The if...then
construct
if (logical expression) {
...statements...}
If the logical expression is true then evaluate the statements between the braces. The following code prints a number if it is even. The "%" operator returns the remainder after division. (If you divide a number by 2 and the remainder is zero, then the number is even).
if ((a%2) == 0) { printf("%d is even.\n",a); }
The if...then...else
construct
if (logical expression) { ...if
statements...}else { ...else statements...}
If the logical expression is true then evaluate the "if" statements between the braces, otherwise execute the "else" statements. The following code decides if a number if is even or odd.
if ((a%2) == 0) { printf("%d is even.\n",a); } else { printf("%d is odd.\n",a); }
The switch...case construct
switch (variable) {
case val1: ...statements 1...
break;
case val2: ...statements 2...
break;
case val3: ...statements 3...
break;
default: ...statemes default...
break;
}
The following code prints the value of the variable "a" if it is between 3 and 5 inclusive:
switch (a) { case 3: printf("a=3.\n); break; case 4: printf("a=4.\n); break; case 5: printf("a=5.\n); break; default: if (a<3) { printf("a<3.\n); } else { printf("a>5.\n); } break;}
There are other control-of-flow structures you can look up if you are interested.
There are five statements that you will need to perform file I/O (Input/Output). These are:
FILE *myOutFile, *myInFile; // This is a variable declaration for
// two files, one for input and
// one for output.
// Use fopen to open a file to be written to (denoted by "w").
myOutFile=fopen("myFileName", "w"); /*File name is "myFileName"*/
// Use fopen to open a file to be read from (denoted by "r").
myInFile=fopen("myNewFile", "r"); /*File name is "myNewFile"*/
// Use fprintf to do a formatted print to the file myOutFile
fprintf(myOutFile, "FormatString", arg1, arg2, ..., argn);
// Use fscanf to do a formatted read from the file myInFile
// Note that all of the arguments must be pointers.
fscanf(myInFile, "FormatString", arg1, arg2, ..., argn);
// Use fclose to close the files
fclose(myOutFile);
fclose(myInFile);
The use of these statments is pretty obvious. First you must declare a file as a variable, in the example above the variable myFile is a pointer to a variable of type FILE. Then you must open it to be read or to be written using fopen. You then use fprintf and fscanf to read or write to the file in the same way as you read and write from the keyboard and screen using printf and scanf. When you are done with the file you close it using fclose.
See the examples at the end of the handout to see how file I/O is used in practice.
You should now know enough to do some sophisticated things with C. This has been a very brief introduction to C and did not even begin to touch the richness available with the language. If you would like to know more look in the manuals in the lab, or any one of the books in the library or bookstore.
The next few pages give an example of two programs. One generates a series of pseudo-random integers and saves it in a file. The second program reads the file and calculates the average and variance. These programs serve no practical purpose except to demonstrate some of the concepts outlined above.
/****
This program generates a series of random numbers and saves them in a
file. It uses C's capabilities for random number generation. The user
is first prompted for a seed for the random number generator. The
numbers are then generated (the quantity of numbers is determined by
the constant MaxNum) in the function GenRand. The data are then saved
in the function SaveRand, which first prompts the user for a file
name. After the name is given the file is opened -- if this causes an
error the calling program is notified. If there was no error the data
are saved and the file is closed. A NO_ERROR condition is returned to
the calling program. Written by Erik Cheever, 1/94
****/
#include <stdio.h>
#include <stdlib.h> // This library contains random number stuff
#define MaxNum 100 // Number of random numbers generated
#define ERROR -1 // Constants used to signify errors
#define NO_ERROR 0
// Generate the random numbers and save in array
void GenRand(int *Nums) {
int i, randNum;
for (i=0; i<MaxNum; i++) {
randNum = rand(); // This is a (bad) library routine to generate a random number */
Nums[i] = randNum; // Save in array
}
}
// Saves random numbers, returns NO_ERROR if no errors
int SaveRand(int *Nums) {
int i;
char FileName[80];
FILE *OutFile;
printf("Enter file name: "); // Get name of file
scanf("%s",FileName);
OutFile = fopen(FileName,"w"); // Open the file
if (OutFile == NULL) { // If error, return and report
return (ERROR);
}
fprintf(OutFile,"%d\n",MaxNum); // First line in file is number of numbers
for (i=0; i<MaxNum; i++) { // Write the numbers to the file
fprintf(OutFile,"%d\n",Nums[i]);
}
fclose(OutFile); // Close the file
return(NO_ERROR); // Return, report no errors
}
main() {
int randNums[MaxNum], i, iseed;
// user interaction stuff
printf("This program generates a series of random values and then saves it.\n");
printf("Enter the seed for the random number generator:");
scanf("%d",&iseed);
// sets the seed value for the random number generator
srand(iseed);
GenRand(randNums); /*Note we use pointer to array since we want values changed*/
if (SaveRand(randNums) == NO_ERROR) {
printf("\nDone.\n");
}
else {
printf("Error in SaveRand.\n");
}
}
/********
This program reads a series of random numbers from a file and calculates and
prints the mean and variance of the numbers. The program uses three functions.
The first one prompts the user for a file name and then reads the numbers from
the file. It expects the first number in the file to be the number of data in
the file. It checks for errors and returns to the main program if there was a
problem. Note the use of pointers for arguments to this function.
The second function calculates, prints and returns the average of the numbers.
The third calculates, prints and returns the variance of the numbers.
Written by Erik Cheever, 1/94
Update by Bruce maxwell, 1/00
********/
#include <stdio.h>
#define MaxNum 1000 // Number of random numbers that can be handled
#define ERROR -1 // Constants used to signify errors
#define NO_ERROR 0
/*
This program reads a file of numbers and returns them in an array.
*/
int ReadFile(int *nums, int *maxNums) {
char FileName[80]; // Note that since maxNum is passed as a pointer we need to use
FILE *InFile; // the "*" operator to access the actual value of the variable.
int i;
/* Get name of file */
printf("Enter file name: ");
scanf("%s",FileName);
/* Open the file */
InFile = fopen(FileName,"r");
if (InFile == NULL) { /*If error, return and report*/
printf("\nCould not open file '%s'.\n",FileName);
return (ERROR);
}
/*
Note since maxNums is a pointer that we don't need to use the
"&" operator in the fscanf statement.
*/
fscanf(InFile,"%d",maxNums);
/*
Note that since maxNums is a pointer that we need to use the "*" operator when
we want to use the value stored where the pointer is pointing.
*/
if (*maxNums > MaxNum) { //Check if too many to be held in array
printf("\nToo much data in file (%d data).\n",maxNums);
fclose(InFile);
return (ERROR);
}
/* Read data into array and return */
for (i=0;i<*maxNums;i++) {
fscanf(InFile,"%d",&(nums[i]));
}
fclose(InFile);
return(NO_ERROR);
}
/*
This function calculates and prints the average of
an array of numbers. It also returns the average
value back to the main program.
*/
float CalcPrintMean(int *nums, int maxNums) {
int i;
float avg; /* Use a float for the average */
avg = 0.0;
for (i=0;i<maxNums;i++) { /* Calculate average... */
avg = avg + nums[i];
}
avg = avg/maxNums;
printf("Average = %7.2f.\n",avg); /* ...print it... */
return(avg); /* ...and return it. */
}
/*
This function calculate and prints the variance
an array of numbers. It also returns the variance
back to the calling program.
*/
float CalcPrintVar(int *nums, int maxNums, float avg) {
int i;
float SumSqr, temp, var;
SumSqr = 0.0; /* Calculate variance... */
for (i=0;i<maxNums;i++) {
temp = nums[i] - avg;
SumSqr = SumSqr + temp * temp;
}
var = SumSqr/maxNums;
printf("Variance = %7.2f.\n",var); /* ...print it... */
return(var); /* ...and return it. */
}
main() {
int randNums[MaxNum], maxNums;
float avg, var;
printf("This program will read a file written by RandomWrite and\n");
printf("calculate the mean and variance of the numbers in the file.\n");
/*
Note that in the call to the function ReadFile that both arguments RandNums and
maxnums are passed as pointers because we want their values to change in the
function. See the function above to see how it is handled there.
*/
if(ReadFile(randNums, &maxNums)==NO_ERROR) { /* If no error proceed...*/
avg = CalcPrintMean(randNums, maxNums);
var = CalcPrintVar(randNums, maxNums, avg);
}
else { /*...else report error*/
printf("Program ended because of error.\n");
}
}