Chapter 4 – Functions and Program Structure
Exercise 4-1
Write the function strrindex(s,t) , which returns the position of the rightmost occurrence of t in s , or -1 if there is none.
#include <stdio.h>
/* strrindex: which returns the position of the rightmost
occurrence of t in s , or -1 if there is none. */
int strrindex(char s[], char t);
int main() {
char testchar = 'n';
char teststr[] = "goodmorning";
printf("test string = %s/n", teststr);
printf(" test char = %c/n", testchar);
printf("result:/n");
printf(" expected = 9/n");
printf(" real = %d/n",strrindex(teststr, testchar));
return 0;
}
int strrindex( char s[], char t ) {
int i, count;
for(i=0, count = -1; s[i] != '/0'; i++) {
if(s[i] == t)
count = i;
}
return count;
}
Exercise 4-2
Extend atof to handle scientific notation of the form 123.45e-6 where a floating-point number may be followed by e or E and an optionally signed exponent.
#include <ctype.h>
#include <math.h>
#include <stdio.h>
/* atof2: convert string s to double */
double atof2(char s[]);
char *strings[] = {
"1.0e43",
"999.999",
"123.456e-9",
"-1.2e-3",
"1.2e-3",
"-1.2E3",
"-1.2e03",
"cat",
"",
0
};
int main(void)
{
int i = 0;
for (; *strings[i]; i++)
printf("atof(%s) = %g/n", strings[i], atof2(strings[i]));
return 0;
}
double atof2(char s[]) {
double val, power, epart;
int i, sign, esign;
for (i = 0; isspace(s[i]); i++); /* skip space */
sign = (s[i] == '-' ? -1 : 1);
if (s[i] == '+' || s[i] == '-') /* skip the sign */
i++;
for (val = 0.0; isdigit(s[i]); i++)
val = 10.0 * val + (s[i] - '0');
if (s[i] == '.') /* skip the decimal part */
i++;
for (power = 1.0; isdigit(s[i]); i++) {
val = 10.0 * val + (s[i] - '0');
power *= 10.0;
}
if (s[i] == 'e' || s[i] == 'E') /* skip the e/E notation */
i++;
esign = (s[i] == '-' ? -1 : 1);
if (s[i] == '+' || s[i] == '-') /* skip the esign */
i++;
for (epart = 0.0; isdigit(s[i]); i++) {
epart = 10.0 * epart + (s[i] - '0');
}
return sign * val / power * pow(10, (esign * epart));
}
Exercise 4-3
Given the basic framework, it's straightforward to extend the calculator. Add the modulus ( % ) operator and provisions for negative numbers.
int main(void)
{
int type;
double op2;
char s[MAXOP];
while((type = getop(s)) != EOF)
{
switch(type)
{
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if(op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor/n");
break;
case '%':
op2 = pop();
if (op2) {
push(fmod(pop(), op2));
}
else
printf("error: mod by zero/n");
break;
case '/n':
printf("/t%.8g/n", pop());
break;
default:
printf("error: unknown command %s/n", s);
break;
}
}
return 0;
}
/* getop: get next operator or numeric operand */
int getop(char s[])
{
int i = 0, c, next;
while((s[0] = c = getch()) == ' ' || c == '/t')
;
s[1] = '/0';
/* not a number but may contain a unary minus.*/
if(!isdigit(c) && c != '.' && c != '-')
return c;
if (c == '-') {
next = getch();
if (!isdigit(next) && next != '.')
return c;
c = next;
}
else c = getch();
/* collect integer part */
while(isdigit(s[++i] = c))
c = getch();
if(c == '.') {
while(isdigit(s[++i] = c = getch()))
;
}
s[i] = '/0';
if(c != EOF)
ungetch(c);
return NUMBER;
}
Exercise 4-4
Add commands to print the top element of the stack without popping, to duplicate it, and to swap the top two elements. Add a command to clear the stack.
Exercise 4-5
Add access to library functions like sin , exp , and pow . See <math.h> in Appendix B, Section 4.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#define MAXOP 100 /* max size of operand or operator */
#define NUMBER '0' /* signal that a number was found */
#define IDENTIFIER 1 /* signal that identifier like sin, cos was found */
/* getop: get next operator or numeric operand */
int getop(char []);
/* push: push a element from the stack */
void push(double);
/* pop: pop a element from the stack */
double pop(void);
/* added by exe4_4 */
/* showtop: print top element without pop it */
void showtop(void);
/* swapitems: swap the top twp elements of the stack */
void swapitems(void);
/* duplicate: duplicate the top element of the stack */
void duplicate(void);
/* clearstack: clear the stack */
void clearstack(void);
/* added by exe4_5 */
/* accesslib: access to lib func like sin, cos etc. */
void accesslib(char []);
/* reverse Polish calculator */
int main(void)
{
int type;
double op2;
char s[MAXOP];
while((type = getop(s)) != EOF)
{
switch(type)
{
case NUMBER:
push(atof(s));
break;
case IDENTIFIER:
accesslib(s);
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if(op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor/n");
break;
case '%':
op2 = pop();
if (op2) {
push(fmod(pop(), op2));
}
else
printf("error: mod by zero/n");
break;
case '~':
clearstack();
break;
case '!':
duplicate();
break;
case '@':
swapitems();
break;
case '#':
showtop();
break;
case '/n':
printf("/t%.8g/n", pop());
break;
default:
printf("error: unknown command %s/n", s);
break;
}
}
return 0;
}
#define MAXVAL 100 /* maximum depth of val stack */
int sp = 0; /* next free stack position */
double val[MAXVAL]; /* value stack */
/* push: push f onto value stack */
void push(double f)
{
if(sp < MAXVAL)
val[sp++] = f;
else
printf("error: stack full, can't push %g/n", f);
}
/* pop: pop and return top value from stack */
double pop(void)
{
if(sp > 0)
return val[--sp];
else
{
printf("error: stack empty/n");
return 0.0;
}
}
/* print the top element without popping */
void showtop(void)
{
if(sp > 0)
printf("Top of stack contains: %8g/n", val[sp-1]);
else
printf("The stack is empty!/n");
}
/* duplicate the top element */
void duplicate(void)
{
double temp = pop();
push(temp);
push(temp);
}
/* swap the top two element */
void swapitems(void)
{
double item1 = pop();
double item2 = pop();
push(item1);
push(item2);
}
/* pop only returns a value if sp is greater than zero. So setting the
stack pointer to zero will cause pop to return its error */
void clearstack(void)
{
sp = 0;
}
void accesslib(char s[]) {
double op2;
if( 0 == strcmp(s, "sin"))
push(sin(pop()));
else if( 0 == strcmp(s, "cos"))
push(cos(pop()));
else if (0 == strcmp(s, "exp"))
push(exp(pop()));
else if(0 == strcmp(s, "pow"))
{
op2 = pop();
push(pow(pop(), op2));
}
else
printf("%s is not a supported function./n", s);
}
int getch(void);
void ungetch(int);
/* getop: get next operator or numeric operand */
int getop(char s[])
{
int i = 0, c, next;
while((s[0] = c = getch()) == ' ' || c == '/t')
;
s[1] = '/0';
if(isalpha(c))
{
i = 0;
while(isalpha(s[i++] = c ))
c = getch();
s[i - 1] = '/0';
if(c != EOF)
ungetch(c);
return IDENTIFIER;
}
/* not a number but may contain a unary minus.*/
if(!isdigit(c) && c != '.' && c != '-')
return c;
if (c == '-') {
next = getch();
if (!isdigit(next) && next != '.')
return c;
c = next;
}
else c = getch();
/* collect integer part */
while(isdigit(s[++i] = c))
c = getch();
if(c == '.') {
while(isdigit(s[++i] = c = getch()))
;
}
s[i] = '/0';
if(c != EOF)
ungetch(c);
return NUMBER;
}
#define BUFSIZE 100
char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buf */
int getch(void) /* get a (possibly pushed back) character */
{
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c) /* push character back on input */
{
if(bufp >= BUFSIZE)
printf("ungetch: too many characters/n");
else
buf[bufp++] = c;
}
Exercise 4-7
Write a routine ungets(s) that will push back an entire string onto the input. Should ungets know about buf and bufp , or should it just use ungetch ?
/* K&R Exercise 4-7 */
/* Solved by Steven Huang */
#include <string.h>
#include <stdio.h>
#define BUFSIZE 100
char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buf */
/* get a (possibly pushed back) character */
int getch(void);
/* push character back on input */
void ungetch(int c);
/* push s back on input */
void ungets(const char *s);
int main(void)
{
char *s = "hello, world. this is a test.";
int c;
ungets(s);
while ((c = getch()) != EOF)
putchar(c);
return 0;
}
/* get a (possibly pushed back) character */
int getch(void) {
return (bufp > 0) ? buf[--bufp] : getchar();
}
/* push character back on input */
void ungetch(int c) {
if(bufp >= BUFSIZE)
printf("ungetch: too many characters/n");
else
buf[bufp++] = c;
}
/*
ungets() actually takes a little bit of thought. Should the
first character in "s" be sent to ungetch() first, or should
it be sent last? I assumed that most code calling getch()
would be of this form:
char array[...];
int i;
while (...) {
array[i++] = getch();
}
In such cases, the same code might call ungets() as:
ungets(array);
and expect to repeat the while loop to get the same string back.
This requires that the last character be sent first to ungetch()
first, because getch() and ungetch() work with a stack.
To answer K&R2's additional question for this problem, it's
usually preferable for something like ungets() to just build
itself on top of ungetch(). This allows us to change ungetch()
and getch() in the future, perhaps to use a linked list instead,
without affecting ungets().
*/
void ungets(const char *s) {
size_t i = strlen(s);
while (i > 0)
ungetch(s[--i]);
}