【问题标题】:x86 Input a number in the base 2 and output it as a number in base 8x86 以 2 为底输入一个数字,并以 8 为底输出一个数字
【发布时间】:2018-05-29 14:38:00
【问题描述】:

要将基数为 10 的任何数字转换为基数 16,一个好的实现是:

segment data use32 class=data
    number dd 0
    format_input  db "%d", 0
    format_output db "%x", 0

segment code use32 class=code
start:
    push dword number
    push dword format_input
    call [scanf]
    add esp, 4*2

    push dword [number]
    push dword format_output
    call [printf]
    add esp, 4*2

是否有类似的实现将数字从基数 2 转换为基数 8?

【问题讨论】:

  • 这只是调用 C 库函数,而不是转换的“实现”。如果这对您来说已经足够了,那么对于八进制,有格式化程序 %o。对于二进制 .. 令人惊讶的是,我没有看到任何内容,因此您必须检查您的 scanf 文档,是否有一些文档,或者自己编写。但是,这是非常基本的东西,如果您了解数字系统背后的数学,您应该能够轻松地将任何基础转换为纸+笔上的任何基础。然后在汇编中编写这样的数学通常是微不足道的,特别是如果涉及两个基数的幂,你只需要重新排列位值。
  • 即以 2 为基数的输入每个输入元素有 1 个重要位(如果它是像“010101”这样的 ASCII 字符串,那么您可以从每个字符中删除除最低位之外的所有位,这就是您的二进制输入,ASCII '0'0011_0000 和 @ 987654328@ 是0011_0001,因此此类字符串输入的最后一位将是 0, 1, 0, 1, 0, 1 ...) .. 八进制输出需要每个数字 3 位,因此您只需将其分组进入010101 并输出这两个值,即“25”。 ...不需要除法/乘法(与基 10 格式相反,会发生某种乘法/除法)。 0b010101 == 0o25
  • @Ped7g: ISO C scanf/printf don't have base 2 conversion specifiers,它不是标准扩展,not even in glibc。不过,对于输入,strtol 将基数作为参数。
  • 您好,欢迎您!我想说明为什么这个问题没有得到很好的接受。理想的 SO 问题会隔离一个问题并提出一个(可能)具有客观最佳答案的问题。从您的问题来看,目前尚不清楚问题是一般的基本转换算法、使用 C 标准库的序列化和反序列化,还是编写 x86 程序集。此外,每个主题都单独进行了广泛的介绍,因此我们得到的印象是您希望我们为您编写代码。请不要气馁!我们都必须在某个时候学习这里的文化。

标签: c assembly x86 nasm standard-library


【解决方案1】:

这是essentially a duplicate of this C/C++ question,因为您正在询问如何通过调用 C 库函数来实现。没关系,没有理由你不能在 asm 中做到这一点。我决定不只是以重复的形式关闭,因为关于如何正确执行此操作的内容已经足够多,只是评论有点长。

C 语法是一种更简单的方式来表达你需要将哪些函数粘合在一起,所以我将基本上用 C 来回答。如果你想在 asm 中使用循环执行任何步骤,那就去做吧。将二进制字符串转换为整数非常容易,您只需一次移动一个数字。 (或使用 SSE2 pmovmskb)。


使用 printf %o 转换以输出基数 8; ISO C 直接支持。

Base 2 isn't, though。但是strtoul 使用您选择的基数转换字符串-> 无符号整数。剩下的唯一问题是将输入作为字符串读取。有很多方法可以做到这一点,但是如果您想避免过度阅读和消耗不属于 base-2 数字的字符,您可以使用 scanf 和仅接受字符的 %[] 转换0 和 1。

所以你可以编写如下所示的 asm:

// optionally this buffer could be static like your `number`, but there's no reason to do that.
char buf[65];       // reserve 65 bytes on the stack with sub esp, 80  (rounded up to keep stack 16B-aligned)

buf[0] = 0;     // NULL terminate our stack buffer to avoid crashing in strtoul if scanf doesn't convert anything.
int success = scanf(" %64[01]", buf);  // skip optional whitespace and store up to 64 bytes + null terminator into buf
// optional: check the conversion.

unsigned long num = strtoul(buf, NULL, 2);   // parse as base 2, returning a binary integer

printf("%lo\n", num);          // print as octal 
// or %o if int and long are the same width in 32-bit asm

所有这些 C 语句都可以用一小段 asm 指令实现,而不是循环。如果您不确定如何操作,ask a compiler with gcc -O2 -S -masm=intel

注意scanf 格式字符串中的长度限制,因此如果用户按住某个键并且您的程序读取了 1000 个字节的1,则不会出现缓冲区溢出。

还要注意格式字符串中的前导空格,以跳过stdin 缓冲区中当前的任何空格。 (例如,上一行末尾的换行符,scanf 通常不会使用)。 Conversions like %c and %[ don't skip leading whitespace on their own.

如果您希望它使用尾随的非数字字符,请使用 %64sstrtoul 将在第一个非数字字符处停止。 (如果您将非 NULL char ** 作为第二个参数传递,则存储指向该位置的指针。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-14
    • 2018-05-23
    • 2019-04-11
    • 1970-01-01
    • 2018-08-19
    • 1970-01-01
    • 2016-07-14
    相关资源
    最近更新 更多