c语言getchar gets,c - 从汇编中使用getchar的gets()函数 - 堆栈内存溢出

本文探讨了一种可能存在的C语言gets函数实现的bug,并提供了一个改进的版本。作者指出,原始实现可能会导致缓冲区溢出,建议在函数中加入长度限制参数以避免此类问题。同时,文章提到了不同平台的输入处理差异,如DOS系统调用与现代操作系统API的区别,并提醒读者注意处理行尾字符和EOF检查。此外,还讨论了在不同环境(如Arduino或8086模拟器)下编程的考量因素。
摘要由CSDN通过智能技术生成

无论asm getchar实现如何, 您的C gets实现 getchar 损坏 。 您可以使用台式机上的常规调试器在常规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

//#include

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

}

返回字符串长度而不是将其丢弃在已知函数中总是有用的,这只会使编译器花费几条额外的指令。 指针增量简化了寻址模式,节省了代码大小。

您还可以/代替检查'\\n'具体取决于您的getchar实现,以及它是否使行尾正常化。 请记住,如果您有DOS "\\r\\n"行尾,那么在读取\\r后停止将保留\\n未被读取。

在ISO C中,对于以文本模式打开的文件, getchar()应该只给您'\\n'行尾,但是您已经使getchar成为DOS int 21h / AH = 1的包装器(从标准输入中读取字符,带有回显)功能。 这就是设置实现行为的原因。

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位-1 ,或在AX中返回零扩展的unsigned char (即AL中的值)。

顺便说一句, 有一个原因不建议使用gets() ,而实际上已在ISO C11中删除了它:读取未知长度的输入时,不可能防止缓冲区溢出。

您的函数应将大小限制作为第二个参数。

与在仿真的8086上使用DOS系统调用相比, 直接对Arduino的AVR或ARM CPU进行编程可能比使用DOS系统调用更容易学习和更有用。如果要这样做,那么在真正的硬件与硬件上进行编程是没有意义的。模拟器。

如果您不熟悉分段操作,并且不尝试编写引导程序,则可以将x86作为您的第一门汇编语言学习(A20门有很多奥秘的传统东西,并且可以从实模式切换到保护模式) 。 DOS系统调用完全过时,除了维护旧版代码库。 学习细节如何不同AH =? / int 21h系统调用的工作原理与COBOL一样有用。 如果您要创建旧的引导扇区(而不是EFI),则BIOS int 10h和其他系列的功能会稍微有用一些,但是您不需要这样做就可以学习asm。 如果您在Linux,Windows,Mac,* BSD或任何其他版本的用户空间中学习asm,那么以后就可以轻松了解/学习与外界进行通信的其他方式(如果需要),并了解内核的工作方式。

Linux系统调用具有类似的ABI( eax=call number / int 0x80 , sysenter或syscall ),但是Linux系统调用或多或少是POSIX系统调用,这对于了解实际的低级编程很有用。

带有sys_read的POSIX TTY行缓冲输入的复杂性不同于DOS字符读取功能和行末尾的废话的复杂性,但是可以说对于学习更有用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值