我在为我的一个类编写的C代码上编写gets()
函数时遇到了一些问题。所以我已经有了一个getchar()
函数,但是在汇编上,我用extern
从C中调用它,问题是,在我运行代码的时候,我输入了一个字符串,它没有显示完整的字符串,而是一些字符。
这是我的ATM机代码:C代码:
extern char getchar(void);
extern void putchar(char data);
void gets(char *str);
void puts(char *str);
void new_line();
char string[20];
int main(){
while(1){
gets(string);
new_line();
puts(string);
}
return 0;
}
void new_line(){
putchar(0xD);
putchar(0xA);
}
void gets(char *str){
unsigned char i = 0;
while((*str = getchar()) != 0xD){
str[i] = getchar();
i++;
}
}
void puts(char *str){
while(*str){
putchar(*str++);
}
}
和我的ASM代码以防万一:
.MODEL tiny
.CODE
public _putchar
public _getchar
_putchar PROC
push bp
mov bp, sp
mov dl, [bp + 4]
mov ah, 2
int 21h
pop bp
ret
_putchar ENDP
_getchar PROC
push bp
mov bp, sp
mov ah, 1
int 21h
mov [bp + 4], al
pop bp
ret
_getchar ENDP
END
我在Arduino Mega上运行代码,使用MTTTY和我们老师提供的8086解释器。
有什么办法可以用get()函数解决这个问题,这样我就可以正确地显示输入字符串了吗?
例如,如果我输入"hello world“,它只打印"l ol”。
发布于 2018-09-03 04:41:09
无论gets
实现如何,您的C gets
实现都已损坏。你可以用桌面上的普通调试器在普通的C实现上调试它。
您调用getchar()
两次,并且每隔一次结果才保存一次。
第一个结果被分配给str[0]
并检查'\r'
。
// your version with comments
void gets_original_buggy (char *str){
unsigned char i = 0; // this is an index; it should be an `int` or `size_t`
while((*str = getchar()) != 0xD){ // overwrite the first byte of the string with an input
str[i] = getchar(); // get ANOTHER new input and save it to the end.
i++;
}
// str[i] = 0; // missing zero terminator.
}
我是这样写的:
#include <stddef.h>
//#include <stdio.h>
extern unsigned char getchar(void);
// returns length.
// negative means EOF. TODO: implement an EOF check if your getchar() supports it.
// FIXME: take a max-length arg to make it possible to prevent buffer overflows.
ptrdiff_t gets(char *str) {
char *start = str; // optional
char tmp; // read chars into a local, and check before assigning anything to *str
while( (tmp = getchar()) != '\r') {
// TODO: also check for EOF
*str++ = tmp; // classic pointer post-increment idiom
}
*str = 0; // terminate the C string.
return str - start; // optional, return the length
}
返回字符串长度总是有用的,而不是在知道它的函数中丢弃它,这只需要编译器额外的几条指令。指针增量简化了寻址模式,节省了代码大小。
(用gcc and clang for 32-bit x86 on Godbolt编译得很好,应该与x86-16非常相似。)
您还可以根据您的getchar实现检查'\n'
,以及它是否规范化了行结束。请记住,如果你有DOS \r
行结尾,那么在读取\n
之后停止将会留下一个未读的"\r\n"
。
在ISO中,对于以文本模式打开的文件,getchar()
应该只为您提供'\n'
行结尾,但是您已经使getchar
只是DOS (从标准输入读取字符,带回显)函数的包装器。因此,这就是设置实现行为的原因。
asm错误:
# in _getchar:
mov [bp + 4], al ; clobber memory you don't own.
这将占用返回地址上方的内存。char getchar(void)
不接受任何参数,因此您的函数并不“拥有”该内存。你的编译器应该期望一个AL的返回值。(如果你认为这是通过引用返回的,不,你只是覆盖了指针arg。除了调用者甚至没有传递一个。)
如果您希望您的getchar
能够返回不同于0xFF
字节的EOF,请在进行系统调用后将其声明为返回int
,并将AH声明为零。(因此,您可以在AX中返回一个16位的值,或者在AX中返回一个零扩展的unsigned char
(即AL中的值)。
顺便说一句, 和在ISO 中删除了是有原因的:在读取未知长度的输入时,不可能防止缓冲区溢出。
你的函数应该接受一个大小限制作为第二个参数。
直接对Arduino的AVR或ARM CPU编程可能比在仿真的8086上使用DOS系统调用更容易学习,也更有用。如果你打算这样做,那么在真实的硬件和模拟器上做这件事是没有意义的。
学习x86作为你的第一种汇编语言是没问题的,如果你不去折腾分段,并且你不想写一个引导加载程序( A20门有很多神秘的遗留东西,并且从实模式切换到保护模式)。除了维护遗留代码库之外,DOS系统调用完全过时了。了解不同的AH=??/ int 21h
系统调用是如何工作的细节与COBOL语言一样有用。如果你正在创建一个传统的引导扇区(而不是int 10h
),那么BIOS和其他系列会稍微有用一些,但是你不需要这样做来学习asm。如果您在Linux、Windows、Mac、*BSD或其他平台上的用户空间中学习asm,那么以后如果您需要了解/学习其他与外部世界通信的方式,并了解内核如何工作,就会变得很容易。
Linux系统调用具有类似的ABI (eax=call number
/ int 0x80
、sysenter
或syscall
),但是Linux系统调用或多或少都是POSIX系统调用,了解它们对于实际的低级编程非常有用。
使用sys_read
的POSIX TTY行缓冲输入的复杂性不同于DOS字符读取函数和行结束废话的复杂性,但可以说学习起来更有用。
https://stackoverflow.com/questions/52140756
复制相似问题