【问题标题】:unlimited precision NASM calculator using linked lists使用链表的无限精度 NASM 计算器
【发布时间】:2020-09-09 22:06:58
【问题描述】:

作为我的项目的一部分,这是一个无限精度的 RPN 计算器, 我正在尝试编写一种方法来接收大小最多为 80 字节的缓冲区。由于我想支持无限精度(或至少受堆大小的限制),我想获取该缓冲区并创建一个指向表示数字的链表头部的指针,例如,如果里面的输入缓冲区是:0x7D12AF

然后由该输入生成的链表将如下所示:

[AF, addr1] -addr1->[12, addr2] -addr2-> [7D, addr3] -addr3-> 0

其中每个链接是 5 个字节,4 个用于指向下一个链接的指针,一个字节用于数据。 这是我的想法,我会接受任何建议,因为我真的不确定我在做什么: (假设 atoi 接受一个十六进制数字并将其转换为数值)

`section .bss
        ptr: resb 5
 section .data

setcion .text
align 16
extern malloc

_buffersize
    push ebp
    mov ebp, esp
    mov ecx, [ebp+8]


_buffersize:
    push ebp
    mov ebp, esp
    push ecx
    mov ecx, [ebp+8]
    xor eax, eax

    .loop:
        cmp [ecx], 20h
        jle .done
        inc ecx
        inc eax
        jmp .loop

    .done:
        pop ecx
        mov esp, ebp
        pop ebp
        ret

_listify:
    push ebp
    mov ebp, esp

    mov edx, [ebp+8]                ; pointer to the first byte in the number_string
    pushad

    push edx                        ; push function argument
    call _buffersize                ; eax now holds the size of the buffer 
    add esp, 4                      ; clean up stack after call


    mov ecx, eax                    ; count for the loop

    .loop:
        pushad                      ; allocate 5 bytes for a node : 4 for a next ptr, 1 for data
        push 5
        call malloc                 ; eax now points to the 5 bytes allocated
        add esp, 4                  ; clean up stack after call to malloc
        mov [ptr], eax              ; now ptr points to the address in memory of the 5 allocated bytes 
        popad
        push [edx]                  ; push the first byte pointed to by edx as an argument for atoi (atoi converts a signle HEX digit to it's numeric value)
        call _atoi
        add esp, 4                  ; eax now holds the numeric value of that 1 byte character
        mov ebx, [ptr]              ; ebx points to the allocated memory
        mov [ebx], dword 0          ; the address of the next link is NULL as we're insterting at the head of the lsit
        mov [ebx], byte eax         ; hopefully, ebx should now points to 5 bytes in memory of the form [b4b3b2b1b0] where b4b3b2b1 is the address of the next link & b0 is a 0 <= number <16
        mov [ptr], ebx              ; now ptr points to the address of the newly updated linked list representing the number
        inc edx                     ; get ready to read next byte
       loop .loop  

    popad
    mov esp, ebp
    pop ebp
    ret
`

我还有一个问题是:有没有办法以十六进制表示形式存储数字?我认为这是一个愚蠢的问题,因为表示只是我看待它的方式,但值是相同的..所以将十六进制数字 ASCII 表示转换为 int 是一样的,并且要制作十六进制我应该对待它从 char 转换为 int 时的这种方式,反之亦然。如果我错了,请纠正我。 谢谢!

【问题讨论】:

    标签: assembly x86 nasm bigint rpn


    【解决方案1】:

    4 为指向下一个链接的指针,一个字节为数据

    因此,您建议的格式仅将 20% 的空间用于实际数据。实际上比这要少得多,因为malloc 有内部开销,并且每个分配将至少对齐 8 个字节,可能是 16 个。所以你浪费了至少 7/8 或 15/16 的内存/缓存占用空间,等等当您包括 malloc 开销时。

    请参阅this 了解更多关于为什么它很糟糕以及你应该做什么,以及添加每个节点 1 个十六进制数字(4 位)的链表的实现,而不是你建议的 8 位(2 个十六进制数字) .

    使用数组,如果需要扩展,请使用realloc。这使您可以添加 32 或 64 位块(在 64 位模式下)。如果您愿意,可以通过像 C++ std::vector 那样分配额外空间来节省 realloc 调用,分别跟踪已分配和正在使用的空间。

    数组更容易循环,效率更高。


    有没有办法以十六进制表示形式存储数字?我认为这是一个愚蠢的问题,因为表示只是我看待它的方式,但价值是相同的

    ASCII hex 是数字的一种序列化格式;它每 8 位(2 个半字节)数据使用两个 ASCII 字节。请参阅 How to convert a binary integer number to a hex string? 了解如何将二进制整数 转换为 十六进制字符串。

    相反,将十六进制转换为寄存器中的二进制整数,您可以将数字转换为total = (total&lt;&lt;4) | digit。其中digit 是 0..15 范围内的整数。给定一个 ASCII 字符,您可以减去 '0' 并在结果大于 9 时进行分支,如果是这样,则减去 'A'

    对于任意长度的十六进制输入,您可以从缓冲区的末尾开始,将 2 个十六进制数字转换为一个字节,将其存储在缓冲区中并递减指针。

    (如果输入最终是奇数个十六进制数字,这是一个问题,因为您希望数字的开头与字节边界对齐。因此,如果您知道十六进制字符串的长度,请使用来决定是否从自己转换第一个数字开始。或者如果你有一个指向末尾的指针,你可以向后读取数字。

    首选显式长度的字符串/缓冲区来处理 ASCII 数字,因此您首先知道您有多少位,而不必循环查找 0 字节作为 C 隐式长度字符串的终止符.)

    【讨论】:

    • 这是对第一个问题的评论(作为评论发布太长),回答第二个问题(关于十六进制与数字的概念性问题)。这就是 SO 不鼓励在 1 中发布 2 个问题的原因之一。:/
    • 如果我改用数组,并说缓冲区大小以 80 为界,因此表示数字的数组永远不会超过 80 个字节(对吗?),我可以为每个字节静态分配空间编号并从每个堆栈条目中都有一个指向它的指针。
    • @sadElephent:你也可以静态分配一个链表;这种选择是正交的。但是,是的,如果您要使用固定上限进行静态分配,那么链表就更没有意义了。
    • 所以我实际上采纳了您的建议并决定使用数组,但在某种程度上,数组中的每个字节代表 1 位,我使用 malloc 动态分配它,所以基本上我的堆栈是一堆数组每个 log_16(number) 位的指针。我希望能够对这些数组表示的两个数字(反向)执行按位 OR 和 AND,例如:[1,A] | [2,B] 所以数字实际上是 1010 0001 | 1011 0010 = 10110011。我该怎么做?
    • @sadElephent:好的,所以您决定每个字节存储 1 个半字节,而不是将您的值打包成连续的位。奇怪的选择,但还可以。按位 AND 和 OR 的工作方式与它们对整个字节的工作方式相同。填充与填充对齐,值与值对齐。作为奖励,您一次可以处理超过 1 个字节,因为不需要进位。因此,您可以使用 dword or 一次读取 4 个字节,或者使用 SSE2 por 一次读取 16 个字节。即,只需将一个数组中的每个字节 OR 到另一个数组的相应字节中,一次任意多个字节。
    猜你喜欢
    • 1970-01-01
    • 2012-12-28
    • 2016-10-13
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多