我在为我的一个类所做的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 0x80sysentersyscall),但Linux系统调用或多或少是POSIX系统调用,了解这些调用对于实际的低级编程很有用。
POSIX TTY线缓冲输入与sys_read的复杂性不同于DOS字符读取功能和行结束无意义的复杂性,但可以说是更有用的学习。

关于c - 从汇编中使用getchar的gets()函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52140756/

10-13 08:20