C Handout 7 – Loops, #define and arrays
(10/24/00)
Loops
Often
you will want to execute a section of code more than once. The mechanism for doing this is called a
loop. We will discuss only tow of the
loops available in C: the while loop
and the for loop.
while (logical-expression) C-statement-block
In this construct the C-statement-block gets repeatedly executed as long as the logical expression is TRUE. You’ve already seen this construct in action. The program you wrote last week had the form:
void main() {
while (1) {
.
. /* the bulk of your code would go here */
.
}
}
In this example the code following the while loop gets executed forever, since the logical expression, (1), always evaluates to TRUE.
If we wanted the program to stop when you hit the stop button we could use the following:
void main() {
while (!stop_button()) {
.
. /* the bulk of your code would go here */
.
}
}
Recall that stop_button() returns TRUE if the stop button is pressed, and FALSE if not. So this code will loop, as long as the stop button is not pressed.
for (initialization; logical-expression; action)
C-statement-block
This construct is a bit more complicated, and is probably best expressed with an example. Assume an integer variable, speed, has been declared elsewhere in the program, along with the function SetSpeed().
/*Accelerate slowly
(so wheels don’t spin*/
for (Speed=0;
Speed<=100; Speed=Speed+10;) {
SetSpeed(Speed, Speed);
sleep(0.1);
}
The for loop is really just a shorthand for the while loop. The code above is equivalent to this:
/*Accelerate slowly
(so wheels don’t spin*/
Speed=0;
/* initialization*/
while (Speed<=100){
/* logical expression*/
SetSpeed(Speed, Speed);
sleep(0.1);
Speed=Speed+10;
/* action*/
}
Note that the initialization statement comes before the loop, the logical expression is checked by the while loop, and the action is taken at the end of the loop.
The C pre-processor
(#define)
Any
program can be difficult to read. To
make C code a little easier to read, you can use the pre-processor to read you
can use the C pre-processor to define constants to make your code easier to
read. The pre-processor directives
(i.e., #define’s) must go at the top of your file.
For
example if we put the following lines at the top of our file.
#define FALSE 0
#define TRUE !FALSE
#define LEFTMOTOR 0
#define RIGHTMOTOR 1
#define FWD_FULL 100
#define RVS_FULL -100
#define STOP_MOTOR 0
#define LEFT_BUMPER !digital(7)
#define RIGHT_BUMPER !digital(8)
When the C compiler comes across the defined term, it replaces it with the expression on the left. The defined term does not need to be in all caps, but this is a convention to distinguish these constants from variables. This allows us to write more easily understood code. This is probably best seen with some code snippets.
To control the motor, we could use command such as:
motor(LEFTMOTOR,FWD_FULL);
/*Set left motor to max spedd*/
motor(RIGHTMOTOR,STOP_MOTOR);
/*Stop the right motor*/
If we use the previous example with the while loop, we could write:
void main() {
while (stop_button()==FALSE)
{
.
. /* the bulk of your code went here */
.
}
}
FWD_FULL=95; /*Error,
#defined quantities cannot be changed!!!*/
Pre-processor
directives can have arguments (this example is from the interactive C help
page).
#define RIGHT_MOTOR 0
#define LEFT_MOTOR 1
#define GO_RIGHT(power)
(motor(RIGHT_MOTOR,(power)))
#define GO_LEFT(power) (motor(LEFT_MOTOR,(power)))
#define GO(left,right)
{GO_LEFT(left); GO_RIGHT(right);}
void main()
{
GO(0,0);
}
Arrays
Often
it is convenient to be able to have multiple data stored, without using a
separate variable for each. The
structure used to implement this in C is called an array.
For
example, you might want to store the readings from the four photo-cells on your
robot in a single entity. You could do
this with an array.
void main() {
int i; /*loop counter*/
int cds[4]; /*Array declaration*/
/*Sample all 4 CdS cells*/
for (i=0;i<4;i++) {
cds[i]=analog(i);
}
printf(“\nCds Cell 3 reads
%d.”,cds[3]);
}
The array has 4 elements, c[0], c[1], c[2], and c[3]. Note that the array index starts at 0, and so only goes to N-1, where N is the length of the array.
int threshold[]={120, 131,
119, 110};
This code fragment define 4 threshold values for the light sensors, to distinguish light from dark, one for each of the four sensors. You could compare these 4 values against the four readings to determine which is sensing light vs dark.
A short example that the
concepts introduced.
#define FALSE 0
#define TRUE !FALSE
/*
Use #define to define
constants.
#define's are
implemented as a simple text substitution.
These need two go at the
top of the file
/*
/*
Define an integer
globally.
done is a flag to tell
main program when to end.
*/
int done;
/*
This function will be
used as a separate process.
It plays 3 notes,
defined in the array c[].
*/
void PlayNotes() {
int i;
/*loop counter*/
float MyNotes[]={250.0, 250.0,
100.0};
for(i=0;i<3;i++) { /*loop trough notes*/
tone(MyNotes[i],0.5); /* Play note*/
sleep(0.1); /* delay*/
}
done=stop_button(); /*Done will be TRUE if stop is pressed.*/
}
void main() {
int i=0; /*Declare a variable and initialize
it.*/
done=FALSE; /*This is a
global variable*/
while (!done) {
PlayNotes();
sleep(1.0);
i++;
}
printf(“\nPlayed notes %d
times.”,i);
}
Note
that in this example, the main routine stops running while the notes are being
played. There is an example later in
this handout where the main routine continues to run while the music plays.
Review Sheet – Loops, #define and arrays
(Use both sides – problems 1 through 4)
Due 10/26 when you come to lab.
Name:______________________________________
You may work in groups, but each
person should turn in a sheet.
1) What
is the sequence of numbers printed by the following?
#define MAXN 10
void main() {
int i;
for (i=0; i<MAXN; i+=2) {
while (i==6) i--;
printf(“\n %d”,i);
}
}
2) Write
a snippet of code that performs the following.
Given the array x[], compute a new array y[] that is the average of two
consecutive values of x[]. Note that
there is one less element in y[] than in x[].
Start with
void main() {
float x[]={1.0, 2.5, 3.0, 2.0, 2.5};
float
y[4];
/*add your code here*/
}
3) Write code that performs the following. Given a function represented by the dotted
line, the array x[] and the array t[]represent sampled values of the function (i.e.,
the pairs (t[],x[]) represent the coordinates shown as “x” on the plot).
Compute and print the approximate value of the integral as given by the sum of the areas of the rectangels show, between t=0 and t=5. Start with
void main() {
float x[]={1.0, 2.5, 3.0, 2.0, 3.5};
float t[]={0.0, 1.0, 2.0, 4.0, 5.0};
/*add your code here*/
}
4) For
the CdS sensors on the robot, a lower reading represents more light fallin on
the sensors. Assume the sensors are all
identical (not a good assumption in practice).
Write a snippet of code that speeds up the left motor (0), and slows
down the right motor (1) as long as more light falls on sensor 2 (left inside
sensor) than on sensor 3 (right inside sensor).
Multi-tasking (Feel free to skip this subject if you
want to).
The version of C that we are using allows you to
make the processor perform more than one operation at a time. This is referred to as multi-tasking. To start a process that will run at the same
time as your main program, use the function “start_process()”. After this routine is executed, the
operating system will run each concurrent process at the same time. In actuality, each process is run for a
short time before being pre-empted by the next process . This pre-emption process happens many times
per second -- and it appears to you, the programmer, as if the processes are
running at the same time, in parallel.
An example is given on the next page, in which a
process is started to play music, while the main program continues to do other
things. This is in contrast to the
previous example, in which the main program suspends execution while the music
plays.
If you want more information on this multi-tasking,
either consult the “help” that comes with Interactive C, or talk to me.
The following overview is taken from the Interactive C help manual and describes the process in more detail:
One of the
most powerful features of IC is its multi-tasking facility. Processes can be created and destroyed
dynamically during run-time.
Any C
function can be spawned as a separate task.
Multiple tasks running the same code, but with their own local
variables, can be created.
Processes
communicate through global variables: one process can set a global to some
value, and another process can read the value of that global.
Each time
a process runs, it executes for a certain number of ticks, defined in
milliseconds. This value is determined
for each process at the time it is created.
The default number of ticks is five; therefore, a default process will
run for 5 milliseconds until its "turn" ends and the next process is
run. All processes are kept track of in
a process table; each time through the table, each process runs once (for an
amount of time equal to its number of ticks).
Each
process has its own program stack. The stack is used to pass arguments for
function calls, store local variables, and store return addresses from function
calls. The size of this stack is
defined at the time a process is created.
The default size of a process stack is 256 bytes.
Processes
that make extensive use of recursion or use large local arrays will probably
require a stack size larger than the default.
Each function call requires two stack bytes (for the return address)
plus the number of argument bytes; if the function that is called creates local
variables, then they also use up stack space.
In addition, C expressions create intermediate values that are stored on
the stack.
It is up
to the programmer to determine if a particular process requires a stack size
larger than the default. A process may
also be created with a stack size smaller than the default, in order to save
stack memory space, if it is known that the process will not require the full
default amount.
When a process is created, it is assigned a unique process identification number or pid. This number can be used to kill a process.
A short example that uses
all of the concepts introduced.
#define FALSE 0
#define TRUE !FALSE
/*
Use #define to define
constants.
#define's are
implemented as a simple text substitution.
These need two go at the
top of the file
/*
/*
Define two integers
globally.
i is a loop counter.
done is a flag to tell
main program when to end.
*/
int i;
int done;
/*
This function will be
used as a separate process.
It plays 3 notes,
defined in the array c[];
*/
void PlayNotes() {
float c[]={250.0, 250.0,
100.0};
for(i=0;i<3;i++) { /*loop trough notes*/
tone(c[i],0.5); /* Play note*/
sleep(0.1); /* delay*/
}
done=TRUE; /*Signal main process that we are
done.*/
}
void main() {
done=FALSE; /*We are NOT done (set it
FALSE)*/
start_process(PlayNotes()); /*Start the note-playing process*/
while (!done) { /*Loop while done is false*/
printf("\nPlaying
note #%d.",i); /*Print info about
note*/
sleep(0.1);
}
printf("\nDone!");
}
In this example, the main process keeps running while the
music is playing. In the previous
example, the main process was not running while the music was playing.