Как вы делаете интерфейс меню, который принимает двузначные целые числа или символы в C? - PullRequest
0 голосов
/ 28 сентября 2018

Я возился с этой программой последние пару часов, и я не могу найти способ заставить эту программу работать.Я начал с меню стиля оператора switch, но затем у меня возникла проблема, из-за которой меню проваливалось и выходило, и я не мог этого понять, поэтому я просто переключил свой код на меню, основанное на другом.Идея программы заключается в следующем:

Напишите и протестируйте C-программу, которая реализует стековый целочисленный калькулятор.Программа принимает ввод, пока не введено q.Однако мои трудности заключаются в том, чтобы заставить меню принимать числа больше 10.

В моей программе все функции работают должным образом, за исключением того, что когда я ввожу двузначное целое число, обе цифры будут сохраняться по отдельности.Я знаю, что это потому, что у меня есть настройки меню для чтения и работы с символами, но я не смог понять, как заставить работать массив символов.Я никогда раньше не программировал на C, поэтому идея динамического распределения памяти намекает на меня, поскольку я не совсем уверен, когда это необходимо.Вот исходный код, который у меня есть для программы:

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

#define SIZE 6

int stack[SIZE]; //stack size
int top = 0; //top of stack

void pop();
void clear();
void display();
void top_element();
void add();
void multiply();
void subtract();
void division();
void power();

int main()
{
    char input;
    int flag = 1;

while(flag == 1)
{
    printf(": ");
    scanf(" %c",&input);

    if(isdigit(input))
    {
        if(top < SIZE)
        {
            stack[top] = input - '0';
            top++;
        }
        else
            printf("Error: stack overflow\n");
    }
    else if(input=='p')
        pop();
    else if(input=='c')
        clear();
    else if(input=='d')
        display();
    else if(input=='=')
        top_element();
    else if(input=='+')
        add();
    else if(input=='*')
        multiply();
    else if(input=='-')
        subtract();
    else if(input=='/')
        division();
    else if(input=='^')
        power();
    else if(input=='q')
        flag = 0;
    else
        printf("Error: invalid command\n");
    }
    printf("Goodbye!\n");
    return 0;
}

void pop()
{
if(top==0)
    printf("Error: stack is empty\n");
else
    top--;
}

void clear()
{
    top=0;
}

void display()
{
    int i;
    if(top == 0)
    printf("Error: stack is empty\n");

    else
    {
    for(i = top - 1; i >= 0; i--)
        printf("%d\n",stack[i] );
    }
}

void top_element()
{
    printf("%d\n",stack[top-1] );
}

void add()
{
    if(top<2)
        printf("Error: not enough operands for the requested operation\n");
    else
    {
        int ans=stack[top-1]+stack[top-2];
        stack[top-2]=ans;
        top--;
    }
}
void multiply()
{
    int ans=stack[top-1]*stack[top-2];
    stack[top-2]=ans;
    top--;
}
void subtract()
{
    if(top < 2)
        printf("Error: not enough operands for the requested operation\n");
    else
    {
        int ans = (stack[top-2] - stack[top-1]);
        stack[top-2]=ans;
        top--;
    }
}
void division()
{
    if(top < 2)
        printf("Error: not enough operands for the requested operation\n");
    else
    {
        if(stack[top-1]==0)
            printf("Error: attempt to divide by 0\n");
        else
        {
        int ans = (stack[top-2]/stack[top-1]);
        stack[top-2]=ans;
        top--;
        }
    }
}
void power()
{
    if(top < 2)
        printf("Error: not enough operands for the requested operation\n");
    else
    {
        int ans = pow(stack[top - 2], stack[top - 1]);
        stack[top - 2] = ans;
        top--;
    }
}

Ответы [ 2 ]

0 голосов
/ 29 сентября 2018

У меня есть несколько моментов, которые я хочу отметить, и я не хочу превращать это в TLDR, поэтому я постараюсь, чтобы каждая проблема была отделена от абзацев.Вы можете взять это все с зерном соли;В конце концов, это всего лишь совет.

  • Директива формата, которую вы ищете: %2[0123456789].Передайте указатель на место подходящего размера для хранения трех символов и проверьте возвращаемое значение.Эта директива должна вызывать scanf в одиночку, иначе у вас, вероятно, возникнет аневризма, отладившая проблему, связанную с пустыми полями, позже, поэтому возвращаемое значение «зеленого света», указывающее, что ваша программа успешно обрабатывает корректный вводявляется то, что scanf("%2[0123456789]", ptr_into_array_of_char) вернет 1. Любое другое возвращаемое значение означает, что янтарный или красный огни произошли.Имейте в виду, я интерпретирую ваши спецификации (которые являются неполными) довольно строго здесь ... на самом деле я бы просто использовал %d и был бы рад, что мои пользователи вдвое уменьшают свои шансы на развитие артрита, введя 1 вместо01 (и у вас также реже возникают аневризмы, когда вы не имеете дело с %[).
  • Наши компиляторы обычно выдают сообщения об ошибках и прерывают компиляцию, когда мы допускаем некоторую синтаксическую ошибку, но это требование противоречитто зерно: «Программа принимает ввод до тех пор, пока не будет введено q.» Я надеюсь, что ваша полная спецификация объясняет, что должно произойти, когда пользователь отклоняется от ожиданий.Я полагаю, вы могли бы выдать ошибку, очистить стек, прочитать до конца строки и просто работать так, как будто программа перезапустилась ... что-то вроде scanf("%*[^\n]"); getchar(); puts("Error message here"); top = 0;?Обычно мы используем некоторую комбинацию клавиш, например CTRL + d (в Linux) или CTRL + Z (в Windows), чтобы закрыть stdin, что означает прекращение ввода.
  • "идея динамического выделения памяти намекаетme ", и поэтому вы будете благодарны за то, что вам, вероятно, здесь не следует использовать динамическое выделение памяти, если только вы не хотите, чтобы ваш стек вырос по сравнению с жестко заданными 6 слотами, которые вы установили,возможно ...
  • Я предполагаю, что название этого вопроса смешалось в замешательстве;Вы не разрабатываете меню, а вместо этого реализуете грамматику.Посмотрите, как «меню» gcc s разработано для вдохновения здесь.Если вы когда-нибудь испытывали желание создать меню около stdin, stop ;возможно, вам действительно нужен графический интерфейс для указания и щелчка, потому что Unix не работает.
  • Объявление void fubar(void); с последующим void fubar() { /* SNIP */ } - неопределенное поведение из-за некоторых технических исторических артефактов, и то же самое происходитдля int main() ... Вот почему вам лучше всего выбрать книгу, которая специально преподает С, написанную кем-то авторитетным, для изучения С. Есть много тонких нюансов, которые могут вас поймать.
  • Вкл.Примечание о прототипах функций и т. д., считают, что стек является общей структурой данных.В качестве альтернативного мысленного эксперимента рассмотрим, какую боль будет strcpy использовать, если он работает только с массивами, объявленными с областью видимости файла.Логически следует, что все требования к внешним данным должны исходить из его аргументов, а не из переменной, то есть stack, объявленной с областью действия файла.
  • Нас учат использовать память несколько осторожно, и мне кажется, что использование переменной исключительно в качестве управляющего выражения, как это противоречит этим урокам.Там, где существуют такие конструкции, как break, continue и goto, возможна более чистая альтернатива без объявлений переменных (и, следовательно, дополнительный свободный регистр, который можно использовать для чего-то другого).
0 голосов
/ 29 сентября 2018

В этот раз проблема не в scanf(), а в том, как вы анализируете входные данные.

Нет ничего плохого в том, чтобы разбирать входной символ за символом, наоборот, это значительно упрощаетхотя бы почти во всех случаях.Но анализ символа за символом также означает, что вы анализируете каждое положительное число больше девяти, а также символ за символом или лучше цифра за цифрой - вы должны построить полное число из одной цифры.Вы анализируете «слева направо», поэтому просто умножьте на десять и добавьте цифру.Промойте и повторяйте, пока у вас не останется цифр, и положите результат в стек.

Пример:

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

#define SIZE 6

/* you should look up what "static" means and when and how to use */
static int stack[SIZE];         //stack size
static int top = 0;             //top of stack

/* 
 * You need to expicitely add "void" to the argumet list.
 * It defaults to "int" otherwise.
 * Please do yourself a favor and switch all warnings on.
 */
void pop(void);
void clear(void);
void display(void);
void top_element(void);
void add(void);
void multiply(void);
void subtract(void);
void division(void);
void power(void);

/* Most checks and balances omitted! */
int main(void)
{
  /* "int" to make things easier */
  int input;
  int flag = 1, anumber;

  while (flag == 1) {
    printf(": ");
    /* get a(n ASCII) character */
    input = fgetc(stdin);

    if (isdigit(input)) {
      anumber = 0;
      /* 
       * We have a digit. Parse input for more digits until
       * no further digits appear and add all digits to "anumber".
       * We assume a decimal representation here.
       */

      /* TODO: check for overflow! */
      for (;;) {
        anumber *= 10;
        anumber += input - '0';
        input = fgetc(stdin);

        if (!isdigit(input)) {
          break;
        }
      }
      /* Push number on the stack */
      if (top < SIZE) {
        stack[top] = anumber;
        top++;
      } else {
        printf("Error: stack overflow\n");
      }
    }
    /* "input" from fgetc() is an integer, we can use a switch */
    switch (input) {
      case 'p':
        pop();
        break;
      case 'c':
        clear();
        break;
      case 'd':
        display();
        break;
      case '=':
        top_element();
        break;
      case '+':
        add();
        break;
      case '^':
        power();
        break;
      case 'q':
        flag = 0;
        break;
      default:
        printf("Error: invalid command\n");
        break;
    }
  }

  printf("Goodbye!\n");
  return 0;
}


void pop(void)
{
  if (top == 0)
    printf("Error: stack is empty\n");
  else
    top--;
}

void clear(void)
{
  top = 0;
}

void display(void)
{
  int i;
  if (top == 0)
    printf("Error: stack is empty\n");

  else {
    for (i = top - 1; i >= 0; i--)
      printf("%d\n", stack[i]);
  }
}

void top_element(void)
{
  printf("%d\n", stack[top - 1]);
}

void add(void)
{
  if (top < 2)
    printf("Error: not enough operands for the requested operation\n");
  else {
    int ans = stack[top - 1] + stack[top - 2];
    stack[top - 2] = ans;
    top--;
  }
}

/* Using pow() from math.h is not a good idea beause it uses floating point */
/* TODO check for overflows! */
static int integer_pow(int x, int n)
{
  int r;
  r = 1;
  while (n != 0) {
    if (n & 1) {
      r *= x;
    }
    x *= x;
    n >>= 1;
  }
  return r;
}

void power(void)
{
  if (top < 2)
    printf("Error: not enough operands for the requested operation\n");
  else {
    int ans = integer_pow(stack[top - 2], stack[top - 1]);
    stack[top - 2] = ans;
    top--;
  }
}

Тест:

$ ./stackbcalc 
: 123+23=
Error: not enough operands for the requested operation
: 23
: Error: invalid command
: q
Goodbye!

Не работает.Зачем?Функция add() ожидает два операнда в стеке.Вам также нужно поместить + в стек (это целое число), и как только вы в конце наберете =, вы сможете оценить стек.Вам может понадобиться кое-что узнать о нотации infix / postfix / prefix, чтобы успешно это сделать.

Подсказка: я бы также проигнорировал пробел (пробел и табуляция, возможно, даже возврат) в переключателе.

...