【问题标题】:Why am I getting different results with the same code as someone else?为什么我使用与其他人相同的代码得到不同的结果?
【发布时间】:2017-09-09 03:33:03
【问题描述】:

我一直在为 K.N. 的这个项目而苦苦挣扎。 King's C Programming: A Modern Approach 有一段时间了。本书指导使用堆栈编写一个简单的反向波兰符号评估器。 "使用scanf(" %c", &ch) 阅读操作符和操作数”指导书,但我认为编写一个只能评估一位数整数的评估器是荒谬和不现实的,所以我使用float 值作为操作数和char 值作为运算符。

我的问题是程序从第一个运算符开始跳过所有其他运算符'+''-''*''/'。它不会为'=' 执行此操作,也不会为float 操作数执行此操作。根据我对scanf 的了解,这不应该发生 - 请您向我解释一下这种现象吗?

编辑:嗯,看起来这段代码对下面的 Jonathan Leffler 来说工作得相当好,现在我很困惑为什么它对他有用,但对我却没有。

#include <stdio.h>
#include <stdlib.h>
#include "stack.h"

float read_expression(void);

int main(void)
{
    for(;;)
    {
        printf("Enter an RPN expression: ");
        printf("\nValue of expression: %.4f\n", read_expression());
    }
}

float read_expression(void)
{
    while(scanf("\n")==false)
    {

        float operand, op1, op2;
        char ch;

        if (scanf(" %f", &operand))
        {
            push(operand);
            continue;
        }

        else 
        {
            scanf(" %c", &ch);

            switch (ch)
            {
                case '+':
                    op2=pop();
                    op1=pop();
                    push(op1+op2);
                    break;
                case '-':
                    op2=pop();
                    op1=pop();
                    push(op1-op2);
                    break;
                case '*':
                    op2=pop();
                    op1=pop();
                    push(op1*op2);
                    break;
                case '/':
                    op2=pop();
                    op1=pop();
                    push(op1/op2);
                    break;
                case '=': return pop();
                default: exit(EXIT_SUCCESS);
            }
        }

    }
}

void stack_underflow(void)
{
    printf("\nNot enough operands in expression\n");
    exit(EXIT_FAILURE);
}

void stack_overflow(void)
{
    printf("\nExpression is too complex\n");
    exit(EXIT_FAILURE);
}

如果你想执行这段代码,这里是包含的 stack.h 文件:

#include<stdbool.h>

#define STACK_SIZE 100

void stack_overflow(void);
void stack_underflow(void);

float contents[STACK_SIZE];
int top=0;

void make_empty(void)
{
    top=0;
}

bool is_empty(void)
{
    return top==0;
}

bool is_full(void)
{
    return top == STACK_SIZE;
}

void push(float i)
{
    if (is_full())
        stack_overflow();
    else
        contents[top++]=i;
}

float pop(void)
{
    if (is_empty())
        stack_underflow();
    else
        return contents[--top];
}

编辑: 以下是我在我的 Arch linux vm 机器上使用 gcc 7.1.1-4 和 clang 4.0.1-5 编译和执行 Jonathan Leffler 的 rpn61.c 时的输出。

[lyle@ARCHnix chapter10]$ ./proj6
Enter an RPN expression: 1 2 3 4 5 + + + + =

Value of expression: 12.0000                                                                                                                                  
Enter an RPN expression: g
[lyle@ARCHnix chapter10]$ clang rpn61.c -o rpn61
[lyle@ARCHnix chapter10]$ ./rpn61
Enter an RPN expression: 1 2 3 4 5 + + + + =
4.000000 + 5.000000 = 9.000000
3.000000 + 9.000000 = 12.000000
Result = 12.000000

Value of expression: 12.0000
Enter an RPN expression: g
Unexpected operator g
[lyle@ARCHnix chapter10]$ gcc rpn61.c -o rpn61
[lyle@ARCHnix chapter10]$ ./rpn61
Enter an RPN expression: 1 2 3 4 5 + + + + =
4.000000 + 5.000000 = 9.000000
3.000000 + 9.000000 = 12.000000
Result = 12.000000

Value of expression: 12.0000
Enter an RPN expression: 1 2 + =
Result = 2.000000

Value of expression: 2.0000
Enter an RPN expression: 1 2 + + =
1.000000 + 2.000000 = 3.000000
Result = 3.000000

Value of expression: 3.0000
Enter an RPN expression: g
Unexpected operator g

【问题讨论】:

  • 你能举一个不工作的表达式的例子吗?我刚刚做了3 4 5 6 + + + =3 4 + 5 6 + 并没有发现任何问题。我还尝试了3 4 +5 6 + + = 并得到了 15,但是如果您认为 +5 是操作数,这是有道理的,而 scanf 将。
  • 好吧,对我来说1 2 3 4 5 + + + + = 返回 12,其他输入的结果类似。如果我输入像 3 4 + = 这样的单数运算符,它会忽略它并弹出最后一个 float 推送的变量。
  • 您使用的是什么平台/编译器/标准 C 库?
  • 我很好奇:当read_expression()返回float时,为什么堆栈类型为int
  • 只是我的一个错误,我更正了,但不影响程序执行。

标签: c scanf stdin


【解决方案1】:

我对代码进行了一些小的修改,以便可以将其编译为单个文件,并添加了基本的调试打印,并将堆栈类型从 int 更改为 float。通过这些更改,我无法重现声称的问题。

代码(rpn61.c):

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// #include "stack.h"

void make_empty(void);
bool is_empty(void);
bool is_full(void);
void push(float i);
float pop(void);

float read_expression(void);

int main(void)
{
    for ( ; ; )
    {
        printf("Enter an RPN expression: ");
        printf("\nValue of expression: %.4f\n", read_expression());
    }
}

float read_expression(void)
{
    while (scanf("\n") == false)
    {
        float operand, op1, op2;
        char ch;

        if (scanf(" %f", &operand))
        {
            push(operand);
        }
        else
        {
            scanf(" %c", &ch);

            switch (ch)
            {
            case '+':
                op2 = pop();
                op1 = pop();
                printf("%f + %f = %f\n", op1, op2, op1 + op2);
                push(op1 + op2);
                break;
            case '-':
                op2 = pop();
                op1 = pop();
                printf("%f - %f = %f\n", op1, op2, op1 - op2);
                push(op1 - op2);
                break;
            case '*':
                op2 = pop();
                op1 = pop();
                printf("%f * %f = %f\n", op1, op2, op1 * op2);
                push(op1 * op2);
                break;
            case '/':
                op2 = pop();
                op1 = pop();
                printf("%f / %f = %f\n", op1, op2, op1 / op2);
                push(op1 / op2);
                break;
            case '=':
                op1 = pop();
                printf("Result = %f\n", op1);
                return op1;
                //return pop();
            default:
                printf("Unexpected operator %c\n", ch);
                exit(EXIT_SUCCESS);
            }
        }
    }
    return 0;
}

#define STACK_SIZE 100

_Noreturn void stack_overflow(void);
_Noreturn void stack_underflow(void);

void stack_underflow(void)
{
    printf("\nNot enough operands in expression\n");
    exit(EXIT_FAILURE);
}

void stack_overflow(void)
{
    printf("\nExpression is too complex\n");
    exit(EXIT_FAILURE);
}

float contents[STACK_SIZE];
int top = 0;

void make_empty(void)
{
    top = 0;
}

bool is_empty(void)
{
    return top == 0;
}

bool is_full(void)
{
    return top == STACK_SIZE;
}

void push(float i)
{
    if (is_full())
        stack_overflow();
    else
        contents[top++] = i;
}

float pop(void)
{
    if (is_empty())
        stack_underflow();
    else
        return contents[--top];
}

样本输出

$ rpn61
Enter an RPN expression: 1 2 3 4 5 + + + + =
4.000000 + 5.000000 = 9.000000
3.000000 + 9.000000 = 12.000000
2.000000 + 12.000000 = 14.000000
1.000000 + 14.000000 = 15.000000
Result = 15.000000

Value of expression: 15.0000
Enter an RPN expression:  3 4 + =
3.000000 + 4.000000 = 7.000000
Result = 7.000000

Value of expression: 7.0000
Enter an RPN expression: 3 4 5 6 + + + = 
5.000000 + 6.000000 = 11.000000
4.000000 + 11.000000 = 15.000000
3.000000 + 15.000000 = 18.000000
Result = 18.000000

Value of expression: 18.0000
Enter an RPN expression: 3 4 + 5 6 + 
3.000000 + 4.000000 = 7.000000
5.000000 + 6.000000 = 11.000000
=
Result = 11.000000

Value of expression: 11.0000
Enter an RPN expression: =
Result = 7.000000

Value of expression: 7.0000
Enter an RPN expression:  3 4 + 5 6 +
3.000000 + 4.000000 = 7.000000
5.000000 + 6.000000 = 11.000000
=
Result = 11.000000

Value of expression: 11.0000
Enter an RPN expression: 11.11111 +=
7.000000 + 11.111110 = 18.111111
Result = 18.111111

Value of expression: 18.1111
Enter an RPN expression: 355 113 /
355.000000 / 113.000000 = 3.141593
q
Unexpected operator q
$

TL;DR

我无法重现您的问题...

更多调试(rpn67.c):

此代码包含更多调试信息。特别是,有一个函数dump_stack() 将信息转储到堆栈上,并且代码在读取操作数和运算符时报告它们。很遗憾,我仍然无法重现您的问题。

我想知道scanf(" %f", &amp;operand) 是否正在使用+ 运算符。它不应该,但它可能是对出了什么问题的解释。这可以通过查看操作符在读取时的内容来揭示。是不是只有+- 丢失了,还是你有一个带有*/(而不是+-)的序列也出错了?

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// #include "stack.h"

void make_empty(void);
bool is_empty(void);
bool is_full(void);
void push(float i);
float pop(void);
static void dump_stack(void);

float read_expression(void);

int main(void)
{
    for ( ; ; )
    {
        printf("Enter an RPN expression: ");
        printf("\nValue of expression: %.4f\n", read_expression());
        dump_stack();
    }
}

float read_expression(void)
{
    while (scanf("\n") == false)
    {
        float operand, op1, op2;
        char ch;

        if (scanf(" %f", &operand))
        {
            printf("NUmber: %f\n", operand);
            push(operand);
            dump_stack();
        }
        else
        {
            if (scanf(" %c", &ch) != 1)
            {
                fprintf(stderr, "Operator scan failed\n");
                exit(EXIT_FAILURE);
            }
            printf("Operator: %c\n", ch);
            dump_stack();

            switch (ch)
            {
            case '+':
                op2 = pop();
                op1 = pop();
                printf("%f + %f = %f\n", op1, op2, op1 + op2);
                push(op1 + op2);
                break;
            case '-':
                op2 = pop();
                op1 = pop();
                printf("%f - %f = %f\n", op1, op2, op1 - op2);
                push(op1 - op2);
                break;
            case '*':
                op2 = pop();
                op1 = pop();
                printf("%f * %f = %f\n", op1, op2, op1 * op2);
                push(op1 * op2);
                break;
            case '/':
                op2 = pop();
                op1 = pop();
                printf("%f / %f = %f\n", op1, op2, op1 / op2);
                push(op1 / op2);
                break;
            case '=':
                op1 = pop();
                printf("Result = %f\n", op1);
                return op1;
                //return pop();
            default:
                printf("Unexpected operator %c\n", ch);
                exit(EXIT_SUCCESS);
            }
        }
    }
    return 0;
}

#define STACK_SIZE 100

_Noreturn void stack_overflow(void);
_Noreturn void stack_underflow(void);

void stack_underflow(void)
{
    printf("\nNot enough operands in expression\n");
    exit(EXIT_FAILURE);
}

void stack_overflow(void)
{
    printf("\nExpression is too complex\n");
    exit(EXIT_FAILURE);
}

float contents[STACK_SIZE];
int top = 0;

void make_empty(void)
{
    top = 0;
}

bool is_empty(void)
{
    return top == 0;
}

bool is_full(void)
{
    return top == STACK_SIZE;
}

void push(float i)
{
    if (is_full())
        stack_overflow();
    else
        contents[top++] = i;
}

float pop(void)
{
    if (is_empty())
        stack_underflow();
    else
        return contents[--top];
}

static void dump_stack(void)
{
    printf("Stack (%d): Top", top);
    for (int i = top; i > 0; i--)
        printf(" %f", contents[i-1]);
    printf(" Bottom\n");
}

样本输出

$ rpn67
Enter an RPN expression: 1 2 3 4 5 + + + + =
NUmber: 1.000000
Stack (1): Top 1.000000 Bottom
NUmber: 2.000000
Stack (2): Top 2.000000 1.000000 Bottom
NUmber: 3.000000
Stack (3): Top 3.000000 2.000000 1.000000 Bottom
NUmber: 4.000000
Stack (4): Top 4.000000 3.000000 2.000000 1.000000 Bottom
NUmber: 5.000000
Stack (5): Top 5.000000 4.000000 3.000000 2.000000 1.000000 Bottom
Operator: +
Stack (5): Top 5.000000 4.000000 3.000000 2.000000 1.000000 Bottom
4.000000 + 5.000000 = 9.000000
Operator: +
Stack (4): Top 9.000000 3.000000 2.000000 1.000000 Bottom
3.000000 + 9.000000 = 12.000000
Operator: +
Stack (3): Top 12.000000 2.000000 1.000000 Bottom
2.000000 + 12.000000 = 14.000000
Operator: +
Stack (2): Top 14.000000 1.000000 Bottom
1.000000 + 14.000000 = 15.000000
Operator: =
Stack (1): Top 15.000000 Bottom
Result = 15.000000

Value of expression: 15.0000
Stack (0): Top Bottom
Enter an RPN expression: q
Operator: q
Stack (0): Top Bottom
Unexpected operator q
$

再现!

在 Mac 上运行的 Ubuntu 16.04 VM,rpn67 产生:

$ rpn67
Enter an RPN expression: 1 2 3 4 5 + + + + =
NUmber: 1.000000
Stack (1): Top 1.000000 Bottom
NUmber: 2.000000
Stack (2): Top 2.000000 1.000000 Bottom
NUmber: 3.000000
Stack (3): Top 3.000000 2.000000 1.000000 Bottom
NUmber: 4.000000
Stack (4): Top 4.000000 3.000000 2.000000 1.000000 Bottom
NUmber: 5.000000
Stack (5): Top 5.000000 4.000000 3.000000 2.000000 1.000000 Bottom
Operator: +
Stack (5): Top 5.000000 4.000000 3.000000 2.000000 1.000000 Bottom
4.000000 + 5.000000 = 9.000000
Operator: +
Stack (4): Top 9.000000 3.000000 2.000000 1.000000 Bottom
3.000000 + 9.000000 = 12.000000
Operator: =
Stack (3): Top 12.000000 2.000000 1.000000 Bottom
Result = 12.000000

Value of expression: 12.0000
Stack (2): Top 2.000000 1.000000 Bottom
Enter an RPN expression: q
Operator: q
Stack (2): Top 2.000000 1.000000 Bottom
Unexpected operator q
$

显示有问题。我们在scanf() 的 Linux 和 BSD/macOS 版本之间存在差异。

Linux 实现中甚至可能存在一些正义。它最多向前看一个字符,因此它将+ 读取为有效的可能开始,然后找到一个空白,意识到它作为数字无效,并且转换失败,但只有空格而不是+被推回去了。

因此,您将需要一种不同的机制来代替 if (scanf(" %f", &amp;operand)) 来读取操作数。

分辨率

有很多方法可以“解决”问题。下面的代码很hacky,但或多或​​少可以完成这项工作。更好的解决方案是将接口重新设计为read_expression(),以便它返回状态和评估的表达式。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
// #include "stack.h"

void make_empty(void);
bool is_empty(void);
bool is_full(void);
void push(float i);
float pop(void);
static void dump_stack(void);

float read_expression(void);

int main(void)
{
    for ( ; ; )
    {
        printf("Enter an RPN expression: ");
        printf("\nValue of expression: %.4f\n", read_expression());
        dump_stack();
    }
}

static inline bool is_operator(char c)
{
    return (c == '+' || c == '-' || c == '*' || c == '/' || c == '=' || c == '.' || c == 'q');
}

static bool get_token(size_t buflen, char buffer[buflen])
{
    int c;
    while ((c = getchar()) != EOF && isspace((unsigned char)c))
        ;
    ungetc(c, stdin);
    size_t i;
    for (i = 0; i < buflen - 1; i++)
    {
        c = getchar();
        if (c == EOF)
        {
            if (i > 0)
            {
                buffer[i] = '\0';
                return true;
            }
            else
            {
                printf("EOF detected\n");
                return false;
            }
        }
        else if (isspace(c))
        {
            buffer[i] = '\0';
            return true;
        }
        else if (isdigit(c) || is_operator(c))
        {
            buffer[i] = c;
        }
        else
        {
            printf("Invalid character %c\n", c);
            buffer[i] = '\0';
            return false;
        }
    }
    buffer[i] = '\0';
    return false;
}

float read_expression(void)
{
    char buffer[64];
    while (get_token(sizeof(buffer), buffer))
    {
        float operand;

        if (sscanf(buffer, " %f", &operand))
        {
            printf("NUmber: %f\n", operand);
            push(operand);
            dump_stack();
        }
        else
        {
            char ch;
            if (sscanf(buffer, " %c", &ch) != 1)
            {
                fprintf(stderr, "Operator scan failed\n");
                exit(EXIT_FAILURE);
            }
            printf("Operator: %c\n", ch);
            dump_stack();
            float op1, op2;
            switch (ch)
            {
            case '+':
                op2 = pop();
                op1 = pop();
                printf("%f + %f = %f\n", op1, op2, op1 + op2);
                push(op1 + op2);
                break;
            case '-':
                op2 = pop();
                op1 = pop();
                printf("%f - %f = %f\n", op1, op2, op1 - op2);
                push(op1 - op2);
                break;
            case '*':
                op2 = pop();
                op1 = pop();
                printf("%f * %f = %f\n", op1, op2, op1 * op2);
                push(op1 * op2);
                break;
            case '/':
                op2 = pop();
                op1 = pop();
                printf("%f / %f = %f\n", op1, op2, op1 / op2);
                push(op1 / op2);
                break;
            case '=':
                op1 = pop();
                printf("Result = %f\n", op1);
                return op1;
                //return pop();
            case 'q':
                printf("Quitting\n");
                exit(EXIT_SUCCESS);
            default:
                printf("Unexpected operator %c\n", ch);
                exit(EXIT_FAILURE);
            }
        }
    }
    printf("EOF or invalid input\n");
    exit(EXIT_FAILURE);
}

#define STACK_SIZE 100

_Noreturn void stack_overflow(void);
_Noreturn void stack_underflow(void);

void stack_underflow(void)
{
    printf("\nNot enough operands in expression\n");
    exit(EXIT_FAILURE);
}

void stack_overflow(void)
{
    printf("\nExpression is too complex\n");
    exit(EXIT_FAILURE);
}

float contents[STACK_SIZE];
int top = 0;

void make_empty(void)
{
    top = 0;
}

bool is_empty(void)
{
    return top == 0;
}

bool is_full(void)
{
    return top == STACK_SIZE;
}

void push(float i)
{
    if (is_full())
        stack_overflow();
    else
        contents[top++] = i;
}

float pop(void)
{
    if (is_empty())
        stack_underflow();
    else
        return contents[--top];
}

static void dump_stack(void)
{
    printf("Stack (%d): Top", top);
    for (int i = top; i > 0; i--)
        printf(" %f", contents[i-1]);
    printf(" Bottom\n");
}

样本输出

$ rpn71
Enter an RPN expression: 1 2 3 4 5 + + + + =
NUmber: 1.000000
Stack (1): Top 1.000000 Bottom
NUmber: 2.000000
Stack (2): Top 2.000000 1.000000 Bottom
NUmber: 3.000000
Stack (3): Top 3.000000 2.000000 1.000000 Bottom
NUmber: 4.000000
Stack (4): Top 4.000000 3.000000 2.000000 1.000000 Bottom
NUmber: 5.000000
Stack (5): Top 5.000000 4.000000 3.000000 2.000000 1.000000 Bottom
Operator: +
Stack (5): Top 5.000000 4.000000 3.000000 2.000000 1.000000 Bottom
4.000000 + 5.000000 = 9.000000
Operator: +
Stack (4): Top 9.000000 3.000000 2.000000 1.000000 Bottom
3.000000 + 9.000000 = 12.000000
Operator: +
Stack (3): Top 12.000000 2.000000 1.000000 Bottom
2.000000 + 12.000000 = 14.000000
Operator: +
Stack (2): Top 14.000000 1.000000 Bottom
1.000000 + 14.000000 = 15.000000
Operator: =
Stack (1): Top 15.000000 Bottom
Result = 15.000000

Value of expression: 15.0000
Stack (0): Top Bottom
Enter an RPN expression: q
Operator: q
Stack (0): Top Bottom
Quitting
$

测试:在使用 GCC 7.2.0 运行 macOS Sierra 10.12.6 的 Mac 上,以及使用 GCC gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609 在 Mac 上运行的 Ubuntu 16.04 VM 上。

【讨论】:

  • @ JonathanLeffler 所以,我刚刚编译并执行了你稍微修改过的 rpn61.c,它仍然有同样的问题。我无法想象为什么它对你有用而不对我有用......
  • 我也没有。好的;我发现我正在使用运行 macOS Sierra 10.12.6 并使用 GCC 7.2.0 编译的 Mac。让我们找出您正在使用的平台。你能显示你得到的调试打印输出吗?你有我展示空间的空间吗?也许在阅读时打印出运算符和操作数是值得的。
  • @Lyle:查看我用来在 Linux VM 上重现该问题的 rpn67.c 程序,以及包含该问题解决方法的 rpn71.c 程序。有更好的方法来解决这个问题,但它们涉及对代码进行更大的更改。解决方法代码仅在 RPN 操作数和操作是空格分隔的情况下才有效。如果它们一起运行(没有空格),则代码将整个字符串视为单个操作数或运算符,并忽略任何额外的尾随材料。因此,输入1 2+= 只会堆叠12 而不会执行任何运算符。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-07
  • 2022-11-25
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多