汇编实现二叉树

今天翻找以前的代码, 突然发现, 以前曾经用汇编写的二叉树, 以前对汇编过于执着. 是好事, 也是坏事. 个人觉得, 用MASM写代码其实和C语言的差别是非常小的, 但是毕竟是不同的语言, 就和C和C++的差别一样, C++在C的基础之上多了一点东西, 多的这点东西带来的是思想上的转变, 汇编也是一样, 用汇编写的代码, 我觉得带来的其实也是一种思想. 一种用汇编想问题的思想.

有个朋友C++学的很不错, 但是他始终对代码编译出的二进制模型比较模糊, 当然如果使用汇编, 长期养成的习惯就不会有这个问题了. 当然这个汇编不只是32位了.. 16位现在虽然已经过时了. 但是不可否认从16位开始学起, 会更加了解什么是代码, 什么是数据. 目前的masm的版本是11了. 跟随着vs2012发布, 很多人写16位的代码还是用masm5. 究其原因就是16位的代码都用masm5, 32位都用masm6xx以上, 其实这种观点是不对的, 包括最新的masm11在内, 都是可以写16位的代码的, 我要特别提到这点, 空间里面有很多的保护模式下面的文章, 我都是用masm9写的.. 哪个说不能写16位代码??

使用汇编是其中一个, 另外一个为了组件自己的开发环境, 没有使用masm32包, 我自己搞了个大杂烩 我当时使用了vs2010 ml nmak rc link等工具, 外加cl. 然后把Windows SDK的头文件, CRT头文件, MFC头文件, 库文件打包到一起, 开发包不到50M.. 写C/C++ MFC. 都够了.

虽然现在基本不怎么用汇编写代码了. 但是感谢曾经有那么一段执着的日子. 下面这段二叉树操作的代码摘抄自曾经写的一个<<学员管理系统>> 可以看到里面调用了很多类C的函数, 比如cprintf. 这个函数在MASM32里面是真实存在的, 只是名字是_imp___cprintf. 还有MASM32的作者对msvcrt.lib进行了改动, 他改动的这个msvcrt.lib可以用在汇编上, 另外剔除了C++ 使用的一些库函数, 只保留了C的一些函数, 比如printf, scanf啊一类的.. 而且这个库有个优点, 不用初始化, C/C++里面带那个msvcrt.lib必须给初始化一下非常恶心.. so 汇编也可以调用C的函数和系统提供的api..

二叉树的话, 也是增, 删, 改, 查. 下面基本都有了. 比较难的是删除. 删除考虑的事情有点多.. 另外二叉树也是不平衡的, 最坏的情况就变链表了.. 所以二叉树还应该做平衡, 有时间再看看AVL树..

PATH_ASSEMBLY   equ 1
    Include Common.inc
NameStudent struct
    lpStudent   dword   ?   ;当前学员地址
    dwNumber    dword   ?   ;当前学员编号
    lpLeft      dword   ?   ;左节点指针
    lpRight     dword   ?   ;右节点指针
    lpBefor     dword   ?   ;如果有重复的话, 前一个名称地址
    lpNext      dword   ?   ;如果有重复的话, 下一个名称地址
    SzName      byte  32 dup(?) ;学员名字   
NameStudent     ends
NumberStudent   struct
    lpStudent   dword   ?   ;当前学员地址
    dwNumber    dword   ?   ;学员编号
    lpLeft      dword   ?   ;左节点指针
    lpRight     dword   ?   ;右节点指针  
NumberStudent   ends
        .data?
SzIniFile   byte    MAX_PATH dup(?) ;ini文件名称
dwCount     dword       ?   ;遍历计数器
        .code
;============================================================================
;遍历学员信息
;_lpStProInfo:程序信息头
;_lpStNumberHead:编号树头节点
;返回true表示遍历成功, 返回false表示没有节点给遍历
_IterateStudentNode proc public  _lpStProInfo:dword, _lpStNumberHead:dword
     
    assume  ebx:ptr NumberStudent
    assume  esi:ptr Student
    assume  edi:ptr Score
    assume  edx:ptr Course
     
    mov ebx, _lpStNumberHead
     
    .if ebx != null
;============================================================================
        ;先从左边开始遍历, 如果左边有那么就会一直追溯进去, 在堆栈中
        ;很明显就会形成大的在上面, 小的在下面, 然后在左边遍历完了以后
        ;开始返回, 返回一个打印一个, 达到了遍历的目的
;============================================================================       
        Invoke  _IterateStudentNode,_lpStProInfo, [ebx].lpLeft
         
        mov ebx, _lpStNumberHead
        mov esi, [ebx].lpStudent
        Invoke  cprintf, cfm$( "============================================================================\n" )
        Invoke  cprintf, cfm$( "学员编号:    %d\n学员名字:    %s\n课程总数:    %d\n" ), \
            [esi].dwNumber, addr [esi].szName, [esi].dwScoreLen
         
        lea edi, [esi].StScore
        mov ecx, [esi].dwScoreLen
         
    @@: 
;============================================================================
        ;对于右边就先打印了才去遍历, 因为右边是从小到大, 总之这样就
        ;完成了遍历
;============================================================================
        push    ecx
        Invoke  _FindCourseNode, _lpStProInfo, [edi].dwCourse ;去查找该课程的字符串表示
        mov edx, eax
         
        .if !edx
            Invoke  cprintf, cfm$( "课程编号:    %d(无效课程, 请尽快修改)\n课程分数:    %d\n" ),\
                 [edi].dwCourse,  [edi].dwScore
        .else
            Invoke  cprintf, cfm$( "课程编号:    %d\n课程名称:    %s\n任课老师:    %s\n课程分数:    %d\n" ),\
                 [edi].dwCourse, addr [edx].szName, addr [edx].szTeach, [edi].dwScore
        .endif
        add edi, 8
        pop ecx
        loop    @b
         
        inc dwCount
;============================================================================
        ;如果用户输入了想退出, 因为这里是链表, 也许会有很多层, 很难跳
        ;我这里就在外面挂了一个异常, 如果想跳出去, 直接触发一样..
        ;外面捕获自己就出去了
;============================================================================   
        .if dwCount == 20
            push    0
            pop dwCount
             
            Invoke  Jpause
            .if !eax
                xor eax, eax
                mov dword ptr [eax], 0
            .endif
        .endif
        Invoke  _IterateStudentNode,_lpStProInfo, [ebx].lpRight
    .endif
_ret:
    ret
_IterateStudentNode endp
;============================================================================
;以编号查询一个学生
;_lpStNumberHead:编号树头
;_dwNumber:要查找的编号
;找到返回该节点指针, 没有找到返回NULL
_FindNumberStudnet  proc    public _lpStNumberHead:dword, _dwNumber:dword
     
    assume  ebx:ptr NumberStudent
    mov ebx, _lpStNumberHead
     
    .if ebx != null
         
        mov eax, [ebx].dwNumber 
        .if eax < _dwNumber      ;大于向右
            mov ebx, dword ptr [ebx].lpRight    ;这里已经是二级指针了, 不用转了
             
            Invoke  _FindNumberStudnet, ebx, _dwNumber
             
        .elseif eax > _dwNumber      ;小于向左
            mov ebx, dword ptr [ebx].lpLeft ;这里已经是二级指针了, 不用转了
             
            Invoke  _FindNumberStudnet,ebx, _dwNumber
             
        .elseif eax == _dwNumber    ;等于返回
            mov eax, [ebx].lpStudent
        .endif
    .else
        xor eax, eax    
    .endif
_ret:
    ret
_FindNumberStudnet  endp
;============================================================================
;以名称查询一个学生
;_lpStNumberHead:编号树头
;_lpSzName:要查找的名字
;找到返回树中节点的指针, 在外面自己要重新做解析, 没有找到返回NULL
_FindNameStudnet proc   public _lpStNameHead:dword, _lpSzName:dword
;============================================================================
     
    assume  ebx:ptr NameStudent
    mov ebx, _lpStNameHead
 
    .if ebx != null
         
        lea ecx, [ebx].SzName
        Invoke  lstrcmp, ecx, _lpSzName 
         
        cmp eax, 0  ;.if    eax > 0  
        jg  _else2  
        cmp eax, 0  ;.elseif eax < 0 
        jl  _else1
        jmp _else3  ;.elseif eax == 0
;============================================================================
_else1:     
        mov ebx, dword ptr [ebx].lpRight ;大的到右边
        Invoke  _FindNameStudnet,ebx, _lpSzName
        jmp _ret
_else2:         
        mov ebx, dword ptr [ebx].lpLeft ;小的到左边
        Invoke  _FindNameStudnet, ebx, _lpSzName
        jmp _ret
_else3:
;============================================================================
    ;注意, 这里返回的指针, 和其他函数有点不一样.由于这里需要返回多个同名
    ;的结构指针, 所以直接把内部的链表结构指针返回了
;============================================================================
        mov eax, ebx           
    .else
        xor eax, eax
    .endif
_ret:
    ret
_FindNameStudnet    endp
 
;============================================================================
    .data?
lpFather    dword   ?
    .code
;============================================================================
;查找某个需要删除的值的位置, 并保存其父节点
;============================================================================
_FindNumberNode proc  private uses ebx   _lpCurHead:dword, _lpHead:dword, _dwValue:dword
;==============================================================
 
    assume  ebx:ptr NumberStudent
 
    mov ebx, _lpHead
    ;mov    ebx, dword ptr [ebx]
     
    .if ebx != NULL 
        mov eax, [ebx].dwNumber ;学员编号
         
        .if eax > _dwValue       ;从左边查找
            Invoke  _FindNumberNode, ebx,  [ebx].lpLeft, _dwValue
        .elseif eax < _dwValue       ;从右边查找
            Invoke  _FindNumberNode, ebx,  [ebx].lpRight, _dwValue
        .else
            push    _lpCurHead  ;将父节点保存起来
            pop lpFather
                 
            mov eax, ebx    ;将当前节点返回
        .endif
    .else
        xor eax, eax
    .Endif
    ret
_FindNumberNode Endp
;============================================================================
;在一个树中, 从右边查找比头节点只大一号的值, 这个值一定是从该树的右子树, 一直往
;左,  直到左边的那个节点的左子树为NULL为止..
;lpRightFather:这个值的作用是, 如果右边找到的最小值如果其右子树还有值, 那么就要
;把他们挂上来...
_FindNumberRightMin proc    private _lpHead:dword
     
    assume  ebx:ptr NumberStudent
    mov ebx, _lpHead
     
    .if [ebx].lpLeft != NULL            ;如果右子树不为NULL
        Invoke  _FindNumberRightMin, [ebx].lpLeft;那就直到查找到为NULL为止
    .else
        mov eax, ebx            ;遍历到了最右边的返回就可以了
    .endif
     
    ret
_FindNumberRightMin     endp
;============================================================================
;删除一个学员节点(同时删除在编号树中的节点)
;_lpStProInfo:程序信息头
;_dwNumber:要删除的编号
_DelNumberStudentNodo   proc    public _lpStProInfo:dword, _dwNumber:dword
;============================================================================
    local   _lpDelNode  :dword  ;需要删除的节点
    local   _lpWrieteMem    :dword  ;要写入内存中的值
     
     
     
    assume  ebx:ptr NumberStudent
    assume  esi:ptr NumberStudent
    push    -1
    pop _lpWrieteMem
    push    NULL
    pop lpFather
;============================================================================
    mov eax, _lpStProInfo
    mov eax, [eax+ProInfo.lpNumberStudent]
    Invoke  _FindNumberNode, NULL, eax, _dwNumber   ;查找某个需要删除的值
    mov _lpDelNode, eax
     
     
    mov ebx, _lpDelNode
     
    ;如果左子树和右子树都为NULL删除就比较容易了
    .if [ebx].lpLeft == NULL && [ebx].lpRight == NULL
         
        ;if要删除的父节点为NULL(说明整个程序只有一个节点)
        .if lpFather == NULL
            mov _lpWrieteMem, NULL  ;;写入NULL到_lpWrieteMem
             
        ;else要删除的父节点不为NULL判断该节点是左还是右, 置为NULL就可以了
        .else
            mov ebx, lpFather
            mov eax, _lpDelNode
            .if [ebx].lpLeft == eax     ;如果是左边
                mov [ebx].lpLeft, NULL  ;左边置NULL
            .else
                mov [ebx].lpRight, NULL ;右边置为NULL
            .endif
        .endif
             
    ;如果左子树和右子树都不为NULL
    .elseif [ebx].lpLeft != NULL && [ebx].lpRight != NULL
         
        ;在右子树中查找最小的一个值的指针
        Invoke  _FindNumberRightMin, [ebx].lpRight
        .if !eax
            jmp _ret
        .endif
         
        ;将左子树挂到右子树中最小的那个节点的左子树中
        mov ebx, _lpDelNode
        push    [ebx].lpLeft
         
        mov ebx, eax
        pop [ebx].lpLeft
         
        ;if要删除的父节点为NULL(说明要删除的是头节点
        .if lpFather == NULL
            mov ebx, _lpDelNode
            push    [ebx].lpRight   ;将要删除的右子树作为新的头节点
            pop _lpWrieteMem
             
        ;否则需要判断是需要删除的节点是左子树还是右子树
        .else
            mov ebx, lpFather
            mov eax, _lpDelNode
             
            mov esi, _lpDelNode
            push    [esi].lpRight
            .if [ebx].lpLeft == eax
                pop [ebx].lpLeft    ;如果要删除的节点原来在左边就挂左边
            .else
                pop [ebx].lpRight   ;如果要删除的节点原来在右边就挂右边
            .endif
        .endif
     
    ;如果只有左边为NULL
    .elseif [ebx].lpLeft == NULL
         
        ;if如果要删除的父节点为NULL
        .if lpFather == NULL
            push    [ebx].lpRight   ;右节点成为新的头节点
            pop _lpWrieteMem    ;写入_lpWrieteMem中
             
        ;如果要删除的父节点不为NULL
        .else
            push    [ebx].lpRight   ;要删除节点的右子树
            mov ebx, _lpDelNode 
             
            mov esi, lpFather   ;将右边挂到父节点下面
            .if [esi].lpLeft == ebx
                pop [esi].lpLeft ;左边相等挂左边
            .else
                pop [esi].lpRight;右边相等挂右边
            .endif
             
        .endif
                 
    ;如果只有右子树为NULL
    .elseif [ebx].lpRight == NULL
         
        ;if如果要删除的父节点为NULL
        .if lpFather == NULL
            push    [ebx].lpRight   ;左节点成为新的头节点
            pop _lpWrieteMem    ;写入_lpWrieteMem中
         
        ;如果要删除的节点不为NULL
        .else
            push    [ebx].lpLeft
            mov esi, lpFather   ;被删除值的左子树
             
            .if [esi].lpLeft == ebx
                pop [esi].lpLeft    ;将左边挂到父节点下面
            .else
                pop [esi].lpRight   ;如果右边相等就挂右边
            .endif
             
        .endif
    .endif
     
    ;.if    _lpWrieteMem != -1
    .if _lpWrieteMem != -1          ;如果不为NULL就写入ini文件, 和内存中
         
        mov ebx, eax
        Invoke  JWritePrivateProfileInt,\   ;写入注册表中
            offset SzSection, offset SzKeylpNumStu, _lpWrieteMem , offset SzIniFile 
        .if !eax
            jmp _ret
        .endif      
     
        mov esi, _lpStProInfo       ;更新文件中的结构
        push    _lpWrieteMem
        pop [esi+ProInfo.lpNumberStudent]
         
    .endif
     
    ;释放内存
    mov ebx, _lpDelNode
    Invoke  Jfree, [ebx].lpStudent
    Invoke  Jfree, ebx
_ret:
    ret
_DelNumberStudentNodo   endp
;============================================================================
;插入一个节点到编号树中(是个递归函数)
;_lpStNumberHead:头节点指针, 二级指针
;_lpNewStudent:新学员指针
;_dwNumber:编号
;成功返回生成的头指针, 否则返回false
_InsertNumberTree   proc    private    _lpStNumberHead:dword, _lpNewStudent:dword, _dwNumber:dword
;============================================================================
     
    assume  ebx:ptr NumberStudent
    assume  esi:ptr NumberStudent
;============================================================================
    ;如果树目前不是空的, 那么就递归调用, 直到空为止
;============================================================================
    mov ebx, _lpStNumberHead    
    mov eax, dword ptr [ebx]    ;二级指针
     
    .if eax
        mov eax, _dwNumber
        ;这句非常重要, 因为这里是3级指针, 比上面的又多加了一层..
        ;昨天调试了一晚上就是这个地方
        mov ebx, dword ptr [ebx]            
     
        .if [ebx].dwNumber <= eax            ;大的到右边      
            lea ecx, [ebx].lpRight
            Invoke  _InsertNumberTree, ecx, _lpNewStudent, _dwNumber
        .else                       ;小的到左边
            lea ecx, [ebx].lpLeft
            Invoke  _InsertNumberTree,ecx , _lpNewStudent, _dwNumber
        .endif  
    .else
;============================================================================
;   生成一个新的树节点
;============================================================================       
        Invoke  Jmalloc, sizeof NumberStudent
        .if !eax
            jmp _ret
        .endif
         
        xchg    esi, eax
        push    _lpNewStudent           ;新的头节点
        pop [esi].lpStudent
     
        push    null
        pop [esi].lpLeft            ;左子树为NULL
        push    null
        pop [esi].lpRight           ;右子树为NULL
         
        push    _dwNumber
        pop [esi].dwNumber          ;编号
         
        mov dword ptr [ebx], esi        ;返回该地址指针
        mov eax, esi
    .endif
;============================================================================       
_ret:
    ret
_InsertNumberTree endp
;============================================================================
;插入一个节点到名称树中(是个递归函数)
;_lpStNumberHead:头节点指针, 二级指针
;_lpNewStudent:新学员指针
;_lpSzName:名字
;成功返回生成的头指针, 否则返回false
_InsertNameTree  proc   private    _lpStNumberHead:dword, _lpNewStudent:dword, _lpSzName:dword
     
    assume  ebx:ptr NameStudent
    assume  esi:ptr NameStudent
     
;============================================================================
    ;如果树目前不是空的, 那么就递归调用, 直到空为止
;============================================================================
    mov ebx, _lpStNumberHead    
    mov eax, dword ptr [ebx]    ;二级指针
 
    .if eax
        mov eax, _lpSzName
        ;这句非常重要, 因为这里是3级指针, 比上面的又多加了一层..
        ;昨天调试了一晚上就是这个地方
        mov ebx, dword ptr [ebx]            
         
        lea ecx, [ebx].SzName
        Invoke  lstrcmp, ecx, eax
         
        cmp eax, 0  ;.if    eax > 0  
        jg  _else1  
        cmp eax, 0  ;.elseif eax < 0 
        jl  _else2
        jmp _else3  ;.elseif eax == 0
;============================================================================
_else1:     
        lea ecx, [ebx].lpLeft   ;小的到左边
        Invoke  _InsertNameTree,ecx , _lpNewStudent, _lpSzName
        jmp _ret
_else2:     
        lea ecx, [ebx].lpRight  ;大的到右边  
        Invoke  _InsertNameTree, ecx, _lpNewStudent, _lpSzName
        jmp _ret    
_else3:
;============================================================================
;       插入同名的学员, 远比我们想象的复杂
;============================================================================
        Invoke  Jmalloc, sizeof NameStudent
        .if !eax
            jmp _ret
        .endif
        xchg    esi, eax
        push    _lpNewStudent           ;新的头节点
        pop [esi].lpStudent
     
        push    null
        pop [esi].lpLeft            ;左子树为NULL
        push    null
        pop [esi].lpRight           ;右子树为NULL
        push    NULL
        pop [esi].lpNext            ;下一个同名节点为NULL
         
        mov eax, _lpNewStudent
        mov eax, [eax+Student.dwNumber] ;填写学员编号
        push    eax
        pop [esi].dwNumber
         
        Invoke  RtlMoveMemory, addr [esi].SzName, _lpSzName, sizeof Student.szName 
;============================================================================
        ;这里也是由原来的单向链表我修改成了双向的, 不过还是在链表末尾添加
;============================================================================
        .if [ebx].lpNext == NULL
            mov [ebx].lpNext, esi   ;将同名的学员链在学员后面
            push    ebx
            pop [esi].lpBefor       ;之前一个就置为ebx, 这样就双向了
        .else
        @@: mov ebx, [ebx].lpNext
            or  [ebx].lpNext, 0     ;在同名学员链表最后添加
            jnz @b
             
            push    esi         ;天下下一个
            pop [ebx].lpNext
             
            push    ebx         ;之前的一个就是保存的前一个节点了
            pop [esi].lpBefor
        .endif  
        mov eax, esi            ;返回该地址指针
;============================================================================       
     
    .else
;============================================================================
;   生成一个新的树节点
;============================================================================       
        Invoke  Jmalloc, sizeof NameStudent
        .if !eax
            jmp _ret
        .endif
         
        xchg    esi, eax
        push    _lpNewStudent           ;新的头节点
        pop [esi].lpStudent
     
        push    null
        pop [esi].lpLeft            ;左子树为NULL
        push    null
        pop [esi].lpRight           ;右子树为NULL
        push    NULL
        pop [esi].lpNext            ;下一个同名节点为NULL
        push    NULL
        pop [esi].lpBefor           ;前一个同名节点为NULL
         
         
        mov eax, _lpNewStudent
        mov eax, [eax+Student.dwNumber] ;填写学员编号
        push    eax
        pop [esi].dwNumber
         
        Invoke  RtlMoveMemory, addr [esi].SzName, _lpSzName, sizeof Student.szName 
         
        mov dword ptr [ebx], esi        ;返回该地址指针
        mov eax, esi
    .endif  
_ret:
    ret
_InsertNameTree  endp
;============================================================================
;将一条学员信息插入到树中
;_lpStProInfo:程序基本信息结构
;_lpStStudent:指向学员信息结构
;成功返回true, 否则返回false
_AddStudentNode proc    public _lpStProInfo:dword, _lpStStudent:dword
;============================================================================
    local   _lpNewStudent   :dword
    local   _dwLen      :dword
    local   _dwBuf      :dword
     
    assume  ebx:ptr Student
    assume  esi:ptr ProInfo
 
    mov ebx, _lpStStudent
    mov esi, _lpStProInfo
;============================================================================
;   在文件中构造一个新的学员对象
;============================================================================
    mov eax, [ebx].dwScoreLen
    lea eax, [eax*8]
    add eax, sizeof Student - 8     
    xchg    _dwLen, eax         ;由于学员信息结构是变长的, 这里求长度
 
    Invoke  Jmalloc, _dwLen         ;分配一个学员大小的内存
    .if !eax
        jmp Error
    .endif
    xchg    _lpNewStudent, eax
    Invoke  RtlMoveMemory, _lpNewStudent, _lpStStudent, _dwLen
     
    Invoke  lstrcpy, offset SzIniFile, offset SzCurrentDir
    invoke  lstrcat, offset SzIniFile, offset SzIniPath
;============================================================================
;   将新建立的学员节点插入树中(编号树)
;============================================================================   
    mov esi, _lpStProInfo
    mov ebx, _lpNewStudent
 
    .if [esi].lpNumberStudent == null       ;如果是首次插入
     
;============================================================================
        ;插入到编号树中
;============================================================================   
        mov _dwBuf, null
        Invoke  _InsertNumberTree, addr _dwBuf , _lpNewStudent, [ebx].dwNumber
        .if !eax
            jmp Error
        .endif
         
        mov ebx, eax
        Invoke  JWritePrivateProfileInt,\   ;写入注册表中
            offset SzSection, offset SzKeylpNumStu, ebx , offset SzIniFile  
        .if !eax
            jmp Error
        .endif      
     
        mov esi, _lpStProInfo       ;更新文件中的结构
        push    ebx
        pop [esi].lpNumberStudent
         
        mov _dwBuf, NULL
;============================================================================
        ;插入到名称树中    
;============================================================================   
        mov ebx, _lpNewStudent      ;插入名称树中
        Invoke  _InsertNameTree, addr _dwBuf, _lpNewStudent, addr [ebx].szName
        .if !eax
            jmp Error
        .endif      
         
        mov ebx, eax
        Invoke  JWritePrivateProfileInt,\   ;写入注册表中
            offset SzSection, offset SzKeylpNameStu, ebx , offset SzIniFile 
        .if !eax
            jmp Error
        .endif  
         
        mov esi, _lpStProInfo       ;更新文件中的结构
        push    ebx
        pop [esi].lpNameStudent 
;============================================================================
    .else              
        lea ecx, [esi].lpNumberStudent  ;插入到编号树中    
        Invoke  _InsertNumberTree, ecx ,_lpNewStudent, [ebx].dwNumber
        .if !eax
            jmp Error
        .endif  
         
        mov esi, _lpStProInfo
        mov ebx, _lpNewStudent
        lea ecx, [esi].lpNameStudent    ;插入到名称树中
        Invoke  _InsertNameTree, ecx ,_lpNewStudent, addr [ebx].szName  
    .endif
;============================================================================
    mov eax, true
Error:
    ret
_AddStudentNode endp
 
End


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值