Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 4.04 on page 79

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.



Solution by Bob Wightman

Mr Wightman's solution has a bug. It can be seen by using the command '?'. After entering '?' the result (printing the top item without popping) is delayed until the ENTER key is pressed, which causes a newline ('\n'), which, in turn, causes the top item to be popped and printed. This defeats the intent of the '?' command. The other stack-manipulation commands have the same, or similar problems.
This situation is encountered in most operating systems. This is so that line editing can be done prior to submitting the line.
--- Pilcrow 03:11, 22 September 2011 (UTC)

#include<stdlib.h>
#include<stdio.h>
#include<ctype.h>
#include<math.h>

#define MAXOP 100
#define NUMBER  0
#define TRUE 1
#define FALSE 0

/* This programme is a basic calculator.

Extra cases have been added to:
1. Show the top item of the stack without permanently popping it.
2. Swap the top two items on the stack.
3. Duplicate the top item on the stack.
4. Clear the stack.

I have used functions for each of the new cases rather than have the
code inline in order to limit the physical size of the switch block.

In anticipation of the following exercise the following characters have
been used for the operations (in the same order as above): ? ~ # !
rather than use alphabetic characters.

It is actually rather difficult to be original in this exercise.

This is exercise 4-4 from Kernighan & Ritchie, page 79.

 */

int Getop(char s[]);
void push(double val);
double pop(void);
void showTop(void);
void duplicate(void);
void swapItems(void);
void clearStack();

int main(void)
{
    int type;
    double op2;
    char s[MAXOP];
    int flag = TRUE;

    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)
                    push(pop() / op2);
                else
                    printf("\nError: division by zero!");
                break;
            case '%':
                op2 = pop();
                if(op2)
                    push(fmod(pop(), op2));
                else
                    printf("\nError: division by zero!");
                break;
            case '?':
                showTop();
                break;
            case '#':
                duplicate();
                break;
            case '~':
                swapItems();
                break;
            case '!':
                clearStack();
            case '\n':
                printf("\n\t%.8g\n", pop());
                break;
            default:
                printf("\nError: unknown command %s.\n", s);
                break;
        }
    }
    return EXIT_SUCCESS;
}

#define MAXVAL 100

int sp = 0;          /* Next free stack position. */
double val[MAXVAL];  /* value stack. */

/* push: push f onto stack. */
void push(double f)
{
    if(sp < MAXVAL)
        val[sp++] = f;
    else
         printf("\nError: 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("\nError: stack empty\n");
        return 0.0;
    }
}

void showTop(void)
{
    if(sp > 0)
        printf("Top of stack contains: %8g\n", val[sp-1]);
    else
        printf("The stack is empty!\n");
}


void duplicate(void)
{
    double temp = pop();

    push(temp);
    push(temp);
}

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;
}

int getch(void);
void unGetch(int);

/* Getop: get next operator or numeric operand. */
int Getop(char s[])
{
    int i = 0;
    int c;
    int next;

    /* Skip whitespace */
    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();
 
    while(isdigit(s[++i] = c))
            c = getch();
    if(c == '.')                        /* Collect fraction part. */
        while(isdigit(s[++i] = c = getch()))
                        ;
    s[i] = '\0';
    if(c != EOF)
        unGetch(c);
    return NUMBER;
}

#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;

/* Getch: get a ( possibly pushed back) character. */
int getch(void)
{
     return (bufp > 0) ? buf[--bufp]: getchar();
}

/* unGetch: push character back on input. */
void unGetch(int c)
{
    if(bufp >= BUFSIZE)
        printf("\nUnGetch: too many characters\n");
    else
        buf[bufp++] = c;
}

Solution by Pilcrow

In anticipation of the requirements of exercises 4-5 & 4-6 I have followed Mr Wightman's example by assigning the characters @ ? # ~ ! to various stack operations, rather than using letters.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>


#define MAXOP 100
#define NUMBER '0'

int getop(char []);
void push(double);
double pop(void);
void stakop(int);

int nopprint = 0;                       /* don't pop & print on ENTER after stack operations */
double op2, op3;

int main(void)
{
    int type;
    char s[MAXOP];

    while((type = getop(s)) != EOF) {
        switch (type) {
            case NUMBER:
                push (atof(s));
                break;
            case '@': case '?': case '#': case '~': case '!':       /* stack operations */
                stakop(type);
                break;
            case '_':                       /* unary minus (underscore) negates previous number */
                push( 0.0 - pop());
                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 '%':                       /* floating modulo operator, not integer */
                op2 = pop();
                if(op2 != 0.0) push(fmod(pop(),op2));
                else printf("error: zero divisor\n");
                break;
            case '\n':
                if(!nopprint)
                    printf("\t%.8g\n",pop());
                nopprint = 0;
                break;
            default:
                printf("error: unknown command %s\n", s);
                break;
        }
    }
    return 1;
}

#define MAXVAL 100

int sp = 0;
double val[MAXVAL];


void push(double f)
{
    if (sp < MAXVAL)
        val[sp++] = f;
    else
        printf("error: stack full, can't push %g\n", f);
    return;
}

double pop(void)
{
    if (sp > 0) return val[--sp];
    else {
        printf("stack empty\n");
        return 0.0;
    }
}

#include <ctype.h>

int getch(void);
void ungetch(int);

int getop(char s[])
{
    int i, c, type;

    while((s[0] = c = getch()) == ' ' || c == '\t') ;       /* skip leading spaces or tabs */
    s[1] = '\0';
    if (!isdigit(c) && c != '.') return c;                  /* was operator */
    i = 0;                                                  /* was digit    */

    if(isdigit(c)) {
        type = NUMBER;
        while(isdigit(s[++i] = c = getch())) ;              /* get digits   */
    }
    if(c == '.') {
        type = NUMBER;
        while(isdigit(s[++i] = c = getch())) ;              /* and dec pt   */
    }

    s[i] = '\0';
    if( c != EOF)
        ungetch(c);
    return type;
}

#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;

int getch(void)
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
    if (bufp >= BUFSIZE) printf("ungetch: too many characters\n");
    else buf[bufp++] = c;
}

void stakop(int op)
{
    int i;
    nopprint = 1;                       /* disable pop & print on '\n' */

    switch(op) {
        case '@':                       /* show whole stack (not required by K&R)*/
            if(sp) {
                putchar('\n');
                for(i = sp - 1 ; i >= 0; i--)   /* last in, first shown */
                    printf("*** %.8g\n",val[i]);
            }
            else printf("stack empty\n");
            return;
        case '?':                       /* print top item */
            if(sp) {
                putchar('\n');
                push(op2=pop());
                printf("*** %.8g\n",op2);
            }
            else printf("stack empty\n");
            return;
        case '#':                       /* dup top item */
            push(op2=pop());
            push(op2);
            return;
        case '~':                       /* swap top 2 items */
            op2 = pop();
            op3 = pop();
            push(op2);
            push(op3);
            return;
        case '!':                       /* clear stack */
            nopprint = 0;
            sp = 0;
            return;
        default:                        /* should only get here if there's a bug */
            printf("error: unknown command %s\n",op);
            return;
    }
}

Solution by menonsahab

/* There is one bug in the previous solutions. In the function used to 
duplicate the top element, people are not checking whether there is sufficient 
space for another element to be inserted in the stack. I've made the necessary changes.*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

#define MAXOP 100
#define NUMBER 0
#define MAXVAL 100
#define BUFSIZE 100

int getop(char *);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);
void viewstack(void);
void showTop(void);
void swap(void);
void duplicate(void);
void clearStack(void);

int bufp = 0;
char buf[BUFSIZE];
int sp = 0;
double val[MAXVAL];


void push(double f)
{
	if(sp < MAXVAL)
		val[sp++] = f;
	else
		printf("error: stack full, can't push %g\n", f);
}

double pop(void)
{
	if(sp > 0)
		return val[--sp];
	else
	{
		printf("error: stack empty\n");
		exit(1);
		//return 0.0;
	}
}

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("ungetch: too many characters\n");
	else
		buf[bufp++] = c;
}

int getop(char *s)
{
	int i, c, d;
	while((s[0] = c = getch()) == ' ' || c == '\t');
	s[1] = '\0';
	if(!isdigit(c) && c != '.' && c != '-')
		return c;
	if(c == '-')
	{
		d = getch();
		if(d == ' ')
			return c;
		else
			ungetch(d);
	}
	i = 0;
	if(isdigit(c) || c == '-')
		while(isdigit(s[++i] = c = getch()));
	if(c == '.')
		while(isdigit(s[++i] = c = getch()));
	s[i] = '\0';
	if(c != EOF)
		ungetch(c);
	return NUMBER;
}

void viewstack(void)
{
	int i;
	printf("\nstack:\n");
	for(i = sp - 1; i >= 0; i--)
		printf("%lf\n", val[i]);
}

void showTop(void)
{
	sp > 0 ? printf("\t%.8g\n", val[sp-1]) : printf("stack is empty\n");
}

void swap(void)
{
	double temp;
	if(sp < 1)
		printf("error: stack has less than 2 elements, can't swap\n");
	else
	{
		temp = val[sp - 1];
		val[sp - 1] = val[sp - 2];
		val[sp - 2] = temp; 
	}
}

void duplicate(void)
{
	if(sp > MAXVAL - 1)
		printf("error: stack is full, can't duplicate\n");
	else
	{
		double temp = pop();
		push(temp);
		push(temp);
		++sp;
	}
}

void clearStack(void)
{
	sp = 0;
}

int main()
{
	int type;
	double op2;
	char s[MAXOP];

	while((type = getop(s)) != EOF)
	{
		//viewstack();  Use this function if you wish to see the stack after every iteration
		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 != 0.0)
					push(fmod(pop(), op2));
				else
					printf("error: division by zero\n");
				break;
			case '?': // show top item on stack without popping
				showTop();
				break;
			case '~': // swap top two elements of the stack
				swap();
				break;
			case '#': // duplicate the top element
				duplicate();
				break;
			case '!': // clearStack
				clearStack();
				break;
			case '\n':
				printf("\t%.8g\n", pop());
				break;
			default:
				printf("error: unknown command %s\n", s);
				break;
		}
	}
	return 0;
}

Solution by dasaphro

menonsahab's code still has a few problems. The special commands, when followed by newline, do not produce the desired output; the viewstack function is not used; and duplicate needlessly increments sp (this is already done by push). The following do the trick.

/* There is one bug in the previous solutions. In the function used to 
   duplicate the top element, people are not checking whether there is sufficient 
   space for another element to be inserted in the stack. I've made the necessary changes.*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

#define MAXOP 100
#define NUMBER 0
#define MAXVAL 100
#define BUFSIZE 100

int getop(char *);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);
void view_stack(void);
void show_top(void);
void swap(void);
void duplicate(void);
void clear_stack(void);

int bufp = 0;
char buf[BUFSIZE];
int sp = 0;
double val[MAXVAL];


int main()
{
    int type, ignore_newline = 0;
    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 != 0.0)
		push(fmod(pop(), op2));
	    else
		printf("error: modulo zero\n");
	    break;
	case '?': // show top item on stack without popping
	    show_top();
	    ignore_newline = 1;
	    break;
	case '~': // swap top two elements of the stack
	    swap();
	    ignore_newline = 1;
	    break;
	case '#': // duplicate the top element
	    duplicate();
	    ignore_newline = 1;
	    break;
	case '!': // clear_stack
	    clear_stack();
	    ignore_newline = 1;
	    break;
	case '$':
	    view_stack();
	    ignore_newline = 1;
	    break;
	case '\n':
	    if (ignore_newline) {
		ignore_newline = 0;
	    }
	    else {
		printf("\t%.8g\n", pop());
	    }
	    break;
	default:
	    printf("error: unknown command %s\n", s);
	    break;
	}
    }
    return 0;
}

void push(double f)
{
    if (sp < MAXVAL) {
	val[sp++] = f;
    }
    else {
	printf("error: stack full, can't push %g\n", f);
    }
}

double pop(void)
{
    if (sp > 0) {
	return val[--sp];
    }
    else {
	printf("error: stack is empty\n");
	return 0.0;
    }
}

int getch(void)
{
    if (bufp > 0) {
	return buf[--bufp];
    }
    else {
	return getchar();
    }
}

void ungetch(int c)
{
    if (bufp >= BUFSIZE) {
	printf("ungetch: too many characters\n");
    }
    else {
	buf[bufp++] = c;
    }
}

int getop(char *s)
{
    int i, c, d;
    while ((s[0] = c = getch()) == ' ' || c == '\t') {
	;
    }
    s[1] = '\0';
    if (!isdigit(c) && c != '.' && c != '-') {
	return c;
    }
    if (c == '-') {
	d = getch();
	if(d == ' ') {
	    return c;
	}
	else {
	    ungetch(d);
	}
    }
    i = 0;
    if(isdigit(c) || c == '-') {
	while(isdigit(s[++i] = c = getch())) {
	    ;
	}
    }
    if(c == '.') {
	while(isdigit(s[++i] = c = getch()));
    }
    s[i] = '\0';
    if(c != EOF) {
	ungetch(c);
    }
    return NUMBER;
}

void view_stack(void)
{
    int i;
    printf("\nstack:\n");
    for(i = sp - 1; i >= 0; i--) {
	printf("%lf\n", val[i]);
    }
}

void show_top(void)
{
    if (sp > 0) {
	printf("\t%.8g\n", val[sp-1]);
    }
    else {
	printf("error: stack is empty\n");
    }
	    
}

void swap(void)
{
    double temp;
    if(sp < 1)
	printf("error: stack has less than 2 elements, can't swap\n");
    else
    {
	temp = val[sp - 1];
	val[sp - 1] = val[sp - 2];
	val[sp - 2] = temp; 
    }
}

void duplicate(void)
{
    if(sp > MAXVAL - 1)
	printf("error: stack is full, can't duplicate\n");
    else
    {
	double temp = pop();
	push(temp);
	push(temp);
    }
}

void clear_stack(void)
{
    sp = 0;
}

Solution by anonymous

dasaphro's code has a small mistake of checking if sp < 1 before performing a swap. sp is similar to a 1 based index since it points to the next free stack position. If sp == 1, the next free stack position is s[1]. This is confirmed by looking at the push function, where the sp value is used before incrementing.

#include <stdio.h>
#include <stdlib.h> // for atof()
#include <ctype.h>
#include <math.h>

/*
    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.
*/

#define MAXOP 100   // max size of operand or operator
#define NUMBER '0'  // signal that a number was found
#define MAXVAL 100  // maximum depth of val stack
#define BUFSIZE 100 

enum boolean {FALSE, TRUE};

int sp = 0;         // next free stack position
double val[MAXVAL]; // value stack
char buf[BUFSIZE];  // buffer for ungetch
int bufp = 0;       // next free position in buf

int getop(char s[]);
void push(double f);
double pop(void);
int getch(void);
void ungetch(int c);
void printTop(void);
void duplicateTop(void);
void swapTopTwo(void);
void clearStack(void);

// reverse Polish calculator
// note: convert ((((-1 - 2) * (4 + -5)) / -3) % 5) * (-1 - -10) to -1 2 - 4 -5 + * -3 / 5 % -1 -10 - * for reverse Polish notation. -1 2 - 4 -5 + * -3 / 5 % -1 -10 - * == -9
int main()
{
    int type;
    double op2;
    char s[MAXOP];
    char skipNextNewline = FALSE;

    while ((type = getop(s)) != EOF)
    {
        switch (type)
        {
        case NUMBER:
            push(atof(s)); // convert the string to type double and push it on the stack
            break;
        case '+':
            push(pop() + pop()); // pop last two digits to sum them and push the result on the stack
            break;
        case '*':
            push(pop() * pop()); // pop last two digits to multiply them and push the result on the stack
            break;
        case '-':
            /*
                Because + and * are commutative operators, the order in which the popped operands are combined is irrelevant, but for - and / the left and right operands
                must be distinguished. In push(pop() - pop());, the order in which the two calls of pop are evaluated is not defined. To guarantee the right order, it is
                necessary to pop the first value into a temporary variable. Hence op2 = pop() in - and / but not in + and *
            */
            op2 = pop();
            push(pop() - op2); // pop last two digits to subtract them in the correct order and push the result on the stack
            break;
        case '/':
            op2 = pop();
            if (op2 != 0.0)
                push(pop() / op2); // pop last two digits to divide them in the correct order and push the result on the stack
            else
                printf("error: zero divisor\n");
            break;
        case '%':
            op2 = pop();
            if (op2 != 0.0)
                push(fmod(pop(), op2)); // pop last two digits in the correct order to find the modulus and push the result on the stack
            else
                printf("error: zero divisor\n");
            break;
        case '?':
            printTop();
            skipNextNewline = TRUE;
            break;
        case '#':
            duplicateTop();
            skipNextNewline = TRUE;
            break;
        case '~':
            swapTopTwo();
            skipNextNewline = TRUE;
            break;
        case '!':
            clearStack();
            skipNextNewline = TRUE;
            break;
        case '\n':
            if (skipNextNewline)
                skipNextNewline = FALSE;
            else
                printf("\t%.8g\n", pop()); // get the final result
            break;
        default:
            printf("error: unknown command %s\n", s);
            break;
        }
    }
    return 0;
}

// push: push f onto value stack
void push(double f)
{
    if (sp < MAXVAL) // if value stack still has space, add f
        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) // if the next free stack position is greater than zero, return the highest level item from stack
        return val[--sp];
    else
    {
        printf("error: stack empty\n");
        return 0.0;
    }
}

// getop: get next operator or numeric operand
int getop(char s[])
{
    int i, c;

    while ((s[0] = c = getch()) == ' ' || c == '\t') // skip white space
        ;
    s[1] = '\0'; // terminate string in case input is not a number (s is expected to be a string throughout program)
    if (!isdigit(c) && c != '.' && c != '-')
        return c; // not a number. Probably an operator, so return it. Minus operator is a special case and is handled right before return NUMBER;
    i = 0;
    if (c == '-' || isdigit(c)) // collect integer(s), if any, after first digit found or after minus symbol found
        while (isdigit(s[++i] = c = getch()))
            ;
    if (c == '.') // collect fraction part if period is found
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0'; // terminate string after digits were captured
    if (c != EOF) 
        ungetch(c); // since we read to far, push the last read char back on the getch buffer. This buffer is read first before getting the next char from input
    if (i == 1 && s[0] == '-') // if s[0] == '-' && s[1] == '\0', return minus operator
        return '-';
    return NUMBER;
}

// get a (possibly pushed back) character
// checks to see if there are any chars in buffer. If there are, get those and return it. If not, call getchar() from stdio.h to get next char from input
int getch(void)
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

// push character back on input
// if bufp is less than BUFSIZE, there is room to store more chars to be read by getch next and it stores c and updates the index for it
void ungetch(int c)
{
    if (bufp >= BUFSIZE)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++] = c;
}

// prints the top element in the value stack
void printTop(void)
{
    if (sp > 0)
        printf("\t%.8g\n", val[sp - 1]);
    else
        printf("error: stack empty\n");
}

void duplicateTop(void)
{
    if (sp < MAXVAL) // only need to see if there is space for one more
        push(val[sp - 1]); // duplicates top item
    else
        printf("error: stack full, can't duplicate top element\n");
}

void swapTopTwo(void)
{   // if sp == 2, there are at least two elements stored
    if (sp > 1)
    {                               // <third> <second> <first>
        double first = pop();       // <third> <second>
        double second = pop();      // <third>
        push(first);                // <third> <first>
        push(second);               // <third> <first> <second>
    }
    else
        printf("error: can't swap top two, not enough elements\n");
}

// sets next free stack position to zero (meaning the value stack is empty).
// all of the original values are still there, but they will no longer be accessible by the current functions and they will be overwritten when new elements are stored
void clearStack(void)
{
    sp = 0; 
}
Personal tools