chapter 3 functions -...

34
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

Upload: nguyenliem

Post on 11-Mar-2018

238 views

Category:

Documents


3 download

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