我在为我的一个类所做的C代码上创建gets()
函数时遇到了一些问题所以我已经有了一个getchar()
函数,但是在程序集上,我使用extern
从C调用它,问题是在运行代码的时候,我输入了一个字符串,它没有显示完整的字符串,而是显示了一些字符。
这是我的自动取款机号码:
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解释器。
我可以用gets()函数来解决这个问题,以便正确地显示输入字符串吗?
例如,如果我输入“hello world”,它只打印“l ol”
最佳答案
不管asmgets
实现如何,您的Cgetchar
实现都已损坏。您可以使用桌面上的普通调试器在普通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应该非常相似。)
您也可以/改为检查
'\n'
,这取决于您的getchar实现,以及它是否规范化了行尾记住,如果您有DOS\r
行结尾,则在读取a\n
后停止将使a"\r\n"
未读。在ISO C中,
getchar()
应该只为以文本模式打开的文件提供'\n'
行尾,但是您已经使getchar
仅仅成为DOSint 21h
/ AH=1函数的包装器(使用ECHO从标准输入读取字符)所以这就是决定实现行为的因素。asm错误:
# in _getchar:
mov [bp + 4], al ; clobber memory you don't own.
这将在返回地址上方清除内存
char getchar(void)
不带任何参数,因此您的函数不“拥有”该内存编译器应该期望返回AL值(如果您认为这是通过引用返回的,则不,您只是覆盖了指针arg只是打电话的人连一个都没经过。)如果希望
getchar
能够返回与0xFF
字节不同的EOF,请在进行系统调用后将其声明为returnint
和zero AH(因此,可以在AX中返回16位-1
,或在AX中返回零扩展unsigned char
(即AL中的值)。顺便说一句,有一个原因
gets()
is deprecated,实际上在ISO C11中被删除了:当读取未知长度的输入时,不可能防止缓冲区溢出。您的函数应该将大小限制作为第二个参数。
直接编程Arduino的AVR或ARM CPU可能比在模拟的8086上使用DOS系统调用更容易学习,也更有用如果你要这么做的话,在真实的硬件和模拟器上做是没有意义的。
学习x86作为你的第一种汇编语言是可以的,如果你不搞分段,你不想写一个bootloader(有很多神秘的遗产与A20门,并从真正的模式切换到保护模式)DOS系统调用是完全过时的,除了维护传统的代码库学习细节有什么不同啊=??/
int 21h
系统调用的作用与COBOL差不多如果要创建传统引导扇区(而不是EFI),BIOSint 10h
和其他系列会稍微有用一些,但不需要这样做就可以学习asm。如果您在Linux、Windows、Mac、*BSD或其他环境下的用户空间中学习asm,那么以后,如果您需要,就可以很容易地理解/学习与外部世界通信的其他方式,并了解内核是如何工作的。Linux系统调用具有类似的ABI(
eax=call number
/int 0x80
,sysenter
或syscall
),但Linux系统调用或多或少是POSIX系统调用,了解这些调用对于实际的低级编程很有用。POSIX TTY线缓冲输入与
sys_read
的复杂性不同于DOS字符读取功能和行结束无意义的复杂性,但可以说是更有用的学习。关于c - 从汇编中使用getchar的gets()函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52140756/