getchar() 获取上一个 printf() 中的最后一个字符?
我正在为深奥的语言 Brainf*ck 编写一个编译器/解释器(我不太确定 StackOverflow 的亵渎政策,所以我会审查自己,直到有人告诉我我不必这样做),然后我正在运行陷入一个非常神秘的(至少对我来说)错误,其中调试输出的最后一个字符被接受作为正在运行的 Brainf*ck 程序的输入。以下是解释器的源代码:brainf*ck.c、程序的源代码:OR.bf,以及通过 Brainf*ck 可执行文件运行 OR.bf 的输出的部分打印。 (对于混乱的代码,我提前表示歉意。作为一个有趣的项目,我在不到一天的时间内编写了解释器。) 预先感谢您的帮助!
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: <*指令指针> <*数据指针>
(SO 似乎破坏了上面的行,所以只需参考 Brainf*ck.c 中 eval() 的开头即可。
如您所见,输出的倒数第二行和最后一行表明 getchar() 是(对于某些原因)从第二行到最后一行的末尾获取换行符,而不是等待用户输入(就像在第一行那样),
我更感兴趣的是为什么这个错误出现在我的程序中,而不是如何修复它。 ,因为这让我觉得我在 C 中的输入流上没有我想象的那么好处理,但是(当然)欢迎提出修复建议。
I'm writing a compiler/interpreter for the esoteric language brainf*ck (I'm not too sure on StackOverflow's profanity policy, so I'll censor myself until somebody tells me I don't have to), and I'm running into a very mysterious (to me, at least) bug in which the last character from my debugging output is being accepted as an input to the brainf*ck program being run. The following is the source for the interpreter: brainf*ck.c, the source for the program: OR.bf, and a partial print of the output from running OR.bf through the brainf*ck executable. (Many apologies in advance for the messy code. I wrote the interpreter in less than a day as a fun project.)
Thanks in advance for the help!
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:
,------------------------------------------------>
,------------------------------------------------<
[[-]>>+<<]
>
[[-]>+<]
>
>+<
[[-]>->+++++++++++++++++++++++++++++++++++++++++++++++++<<]>
[[-]>++++++++++++++++++++++++++++++++++++++++++++++++<]>
.
output:
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
The above output is of the following format:
eval: <*instruction ptr> <*data ptr>
(SO seems to be mangling the above line, so just refer to the beginning of eval() in brainf*ck.c.
As you can see, the second to last and last lines of output indicate that getchar() is (for some reason) getting the newline character from the end of the second to last line instead of waiting for user input (as it did at line 1).
I'm more interested in why this bug has cropped up in my program than how to fix it, because it makes me think that I don't have as good a handle as I thought I had on input streams in C, but suggestions on a fix are (of course) welcome.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
C 的
stdin
是行缓冲的。这意味着当您从stdin
读取输入时,它会从用户读取一行(在本例中,您输入字符串"1\n"
)并返回尽可能多的内容根据要求,将其余部分存储在缓冲区中。由于您只要求一个字符,因此它返回'1'
并将'\n'
存储在缓冲区中。下次您请求字符时,它将返回'\n'
。在标准 C 中,没有办法阻止这种行为。最好的(通用便携式)选项是读取整行,如果用户尝试输入多个字符,则打印诊断信息,然后仅获取该行的第一个字符。这样,下次它要求输入字符时,一定会提示您。如果您想添加对用户输入
\n
以获得换行符的支持,这还为您提供了一些解析空间。这不是必要的,但可能会很好。如果您希望程序按一次按键并继续执行,而不等待用户输入整行,则需要使用特定于平台的库。在 Unix 系统(Linux、OS X 等)上,您可能应该研究一下 ncurses,但也有一些不太重量级的解决方案。在 Windows 上,我认为您所需要的只是
getch
(或者是getche
?)函数。我不久前做了这种事情,如果你愿意的话我可能可以把它捞出来,但你可能可以在网上找到它(就像我做的那样)。C's
stdin
is line-buffered. This means that when you read input fromstdin
, it reads a line from the user (in this case, you entered the string"1\n"
) and returns as much of it as asked for, storing the rest in the buffer. Since you only asked for one character, it returns'1'
and stores the'\n'
in the buffer. Next time you ask for a character, it will return the'\n'
.In standard C, there's no way to prevent this behavior. The best (universally portable) option is to read an entire line, print a diagnostic if the user tried to enter more than one character, and just take the first character of that line. That way, the next time it asks for a character, it will be guaranteed to prompt you. This also allows you some parsing room, if you wanted to add support for, say, users typing in
\n
to get a newline. It's not necessary, but it could be nice.If you want the program to take one keystroke and continue executing, without waiting for the user to enter an entire line, you'll need to use a platform specific library. On Unix systems (Linux, OS X, etc.) you should probably look into
ncurses
, but there are some less heavyweight solutions. On Windows I think all you need is thegetch
(or is itgetche
?) function. I did this kind of stuff a while ago and I could probably fish it up if you wanted, but you can probably find it online (the same way I did).