chapter 3 functions -...
TRANSCRIPT
Chapter 3
Functions
Functions, known as methods in Java, follow-
ing the divide-and-conquer approach, break large
program into smaller pieces, and also allow
us to reuse whatever other people have done,
such as all the input functions as we have seen
in <stdio.h> and <string.h> etc..
As we know in Java, a function consists of
four pieces: a name, a return type, a list of
parameters, and a body, which converts the
parameters into a value to be returned.
Again, the best way to learn function is to write
one up.
1
An example
We want to design and then write a program
to print each and every line of its input that
contains a particular “pattern”.
Thus, given the pattern “ould” and the follow-
ing text file test.txt:
Ah love! could you and I with Fate conspireTo grasp this sorry scheme of Things entire,Would not we shatter it to bite – and thenRe-mould it nearer to the Heart’s Desire!
the program will return the following:
Ah love! could you and I with Fate conspireWould not we shatter it to bite – and thenRe-mould it nearer to the Heart’s Desire!
This is essentially what the Unix utility grep
does.
/users/faculty/zshen > grep ould test.txt
2
Design of the program
The design part is pretty straightforward:
while (there is yet another line)
if(the line contains the pattern)
print it out
Let’s see how to write a function for each of
the smaller pieces. The first piece of “there is
yet another line” is just the getline() function.
For the second piece, we will write a function
strindex(s, t) that returns the position in the
string s where the pattern t starts; or -1 if t
does not occur in s.
The third piece is certainly just printf().
3
The strindex() function
int strindex(char s[], char t[]){
int i, j, k;
for(i=0; s[i]!=’\0’; i++){
for(j=i,k=0; t[k]!=’\0’&&s[j]==t[k];j++,k++);
if(k>0)&&t[k]==’\0’)
return i;
}
return -1;
}
In the above function, for each position i, we
will use j and k to test if a segment of s, start-
ing from i is identical to the pattern, t. If it
is the case, prints out i.
Otherwise, k>0 but we haven’t got to the end
of the pattern, it sends back -1.
4
The whole thing
/home/PLYMOUTH/zshen > more match.c
#include <stdio.h>
#define MAXLINE 1000
int getline1(char line[], int max);
int strindex(char source[], char searchfor[]);
char pattern[]="ould";
main(){
char line[MAXLINE];
int found=0;
while(getline1(line, MAXLINE)>0)
if(strindex(line, pattern)>=0){
printf("%s", line);
found++;
}
return found;
}
5
int getline1(char s[], int lim){
int c, i;
for(i=0;i<lim-1 && (c=getchar())!=EOF
&& c!=’\n’; ++i)
s[i]=c;
if(c==’\n’){
s[i]=c;
++i;
}
s[i]=’\0’;
return i;
}
int strindex(char s[], char t[]){
int i, j, k;
for(i=0; s[i]!=’\0’; i++){
for(j=i,k=0; t[k]!=’\0’ && s[j]==t[k]; j++,k++);
if(k>0 && t[k]==’\0’)
return i;
}
return -1;
}
6
Test it out
/home/PLYMOUTH/zshen > cc match.c
/home/PLYMOUTH/zshen > more test.txt
Ah love! could you and I with Fate conspire
To grasp this sorry scheme of Things entire,
Would not we shatter it to bite -- and then
Re-mould it nearer to the Heart’s Desire!
/home/PLYMOUTH/zshen > ./a.out < test.txt
Ah love! could you and I with Fate conspire
Would not we shatter it to bite -- and then
Re-mould it nearer to the Heart’s Desire!
/home/PLYMOUTH/zshen >
7
A bit more
In general, a function looks like the following:
return-type function-name(argument declarations){declarations and statements}
A simplest function is void dummy(){}, which
takes nothing, does nothing and sends back
nothing.
Notice that when the return type is not speci-
fied, int is assumed. We will see later, that it
is not the case with C++.
Except the case when void is used as the return
type, a function has to have a return statement
as its logical end, which might return a value
of the return type.
8
What is a program then?
A C program is just a collection of definitions
and functions. When a program runs, the func-
tion main() is the first to be called.
Communication between the functions is car-
ried out with arguments as inputs and values
returned by the functions as outputs.
Homework: Self-study §4.2 on functions that
return something that is not an integer.
9
Labworks
1. Write a program that will test out a function
strRindex(s, t) that returns the index of the
rightmost occurrence of a string t in another
string s; or -1 if t does not occur in s.
2. Expand atof as discussed in Sec. 4.2 so
that it can deal with scientific notation such as
123.45e-6, i.e., a floating-point number that
could be followed by e or E and an optional
signed exponent. For example, with the ‘g’
conversion, I got the following:
/home/zshen > ./a.out
123.45
123.45
123.45E-6
0.00012345
-123.45e6
-1.2345e+08
10
Another example
We might have learned something about the
polish, or postfix, representation of the arith-
metic expression. The idea is that instead of
placing the operator in between two operands,
we put it after the operands. For example,
instead of
(1-2)*(4+5)
we can say
1 2 - 4 5 + *
One of the major benefits of using the postfix
format is that we no longer need parentheses
to make the expression unambiguous.
We will develop a program that can evaluate
such a postfix expression.
11
An interesting data structure
The idea is pretty easy: get out the next two
operands, apply the following operator then
put the result back to... a stack, which is used
to hold on this expression.
I also saw a coin storage device like this in a
SUBWAY restaurant.
Essentially, with a stack, the last piece that we
put in will be the first to be taken out, thus
sometimes called a LIFO list.
12
The program
The top layer of the algorithm might look like
the following, assuming we read lines of postfix
expressions from a file:
while(not EOF yet)
if (number)
push it into the stack
else if (operator)
pop two operands out the stack
apply the operator
push result back into the stack
else if(newline)
pop and print the top
else error
13
The structure
When we throw in everything, the program will
become big thus tough to read and analyze.
As a result, we definitely should cut the codes
into functions, and even collect those functions
into separate files.
We have already seen how to compile those
files individually into .o files, using the -c op-
tion, then link them together, using
>cc *.o
or
>cc file1.o file2.o file3.o
to get a.out, or even
>cc file1.c file2.c file3.c -o myExecutable
14
What do we want?
Besides the main function, we also have the
stack related functions, and a function, getop(),
that will get us the next token from the input
stream, i.e., the tokenizer.
The important thing is that the main() func-
tion has no need to know the structure of the
stack. Thus, we just collect all the stack re-
lated functions in a separate file, e.g., stack.c.
This is a way of doing modular programming:
we only want to know the interface, but not
the details.
We might also throw gettop() and its company
into a separate file, and hook up the variables
with you-know-what.
Let’s check them out.
15
The main() stuff/home/PLYMOUTH/zshen > more main.c#include <stdio.h>#include <stdlib.h> /* for atof() */#include "myFile.h" /* for the utility functions */
#define MAXOP 100#define NUMBER ’0’
main(){int type;double op2;char s[MAXOP];
while((type=getop(s))!=EOF){switch(type){case NUMBER:push(atof(s)); //Convert then pushbreak;
case ’+’:push(pop() + pop());break;
case ’*’:push(pop()*pop());break;
16
//Neither ’-’ nor ’/’ is commutative.case ’-’:op2=pop();push(pop()-op2);break;
case ’/’:op2=pop();if(op2!=0.0)
push(pop()/op2);else printf("error: zero decision\n");break;
case ’\n’:printf("\t%.6g\n", pop());break;
default:printf("error: unknown command %s\n", s);break;
}}return 0;}
/home/PLYMOUTH/zshen > more myFile.h
int getop(char []);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);
17
The stack package/home/PLYMOUTH/zshen > more stack.c#include <stdio.h>#define MAXVAL 100
//Will talk about static later...static int sp=0;static double val[MAXVAL];
void push(double f){if(sp<MAXVAL){val[sp++]=f;printf("I have pushed in %f\n", f);
}else printf("error: stack full, can’t push %g\n", f);}
double pop(void){if(sp>0){double temp=val[--sp];printf("I am popping off %f\n", temp);return temp;
}else {printf("error: stack empty\n");return 0.0;
}}
18
Get the next token/home/PLYMOUTH/zshen > more gettop.c#include <stdio.h>#include <ctype.h>#define NUMBER ’0’
int getop(char s[]){int i, c;//Skip all the leading spaceswhile((s[0]=c=getch())==’ ’||c==’\t’);
s[1]=’\0’;if(!isdigit(c)&&c!=’.’)return c; //Must be an operator
//Ready to read in a numberi=0;if(isdigit(c))//i points to the last read diditwhile(isdigit(s[++i]=c=getch()));
if(c==’.’) //bypass the decimal and read in digitswhile(isdigit(s[++i]=c=getch()));
//Stop when c is not a digits[i]=’\0’;//s[i] contains the first non-digitif(c!=EOF)ungetch(c);//Keep c somewhere?
printf("I am getting this much %s\n", s);return NUMBER;}
The function isdigit() tells if its input is a
digit, and is defined in ctype.h.
19
Keep c somewhere!
When reading stuff from an input stream, you
will not realize that you have read too much
until you have already done so. For example,
when reading in a number, you will not know
that it has ended until you have read in the
first non-digit symbol.
This example shows us how to deal with it by
using a separate buffer. When finding out we
have read too much, we put it into the buffer.
Next time, when we need something, we will
get it from the buffer first, if there is something
there, before getting the next from the input
stream.
Notice this buffer should organize as a queue,
i.e., whatever thrown in first should be the first
to be taken out.
20
Get the next token/home/PLYMOUTH/zshen > more getch.c#include <stdio.h>#define BUFSIZE 100
char buf[BUFSIZE]; /* buffer for ungetch */int bufp=0; /* next free position in bf */
int getch(void){ // get a character, which might be// pushed back
return (bufp>0) ? buf[--bufp]: getchar();}
void ungetch(int c){ /*push the character back on input */if(bufp>=BUFSIZE)printf("ungetch: too many characters\n");
else buf[bufp++]=c;}
The buf now acts like a stack. It works in this
case since we only keep one piece at a time..
In general, this will not work. (?)
21
Let’s check it out/home/PLYMOUTH/zshen > cc *.c/home/PLYMOUTH/zshen > more polishTest3.txt12.5 2 - 4 5 + */home/PLYMOUTH/zshen > ./a.out < polishTest3.txtI am getting this much 12.5I have pushed in 12.500000I am getting this much 2I have pushed in 2.000000I am popping off 2.000000I am popping off 12.500000I have pushed in 10.500000I am getting this much 4I have pushed in 4.000000I am getting this much 5I have pushed in 5.000000I am popping off 5.000000I am popping off 4.000000I have pushed in 9.000000I am popping off 9.000000I am popping off 10.500000I have pushed in 94.500000I am popping off 94.500000
94.5/home/PLYMOUTH/zshen >
22
Header files
Because of what we just said, C provides a con-
venient way for us to organize all the functions
that will be used in a project, maybe consisting
of a bunch of .h files.
1. We separate all the definitions of those
functions according to their functionalities.
2. We collect the declaration, or signature, of
all these functions into a separate header file,
e.g., the myFile.h, and let it be included in the
relevant files, so they know about each other
in a unique way.
This is what we have done when we include
"myFile.h" at the beginning of the main.c file
in the above program.
23
Labwork
0. Self study §4.5.
1. Extend the postfix evaluation program so
that we can evaluate polish expression that
contains a mixture of positive and negative
numbers.
2. Add access to such popular library func-
tions: sin (s), cos (c), square root (r), and
pow (p), using the <math.h> library.
To simplify the input, just use the first letter
of these functions, e.g.,
-2 3 p 3.6 2 / + 3 s -
To compile all the files while making use of the
math library, do the following:
> cc getch.c gettop.c stack.c main.c -lm
24
A little test/home/zshen > more test4.txt-2 3 p 3.6 2 / + 3 s -/home/zshen > ./a.out < test4.txtI have pushed in -2.000000I have pushed in 3.000000I am popping off 3.000000I am popping off -2.000000I have pushed in -8.000000I have pushed in 3.600000I have pushed in 2.000000I am popping off 2.000000I am popping off 3.600000I have pushed in 1.800000I am popping off 1.800000I am popping off -8.000000I have pushed in -6.200000I have pushed in 3.000000I am popping off 3.000000I have pushed in 0.141120I am popping off 0.141120I am popping off -6.200000I have pushed in -6.341120I am popping off -6.341120
-6.34112
Notice that sin 3 = 0.141120, when 3 refers to
3 radians, about 171.89◦.
25
Register variables
An automatic variable is kept in a system stack,
usually located in the main memory.
If you will heavily use a variable, you can de-
clare it to be register, so its space is within
the processor, thus much faster to get access
to. For example,
register int x;
register char c;
Of course, you can’t declare too many of them,
and there is also a restriction on the types of
such variables. You also can’t take the address
of such a variable(?), as we will discuss in the
chapter.
Homework: Check out §4.8 and §4.9 please.
26
An example
Question: What does the following piece do?
int global-positives=0;
typedef struct list {
struct list* next;
double val;
} * list;
void count_positives(list l){
list* p;
for(p=l; p; p=p->next)
if(p->val>0.0)
++global_positives;
}
Answer: It counts the number of positive stuff
in the list l.
27
An optimal version
If you use some “optimizing” compiler, includ-
ing gcc, to compile the above segment, it will
turn the above function into the following:
void count_positives(list l){
list p;
register int r;
r=global_positives;
for(p=l; p; p=p->next)
if(p->val>0.0)
++r;
global_positives=r;
}
Instead of repeatedly accessing global_positives
from the memory, we do it just twice, thus
saving time when l is really long and cnotains
many positive numbers.
28
Recursion
C allows a function to be called by itself, of
course, with smaller and/or simpler arguments.
We just give a simple example, which prints out
a number as a character sequence, e.g., it will
print out 234 as ”234”.
#include <stdio.h>
void printd(int n){
if (n<0) {
putchar(’-’);
n=-n;
}
if(n/10)
/* if there is more than one digit
print out the previous digits first. */
printd(n/10);
/* then print out this digit */
putchar(n%10+’0’);
}
29
The whole nine yards/home/PLYMOUTH/zshen > more printNumber.c#include <stdio.h>
void printd(int);
main(void){int num;
printf("Please enter a number: ");scanf("%d", &num);printf("You just entered %d\n", num);printf("This is the same as the following:\n");printd(num);printf("\n");}
void printd(int n){if (n<0) {putchar(’-’);n=-n;
}if(n/10)/* if there is more than one digit
print out the previous digits first. */printd(n/10);
/* then print out this digit */putchar(n%10+’0’);}
30
Check it out...
/home/PLYMOUTH/zshen > cc printNumber.c
/home/PLYMOUTH/zshen > ./a.out
Please enter a number: 234
You just entered 234
This is the same as the following:
234
/home/PLYMOUTH/zshen >
31
The C preprocessor
We have already seen that the preprocessor will
check out all the #include and #define lines. In
fact, the #define places a more general substi-
tution function.
#define name replacementText
Two examples:
#define forever for(;;) /* infinite loop */
#define max(A, B) ((A)>(B) ? (A):(B))
Thus, the following line
x=max(p+q, r+s);
will be replaced with the following:
x=((p+q)>(r+s) ? (p+q):(r+s));
Question: What will max(i++, j++) do? Write
a simple driver to test out your belief.
32
Conditional inclusion
It is also possible to provide conditional inclu-
sion to the preprocessor. For example, to make
sure that a file hdr.h is included only once, we
can make the file look like the following:
#if !define(HDR)
#define HDR
/* contents of the hdr.h file */
#endif
The first inclusion of this file defines the HDR
variable, and the following inclusion will simply
jump over to the line following endif. Thus,
no duplicated inclusion will happen.
Is it smart or what?
33
Another example
As we mentioned, there are quite a few dialects
of Unix, each of which should have its own
header file. The following segment does this.
#if SYSTEM==sysv
#define HDR "sysv.h"
#elif SYSTEM==BSD
#define HDR "bsd.h"
#elif SYSTEM==MSDOS
#define HDR "msdos.h"
#else #define HDR "default.h"
#endif
#include HDR
34