스택을 이용한 계산기 프로그램

여리왕자의 이미지

스택(배열)을 이용한 중위 연산식을 후위 연산식으로 변환한 후 그 값을 계산하는 프로그램입니다....

장장 5시간을 붙잡고 있다가.... 결국 질문을 올립니다.

일단 소스를 올리자면....

#include <stdio.h>
#include <stdlib.h>
 
#define MAX_STACK_SIZE 100 
#define MAX_EXPR_SIZE 100 
 
char expr[MAX_EXPR_SIZE] = "(((8/2)-3)+(4*2))";
 
typedef enum {rparen, lparen, plus, minus, times, divide, eos, operand} input_char ;
 
static input_char stack[MAX_STACK_SIZE];
 
int action[7][7] ={      /*?? ) ? (? +  -  *? /? eos */ 
   	          	 /*? ) */ {-1, 2, 1, 1, 1, 1, -1}, 
			 /*? ( */ {-1, 0, 0, 0, 0, 0, 0}, 
              /* input */ /* + */ {-1, 0, 1, 1, 1, 1, 0}, 
			 /* -? */ {-1, 0, 1, 1, 1, 1, 0}, 
		        /* *? */  {-1, 0, 0, 0, 1, 1, 0}, 
		        /* /? */  {-1, 0, 0, 0, 1, 1, 0}, 
			/* eos */ {-1,-1, 1, 1, 1, 1, 3} }; 
 
static	int top = -1; 
 
// - 후위 표기식 변환 함수 
void push( int *, input_char );
input_char pop( int *);
void postfix( void );
input_char get_token(char *, int *	);
int stack_top( int );
void print_token( input_char );
int eval( void );
input_char get_next_token( char *, int * );
 
int main( void ){
	int top = -1;
	printf("fostfix: %s\n\n", expr);
 
	postfix();
 
	printf("\n\nresult: %d", eval());
 
	system( "pause" );
	return 0;
}
 
void postfix(void)  { 
	int n = 0; 	/* 입력 문자열의 위치 */ 
	char symbol;
 	int done;
 
	input_char token;
 
	push(&top, eos); /* 스택의 초기 상태 */ 
	token = get_token(&symbol, &n); 
	done = 0; /* false */ 
 
	while (!done) { 
		if (token == operand) { 
			printf("%c ", symbol); 
			token = get_token(&symbol, &n); 
		} 
		else { 
			switch (action[token][stack_top(top)]) { 
				case -1: /* 오류 */ 
					break; 
				case 0:  push(&top, token);   // push 
					token = get_token(&symbol, &n); 
					break; 
				case 1:	/* pop, print, .... */ 
					print_token(pop(&top));
					break; 
				case 2: /* pop */ 
					pop(&top); /* delete lparen */ 
					token = get_token(&symbol, &n); 
					break; 
				case 3: /* done */ done = 1; /* true */ 
			} /* end of switch */ 
		} 
	} /* end of while */ 
} 
 
void push( int *in, input_char data )  { 
	if (*in >= MAX_STACK_SIZE - 1) {	 /* 스택 포화 상태 */ 
		fprintf(stderr, "Stack full"); 
		exit(1); 
	} 
 
	stack[++*in] = data; 
} 
 
input_char pop(int *out)  { 
	if (*out < 0) { 
		fprintf(stderr, "Stack Empty\n" ); 
		return eos;	// 스택에 존재하지 않는 코드를 반환 
	}
 
	//printf("[pop= %d]\n ", stack[*out]);
	return stack[(*out)--]; 
} 
 
input_char get_token(char *symbol, int *n)  { 
	*symbol = expr[(*n)++]; 
 
	switch (*symbol) { 
		case '(' : return lparen; 
		case ')' : return rparen; 
		case '+' : return plus; 
		case '-' : return minus; 
		case '/' : return divide; 
		case '*' : return times; 
		case '\0': return eos; 
		default  : return operand; 
	} 
}  
 
int stack_top( int top)
{
	if (top < 0 ) {
		fprintf(stderr, "Stack Empty");
		return -1;
	}
 
	return stack[top];
}
 
void print_token( input_char in )
{
	switch ( in ) { 
		case lparen : printf("%c ", '(' );  
			break;
		case rparen : printf("%c ", ')' ); 
			break;
		case plus   : printf("%c ", '+' ); 
			break;
		case minus  : printf("%c ", '-'); 
			break;
		case divide : printf("%c ", '/'); 
			break;
		case times  : printf("%c ", '*'); 
			break;
	} 
}
 
int eval( void )
{
	input_char token;
	char symbol;
	int op1, op2;
	int n=0;
 
	token = get_next_token( &symbol, &n );
 
	while ( token != eos ) {
		if ( token == operand ) push( &top, (input_char)atoi( &symbol ) );
		else {
			op2 = pop( &top );
			op1 = pop( &top );
 
			switch ( token ) {
				case plus   : push( &top, (input_char)(op1 + op2)) ;
					break;	
				case minus  : push( &top, (input_char)(op1 - op2) );
					break;		
				case times  : push( &top, (input_char)(op1 * op2) );
					break;
				case divide : push( &top, (input_char)(op1 / op2) );
					break;
			}
		}
		token = get_next_token( &symbol, &n );
	}
	return pop( &top );
}
 
input_char get_next_token( char *symbol, int *n )
{
	*symbol = expr[ (*n)++ ];
 
	switch ( *symbol ) {
		case '+'  : return plus;
		case '-'  : return minus;
		case '/'  : return divide;
		case '*'  : return times;
		case '\0' : return eos;
		default   : return operand;
	}
}

일단 소스는 여기까지 입니다...

원래 있던 소스들을 껴맞추구... 제가 수정하고, 만들고 하면서 많이 변환되었습니다...

중위에서 후위 변환식으로는 변환이 되는데, 계산하는 함수인... eval() 이 먹히지 않는듯 합니다...

중간에 op1 과 op2 에 들어가는 값을 따로 프린트 해보았는데... 한쪽값에 0 이 들어가는게 이상한듯 하구요....

그리고 eval 함수에는 op1과 op2의 연산 결과값을 열거형인 input_char 형으로 타입캐스팅 해버렸는데요...

순전히 제 생각으로 해놓은겁니다... push 함수를 그대로 이용하려니 저렇게 캐스팅 하지 않으니 컴파일러가 에러를 내더군요... ㅜㅜ

근데 저렇게 캐스팅 해버리면... 열거형으로 인식해서 case 문을 통한 비교연산을 하지 않을까 싶은데요...

도무지 모르겠습니다....

아놔.... 열심히 하는데, 쉽지가 않네요.... 역시 프로그래머의 길은 멀고도 힘들어.... ㅜㅜ

고수님들~~~ 도와 주세요~~~ 충고의 말씀~~~ 한마디 쏴주세요~~~~ ^^*

File attachments: 
첨부파일 크기
Plain text icon stack_calcul.cpp.txt4.08 KB
Anonymousfds의 이미지

에...

스택을 이용한... 계산기니깐..
일단 스택이 제대로 되어있어야 겠죠?

스택이 재대로 되어있나 확인하세요.
스택동작이 틀린듯..

소스 올리실때 정리좀 하고 올리시길.. 보기 힘들어서 다들 몰라라 합니다.
일렬로 쭉...이라니..

Trace의 이미지

이 소스에서 postfix()는 화면에 프린트해 주는 것 이외에 아무런 역할이 없는 것 같습니다.
postfix() 실행이 끝나고 나면 top = -1 이고 stack은 초기화되어 있습니다.

eval()의 원래 의도가 postfix()로 stack에 뭔가 채워넣은 내용을 사용하려는 것이라고도
postfix()는 원래 화면에 프린트만 하는 역할이고 eval()이 독자적으로 실행되는 것이라고도
생각할 수 없습니다. 전자라면 postfix()의 역할이 완전히 오류이고
후자라면 get_next_token()으로 여전히 stack이 아닌 expr에서 읽어들이는데다가
괄호( '(', ')' )의 처리가 빠져있는 점이 말이 되지 않습니다.

아무래도 eval() 자체의 코드만을 놓고 보면 postfix()에서 expr의 내용을 후위연산식으로
수정한 것으로 가정하는 듯 합니다. 하지만 postfix() 에 expr의 내용을 수정하는 코드는
존재하지 않습니다.
따라서 postfix()에서 printf()로 화면에 보여줄 것이 아니라
sprintf() 등을 사용해서 그 내용을 가지고 새로운 문자열을 만든 후에
그 내용을 eval()이 사용하도록 하는 편이 좋을 것 같습니다.

Trace의 이미지

get_token()이건 get_next_token()이건 두 자리 숫자를 입력받을 수 있지 않습니다.
postfix()의 경우에는 두 자리 숫자가 들어오더라도 단순히 숫자 두 개를 화면에 프린트하면
그만이었기 때문에 이렇게 처리해도 아무 문제가 없었습니다만, eval()은 이 숫자를
값으로 인식하고 계산을 해야 하므로 이런 식으로 character 1개씩을 받아서 처리하는 것은
맞지 않습니다. 이 부분도 수정을 하셔야 할 것 같습니다.

eval()에서는 stack에 숫자값들을 집어넣고 있는데, 사실 postfix()에서는 integer값을
stack에 넣고 계산에 사용할 의도가 전혀 없었던 것 같습니다. 따라서 stack은
input_char type으로 되어 있고, integer 값을 입력하기에는 맞지 않습니다.

하지만 내부적으로는 enum 자체가 integer로 구현되어 있을 것이므로 이 부분은 실행 오류 없이
무사히 넘어갈 수 있을 것 같습니다. 의미상으로는 맞지 않습니다만.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.