assembly 在组件x86中使用节点

oewdyzsn  于 2022-12-13  发布在  其他
关注(0)|答案(1)|浏览(179)

我试着理解这段代码的作用:

.section .text
.section .data
A:   .long 3
     .quad B
     .quad C
     .quad D
     .quad 0
B:   .long 10
     .quad E
     .quad F
     .quad 0
C:   .long 22
     .quad 0
D:   .long 17
     .quad G
     .quad 0
E:   .long 85
     .quad 0
F:   .long 2
     .quad 0
G:   .long 8
     .quad 0
.global _start
_start:
    mov $17, %esi
    mov $A, %rdi
    call func
    movq $1, %rdi
    sub %rax, %rdi
    movq $60, %rax
    syscall

func:
    push %rbp
    movq %rsp, %rbp
    cmpl %esi, (%rdi)
    jne next
    mov $1, %rax
    jmp end

next:
    mov $0, %r10

test:
    cmpq $0, 4(%rdi, %r10, 8)
    je fail
    push %rdi
    push %r10
    mov 4(%rdi, %r10, 8), %rdi
    call func
    pop %r10
    pop %rdi
    cmpq $0, %rax
    jne finish
    inc %r10
    jmp test

finish:
mov $1, %rax
jmp end

fail:
     mov $0, %rax
end:
     leave
     ret

据我所知,它试图确定“17”是否出现在数据部分。(使用X),但看起来这取决于测试部分。我们是从A到B,然后E,F,再回到C,最后是D?最后的值是多少?我试着把它“翻译”成C,到目前为止我有:

int func(Node* root, int X){
    if(root->data == null)
        return 0;
    Node *son == root->sons;
    while(son != null){
        if(son->data == 17)
            return 1;
        son += 1;
    }
    return 0;
}

它看起来正确吗?

wmomyfyw

wmomyfyw1#

继续在C版本上工作,因为在将算法转换为汇编代码之前知道你想让程序做什么确实很有帮助。
正如Jester所指出的,C语言版本永远不会搜索E、F或G,因为它只在A之后的一个级别上进行搜索,A枚举了B、C、D --因此,这些 * 将 * 被搜索,但E、F、G * 不会 *。
在您的尝试中,您已写入一行:

if(root->data == null)
        return 0;

这是一个类型不匹配,因为data是一个int,即您要搜索的int,而作为一个int,它不能接受值null,所以可能应该跳过此条件测试。
您已经声明了Node *son,并且在son++中递增。这也是一个类型不匹配,因为root->sons是一个内联数组,并且它的元素是节点指针,所以数组实际上是指向子指针的指针。
对于您的代码,son++将以sizeof(Node)递增,这是完全错误的,因为这些是密集封装的指针,而不是节点。
您希望它是Node **sons = root->sons;-这样sons++将以sizeof(Node*)递增,这正是您想要的。
另外,因为你使用了Node*son = root->sons,所以在获取那些子指针的元素时缺少了一个解引用,当使用Node **sons = root->sons;时,你可以看到它被寻址了--我们可以使用Node *son = *sons;来完成这个解引用。
递归算法在这里是有帮助的和适当的,因为被搜索的树是递归数据结构,所以也是相当自然的。

#define null 0

typedef struct Node {
    int data;
    struct Node* children[1];
} Node;

int func(Node* root, int X){
    if (root->data == X) return 1;

    // this variable holds state to continue search
    //  when the recursive call returns not found
    Node **children = root->children; 

    while (1) {
        Node *child = *children;
        if (child == null) break;

        // check not just this child, but the whole branch starting from this child, using recursion
        int answer = func(child, X);

        if (answer) return 1; // found in that branch, so we're done

        children++;           // not found in that branch, so try next child from here
    }
    return 0; // not found in this branch
}

上面的算法检查“root”以查看它是否匹配,如果不匹配,则继续在每个子节点上递归地运行该函数,该函数执行相同的检查并覆盖所有子节点,包括子节点的子节点。
然而,迭代解决方案可以有效地工作,尽管在算法的持续时间内需要使用一个临时的数据结构,如堆栈或列表--这是为了记录当给定的分支没有找到匹配时“回溯”到哪里。

int func(Node* root, int X) {

    // this temporary variable should be heap allocated and expandable
    // but that is beyond the scope of this answer.  
    // (Using C++ would allow generic list, but also beyond scope)

    Node *searchList[100];  // used as stack

    searchList[0] = root; // push root to prime this stack
    int ix = 1;

    while (ix != 0) {  // while stack not empty

        ix--;
        Node *node = searchList[ix];  // pop

        if (node->data == X) return 1;

        Node **children = node->children;
        while (1) {
            Node *child = *children;
            if (child == null) break;

            searchList[ix] = child; // push child
            ix++;

            children++;
        }
    }
    // exhausted the search list
    return 0;    
}

这个版本使用一个临时数组作为堆栈来记录需要检查的节点。然后遍历堆栈直到空,查看从堆栈中出来的节点是否匹配,同时也将子节点放在堆栈中。因此,它也会搜索子节点的子节点。
(Some程序员可以在将节点压入列表之前执行->data == X检查,尽管这需要在压入的两个位置都执行检查。)
(In另一个变体,我们可以推送Node **而不是Node*。)
(It在C语言中更复杂,这是因为C语言对List等泛型数据类型的处理不佳,并且缺乏我们期望C++或C#等语言能够提供的自动扩展)。

相关问题