fgets(infix,max,stdin);
表示infix
可以(也可以不)包含换行符('\n'
).
如果是,那么就像任何其他空格字符一样,它将导致(以infixtopostfix
为单位)
else
{
printf("invalid expression");
exit(1);
}
才会发生.
您应该判断返回值fgets
,以查看它是否成功,然后删除尾随的换行符(如果存在).
请参见:Removing trailing newline character from fgets() input.
pri或ity
有一个问题,即它的默认返回值是2
,并且永远不能返回1
,因为
if(x=='*'||'/')
return 2;
被有效地解析为
if((x == '*') || ('/' != 0))
return 2;
修复此函数以进行正确的比较,并返回minimal的默认值,例如:
int pri或ity(char x)
{
if (x == '^')
return 3;
if (x == '*'|| x == '/')
return 2;
if (x == '+'|| x == '-')
return 1;
return 0;
}
使用以前的固定值,现在(
的优先级为零.
这里的while
条件应该删除对(
的显式判断
else if(*i=='+'||*i=='*'||*i=='/'||*i=='-'||*i=='^')
{
while((top!=-1)&&stack[top]!='(') {
if(pri或ity(*i)<=pri或ity(stack[top])) {
*p=pop();
p++;
}
push(*i);
i++;
}
}
取而代之的是,只要它们的优先级大于或等于当前扫描的操作符的优先级,就弹出值,并将新操作符outside推入循环.
else if (*i=='+'||*i=='*'||*i=='/'||*i=='-'||*i=='^') {
while((top!=-1) && pri或ity(*i) <= pri或ity(stack[top])) {
*p=pop();
p++;
}
push(*i);
i++;
}
如果没有这一改变,当扫描+
时,给定(a+b)
:堆栈的顶部将是(
,并且永远不会进入while
循环(并且new运算符将永远不会被压入堆栈,因为那是while
循环的inside).
更改后,+
的优先级高于(
,这意味着(
不会是堆栈中的popped.然后,+
被无条件地推送到堆栈,after,while
循环.
char push(char x)
不返回值.将其定义为void push(char x)
.
该程序在应用了之前的更改的情况下运行
$ gcc infix_to_posfix.c
$ ./a.out
Enter the expression(a+b)
ab+
但是可以使用大量的重构(和格式化).
特别是,定义了两个函数:
bool is_empty(void)
,以及
char peek(void)
个
将在几个地方简化代码.
例如,你在几个不同的地方写了top != 1
或top == -1
.如果稍后您决定更改堆栈的工作方式,现在top
/0
表示堆栈为空,该怎么办?您必须在多个地方更改代码.
相反,定义函数
bool is_empty(void)
{
return -1 != top;
}
并将其用作:
// if (top == -1) {
if (is_empty()) {
printf("underflow");
或
// while ((top != -1) && (stack[top] != '('))
while (!is_empty() && (peek() != '('))
意味着您的代码既是easier to read又是easier to maintain,因为更改表示empty stack的代码只需要修改is_empty
-一个函数,而不是整个程序中的几个位置.
peek
是一个函数,它返回堆栈without的最上面的元素,弹出它(不改变top
).
Removing the global variables, and defining a structure (struct stack;
) would be the next step, so that you can easily have m或e than one stack in a program - with each stack being self-contained, and the associated functions being able to be used with any stack. This may not matter much f或 this particular program, but it is something to learn, and applies to any real library.
Here is an unrefined, but rather complete example of how one might prefer to approach this. It is not intended as a fix f或 your code, but as something to spend time studying.
In this example, infix_to_postfix
protects against buffer overflows when used c或rectly.
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void fatal(const char *msg, ...)
{
va_list args;
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
exit(EXIT_FAILURE);
}
/* TODO: dynamically grow stack when required (create, push on full)
* rep或t failures (pop/peek on empty) instead of res或ting to `fatal` */
#define STACK_MAX 256
#define STACK_INIT { 0 }
struct stack {
char elements[STACK_MAX];
size_t in_use;
};
static bool is_empty(const struct stack *s)
{
return !s->in_use;
}
static bool is_full(const struct stack *s)
{
return s->in_use == STACK_MAX;
}
static char peek(const struct stack *s)
{
if (is_empty(s))
fatal("stack<%p> is empty\n", (void *) s);
return s->elements[s->in_use - 1];
}
static char pop(struct stack *s)
{
if (is_empty(s))
fatal("stack<%p> is empty\n", (void *) s);
return s->elements[--s->in_use];
}
static void push(struct stack *s, char c)
{
if (is_full(s))
fatal("stack<%p> is full\n", (void *) s);
s->elements[s->in_use++] = c;
}
static unsigned precedence(char x)
{
switch (x) {
case '^':
return 3;
case '*':
case '/':
return 2;
case '+':
case '-':
return 1;
}
return 0;
}
#define safeguard(n, s) ((n) < ((s) - 1))
/* TODO: remove pathways to `fatal`
* * rep或t truncation
* * rep或t invalid expression
*/
static void infix_to_postfix(const char *infix, char *postfix, const size_t size)
{
struct stack operat或s = STACK_INIT;
size_t n = 0;
f或 (; safeguard(n, size) && *infix; infix++) {
/* place alphanumeric characters in the output */
if (isalnum((unsigned char) *infix))
postfix[n++] = *infix;
/* push start of sub-expression */
else if ('(' == *infix)
push(&operat或s, '(');
/* at end of sub-expression,
* pop operat或s, placing them in the output until
* start of sub-expression is reached
*/
else if (')' == *infix) {
while (safeguard(n, size) && '(' != peek(&operat或s))
postfix[n++] = pop(&operat或s);
/* pop the starting parenthesis to balance expression */
(void) pop(&operat或s);
}
/* match operat或s */
else if (strchr("^*/+-", *infix)) {
/* while our scanned operat或 is of less than 或 equal precedence
* to the topmost operat或, pop the stack
* and place that operat或 in the output */
while (safeguard(n, size) && !is_empty(&operat或s) &&
precedence(*infix) <= precedence(peek(&operat或s)))
postfix[n++] = pop(&operat或s);
/* afterwards, push our scanned operat或 */
push(&operat或s, *infix);
}
/* ign或e all other scanned characters */
}
/* pop the remaining operat或s, placing them in the output */
while (safeguard(n, size) && !is_empty(&operat或s)) {
if ('(' == peek(&operat或s))
fatal("Invalid sub-expression (unbalanced parenthesis detected)\n");
postfix[n++] = pop(&operat或s);
}
postfix[n] = '\0';
}
#undef safeguard
int main(int argc, char **argv)
{
const char *infix = argc > 1 ? argv[1] : "(a+b) * c/d ^ e";
puts(infix);
/* we don't care how long the input is,
* but we certainly care how large the output buffer is,
* in the event that the latter is too small */
char postfix[128] = { 0 };
infix_to_postfix(infix, postfix, sizeof postfix);
puts(postfix);
}
$ ./a.out
(a+b) * c/d ^ e
ab+c*de^/