getchar () берет последний символ из предыдущего printf ()? - PullRequest
1 голос
/ 08 августа 2011

Я пишу компилятор / интерпретатор для эзотерического языка brainf * ck (я не слишком уверен в политике ненормативной лексики StackOverflow, поэтому я буду подвергать цензуре себя, пока кто-нибудь не скажет мне, что я не обязан), и яЯ сталкиваюсь с очень таинственной (по крайней мере для меня) ошибкой, в которой последний символ из моего отладочного вывода принимается в качестве входных данных для выполняемой программы brainf * ck.Ниже приведен источник для интерпретатора: brainf * ck.c, источник для программы: OR.bf и частичная распечатка вывода из запуска OR.bf через исполняемый файл brainf * ck.(Заранее извиняюсь за грязный код. Я написал переводчик менее чем за день как забавный проект.) Заранее благодарен за помощь!

brainf * ck.c:

#include <stdio.h>
#include <string.h>

char* readCmd(int, char* []);
void readProg(FILE*,char[]);
int checkSyntax(char[]);
void init(char*, char[], char[]);
void run(unsigned char**, unsigned char**);
void eval(unsigned char**, unsigned char**);


int main(int argc, char* argv[])
{
  unsigned char data[30000] = {0};
  unsigned char* dptr = &(data[0]);
  unsigned char** dpptr = &dptr;
  unsigned char inst[30000] = {0};
  unsigned char* iptr = &(inst[0]);
  unsigned char** ipptr = &iptr;
  char* cmd = readCmd(argc, argv);
  FILE* src = fopen(cmd, "r");
  if(src != NULL)
  {
    readProg(src, inst);
    if(checkSyntax(inst))
    {
      run(ipptr, dpptr);
    }
    else
    {
      printf("Syntax error. Please fix your code\n");
    }
  }
  else
  {
    printf("File '%s' not found.\n", cmd);
  }
  fclose(src);
  return 0;
}


char* readCmd(int argc, char** argv)
{
  char* cmd = NULL;
  if(argc == 2)
  {
    cmd = argv[1];
  }
  else
  {
    cmd = "";
    printf("Usage: %s <filename>.bf\n", argv[0]);
  }
  return cmd;
}


void readProg(FILE* src, char inst[])
{
  int i = 0;
  while(!feof(src))
  {
    char c = fgetc(src);
    if(c == '<' || c == '>' || c == '+' || c == '-' || c == '.' || c == ',' || c == '[' || c == ']')
    {
      inst[i] = c;
      i++;
    }
  }
}


int checkSyntax(char inst[])
{
  int open = 0;
  int i = 0;
  for(i = 0; i < strlen(inst); i++)
  {
    if(inst[i] == '[')
      open++;
    if(inst[i] == ']')
      open--;
  }
  return !open;
}


void init(char* cmd, char instruct[], char data[])
{
  return;
}


void run(unsigned char** ipptr, unsigned char** dpptr)
{
  while(**ipptr != 0)
  {
    eval(ipptr, dpptr);
    (*ipptr)++;
  }
  return;
}


void eval(unsigned char** ipptr, unsigned char** dpptr)
{
  //fprintf(log, "eval: %c %i %x %x\n", **ipptr, **dpptr, *ipptr, *dpptr);
  printf("eval: %c %i %x %x\n", **ipptr, **dpptr, *ipptr, *dpptr);
  getch();
  int open = 0;
  switch(**ipptr)
  {
    case '>':
      (*dpptr)++;
      break;
    case '<':
      (*dpptr)--;
      break;
    case '+':
      //printf("b: dptr:%x *dptr:%i\n", *dpptr, **dpptr);
      (**dpptr)++;
      //printf("a: dptr:%x *dptr:%i\n", *dptr, **dpptr);
      break;
    case '-':
      (**dpptr)--;
      break;
    case '.':
      putchar(**dpptr);
      break;
    case ',':
      **dpptr = getchar();
      break;
    case '[':
      if(**dpptr)
      {
        //(*ipptr)++;
      }
      else
      {
        open++;
        do {
          (*ipptr)++;
          if(**ipptr == '[')
            open++;
          if(**ipptr == ']')
            open--;
        } while(open);
      }
      break;
    case ']':
      if(**dpptr)
      {
        open = 1;
        do {
          (*ipptr)--;
          if(**ipptr == ']')
            open++;
          if(**ipptr == '[')
            open--;
        } while(open);
      }
      break;
    default:
      break;
  }
  return;
}

OR.bf:

,------------------------------------------------>
,------------------------------------------------<
[[-]>>+<<]
>
[[-]>+<]
>
>+<
[[-]>->+++++++++++++++++++++++++++++++++++++++++++++++++<<]>
[[-]>++++++++++++++++++++++++++++++++++++++++++++++++<]>
.

вывод:

user@userland ~/brainf*ck
$ brainf*ck.exe OR.bf
eval: , 0 22149c 2289d0
1
eval: - 49 22149d 2289d0
eval: - 48 22149e 2289d0
eval: - 47 22149f 2289d0
eval: - 46 2214a0 2289d0
eval: - 45 2214a1 2289d0
eval: - 44 2214a2 2289d0
eval: - 43 2214a3 2289d0
eval: - 42 2214a4 2289d0
eval: - 41 2214a5 2289d0
eval: - 40 2214a6 2289d0
eval: - 39 2214a7 2289d0
eval: - 38 2214a8 2289d0
eval: - 37 2214a9 2289d0
eval: - 36 2214aa 2289d0
eval: - 35 2214ab 2289d0
eval: - 34 2214ac 2289d0
eval: - 33 2214ad 2289d0
eval: - 32 2214ae 2289d0
eval: - 31 2214af 2289d0
eval: - 30 2214b0 2289d0
eval: - 29 2214b1 2289d0
eval: - 28 2214b2 2289d0
eval: - 27 2214b3 2289d0
eval: - 26 2214b4 2289d0
eval: - 25 2214b5 2289d0
eval: - 24 2214b6 2289d0
eval: - 23 2214b7 2289d0
eval: - 22 2214b8 2289d0
eval: - 21 2214b9 2289d0
eval: - 20 2214ba 2289d0
eval: - 19 2214bb 2289d0
eval: - 18 2214bc 2289d0
eval: - 17 2214bd 2289d0
eval: - 16 2214be 2289d0
eval: - 15 2214bf 2289d0
eval: - 14 2214c0 2289d0
eval: - 13 2214c1 2289d0
eval: - 12 2214c2 2289d0
eval: - 11 2214c3 2289d0
eval: - 10 2214c4 2289d0
eval: - 9 2214c5 2289d0
eval: - 8 2214c6 2289d0
eval: - 7 2214c7 2289d0
eval: - 6 2214c8 2289d0
eval: - 5 2214c9 2289d0
eval: - 4 2214ca 2289d0
eval: - 3 2214cb 2289d0
eval: - 2 2214cc 2289d0
eval: > 1 2214cd 2289d0
eval: , 0 2214ce 2289d1
eval: - 10 2214cf 2289d1

Приведенный выше вывод имеет следующий формат: eval: <* инструкция ptr> <* data ptr>

(похоже, SO искажает вышеприведенную строку, поэтому просто обратитесь к началу eval () в brainf * ck.c.

Как видите, вторая, последняя и последняя строкивывод указывает, что getchar () (по какой-то причине) получает символ новой строки с конца второй до последней строки вместо ожидания ввода пользователя (как это было в строке 1).

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

1 Ответ

2 голосов
/ 08 августа 2011

C's stdin является буферизованной строкой.Это означает, что когда вы читаете ввод из stdin, он читает строку от пользователя (в данном случае вы ввели строку "1\n") и возвращает столько, сколько было запрошено, сохраняя остаток в буфере.Поскольку вы запрашивали только один символ, он возвращает '1' и сохраняет '\n' в буфере.В следующий раз, когда вы попросите персонажа, он вернет '\n'.

. В стандартном C нет способа предотвратить такое поведение.Лучший (универсально переносимый) вариант - прочитать всю строку, распечатать диагностику, если пользователь попытался ввести более одного символа, и просто взять первый символ этой строки.Таким образом, в следующий раз, когда он попросит персонажа, он гарантированно подскажет вам.Это также дает вам немного места для разбора, если вы хотите добавить поддержку, скажем, пользователям, набирающим \n для получения новой строки.В этом нет необходимости, но это может быть неплохо.

Если вы хотите, чтобы программа взяла одно нажатие клавиши и продолжила выполнение, не дожидаясь, пока пользователь введет всю строку, вам потребуется использовать библиотеку, специфичную для платформы.,В Unix-системах (Linux, OS X и т. Д.) Вам, вероятно, стоит взглянуть на ncurses, но есть некоторые менее тяжелые решения.В Windows я думаю, что все, что вам нужно, это getch (или это getche?) Функция.Я делал такие вещи некоторое время назад, и, возможно, я мог бы выловить их, если бы вы захотели, но вы, вероятно, можете найти их в Интернете (так же, как я).

...