8086 汇编(十二)

本文对比分析了C语言实现的Objective-C数组对象(链式存储结构)与顺序存储结构线性表的函数执行效率,探讨了不同存储结构在插入、删除等操作上的性能差异,并介绍了XCode下的汇编语言使用方法。

用 C语言 写一个 Objective-C 的数组对象(链式存储结构)

  • 分析 Objective-C 中 NSMutableArray 的属性与方法,规划线性表对象
    1.属性方面:容量(capacity)、长度(length)、指向存储线性表元素的堆内存的指针(value)
    2.方法方面:创建线性表、销毁线性表、清空线性表、获取线性表的属性(capacity、length)、对线性表的增、删、改、查、删除特定值,打印

    线性表对象内存图如下所示:
    线性表内存结构图

  • 实现线性表对象
    HZPLinearList.h 文件代码如下:

    #ifndef LinearList_h
    #define LinearList_h
    
    #include <stdio.h>
    
    #pragma mark -  宏定义(条件编译)
    #ifndef LINEARLIST_STRUCT
    
    // 线性表节点数据
    typedef void* LinearListNodeValue;
    // 线性表
    typedef void LinearList;
    
    #endif
    
    #pragma mark - 创建 销毁 清空
    // 创建线性表
    // return.线性表指针
    LinearList* listCreat();
    
    // 销毁线性表
    // param0.线性表指针
    void listRelease(LinearList* list);
    
    // 清空线性表
    // param0.线性表指针
    void listClear(LinearList* list);
    
    #pragma mark - 属性获取
    // 获取线性表的长度
    // param0.线性表指针
    // return.线性表长度
    int listLength(LinearList* list);
    
    #pragma mark - 增
    // 往线性表中插入数据
    // param0.线性表指针
    // param1.要插入的位置的索引
    // param2.要插入的值
    void listInsert(LinearList* list, int index, LinearListNodeValue value);
    
    // 往线性表中添加数据(添加在表尾)
    void listAdd(LinearList* list, LinearListNodeValue value);
    
    #pragma mark - 删
    // 删除线性表中指定索引位置的元素
    // param0.线性表指针
    // param1.索引
    void listRemove(LinearList* list, int index);
    
    #pragma mark - 改
    // 修改线性表中指定位置的元素为指定的值
    // param0.线性表指针
    // param1.索引
    // param2.值
    void listSet(LinearList* list, int index, LinearListNodeValue value);
    
    #pragma mark - 查
    // 获取线性表指定索引处元素的值
    // param0.线性表指针
    // param1.索引
    // return.元素的值
    LinearListNodeValue listGet(LinearList* list, int index);
    
    #pragma mark - 特殊功能
    // 删除线性表中具有指定值的所有元素
    // param0.线性表指针
    // param1.要删除的值
    void listRemoveValue(LinearList* list, LinearListNodeValue value);
    
    // 打印线性表
    // param0.线性表指针
    void listPrint(LinearList* list);
    
    #endif /* LinearList_h */
    

    HZPLinearList.c 代码如下:

    #pragma mark - 定义线性表结构体
    // 节点数据指针
    typedef void* LinearListNodeValue;
    
    // 节点结构体
    typedef struct _LinearListNode LinearListNode;
    struct _LinearListNode {
        LinearListNodeValue value;  // 节点数据
        LinearListNode* next;       // 指向下个节点的指针
    };
    
    // 线性表结构体
    typedef struct {
        int length;                 // 长度
        LinearListNode* header;     // 头节点
    } LinearList;
    
    /*
     注意:宏 LINEARLIST_STRUCT 的定义,一定要在导入 HZPLinearList.h 之前
     这是为了防止上面的 LinearListNodeValue 指针和 LinearList 结构体被重复定义
     **/
    //
    #define LINEARLIST_STRUCT
    
    #pragma mark - 导入头文件
    #include "HZPLinearList.h"
    
    // 使用 mallc函数 需要导入
    #include <stdlib.h>
    
    #pragma mark - 创建 销毁 清空
    // 创建线性表
    LinearList* listCreat() {
        // 分配线性表结构体和表头的内存空间(在堆区)
        // malloc函数如果遇到内存资源紧张,给不了这么多字节,可能会返回空
        LinearList* list = malloc(sizeof(LinearList) + sizeof(LinearListNode));
        if (list) {
            list->length = 0;
            list->header = (LinearListNode *)(list + 1);
            list->header->value = NULL;
            list->header->next = NULL;
        }
        return list;
    }
    
    // 销毁线性表
    void listRelease(LinearList* list) {
        if (NULL == list) {
            return;
        }
        listClear(list);
        free(list);
    }
    
    // 清空线性表
    void listClear(LinearList* list) {
        if (NULL == list) {
            return;
        }
        // while 循环中,headerNode 会一直有值,while 循环通过 break 跳出
        LinearListNode* headerNode = list->header;
        while (headerNode) {
            LinearListNode* removeNode = headerNode->next;
            if (NULL == removeNode) {
                break;
            }
            headerNode->next = removeNode->next;
            free(removeNode);
        }
        // 将线性表的长度置为 0
        list->length = 0;
    }
    
    #pragma mark - 属性获取
    // 获取线性表的长度
    int listLength(LinearList* list) {
        if (NULL == list) {
            return 0;
        }
        return list->length;
    }
    
    #pragma mark - 增
    // 往线性表中插入数据
    void listInsert(LinearList* list, int index, LinearListNodeValue value) {
        if (NULL == list) {
            return;
        }
        // 可以在表尾进行插入,因此这里的条件是 index > list->length,而不是 index >= list->length
        if (index < 0 || index > list->length) {
            return;
        }
        // 获取 index - 1 节点
        LinearListNode* currentNode = list->header;
        for (int i = 0; i < index; i++) {
            currentNode = currentNode->next;
        }
        // 创建新增节点
        LinearListNode* tempNode = malloc(sizeof(LinearListNode));
        if (NULL == tempNode) {
            return;
        }
        tempNode->value = value;
        // 交换 新增节点 与 index - 1节点 的 next 指针
        tempNode->next = currentNode->next;
        currentNode->next = tempNode;
        // 线性表的长度 + 1
        list->length++;
    }
    
    // 往线性表中添加数据(添加在表尾)
    void listAdd(LinearList* list, LinearListNodeValue value) {
        if (NULL == list) {
            return;
        }
        listInsert(list, list->length, value);
    }
    
    #pragma mark - 删
    // 删除线性表中指定索引位置的元素
    void listRemove(LinearList* list, int index) {
        if (NULL == list) {
            return;
        }
        if (index < 0 || index > list->length - 1) {
            return;
        }
        // 获取 index - 1 节点
        LinearListNode* currentNode = list->header;
        for (int i = 0; i < index; i++) {
            currentNode = currentNode->next;
        }
        // 获取 index 节点
        LinearListNode* removeNode = currentNode->next;
        // 删除 index 节点
        currentNode->next = removeNode->next;
        free(removeNode);
        // 线性表的长度 - 1
        list->length--;
    }
    
    #pragma mark - 改
    // 修改线性表中指定位置的元素为指定的值
    void listSet(LinearList* list, int index, LinearListNodeValue value) {
        if (NULL == list) {
            return;
        }
        if (index < 0 || index > list->length - 1) {
            return;
        }
        // 获取 index 节点,并修改 index 节点的值
        LinearListNode* currentNode = list->header;
        for (int i = 0; i <= index; i++) {
            currentNode = currentNode->next;
        }
        currentNode->value = value;
    }
    
    #pragma mark - 查
    // 获取线性表指定索引处元素的值
    LinearListNodeValue listGet(LinearList* list, int index) {
        if (NULL == list) {
            return 0;
        }
        if (index < 0 || index > list->length - 1) {
            return 0;
        }
        // 获取 index 节点,并获取 index 节点的值
        LinearListNode* currentNode = list->header;
        for (int i = 0; i <= index; i++) {
            currentNode = currentNode->next;
        }
        return currentNode->value;
    }
    
    #pragma mark - 特殊功能
    // 删除线性表中具有指定值的所有元素 - 效率较高
    void listRemoveValue(LinearList* list, LinearListNodeValue value) {
        if (NULL == list) {
            return;
        }
        // 遍历所有元素
        // 注意:
        // 1.for 循环的循环次数依赖于集合的大小,如果在循环过程中集合的大小发生改变,会变得比较难处理。因此这里用 while 循环
        // 2.这里的 while 循环通过 return 跳出
        LinearListNode* currentNode = list->header;
        while (currentNode) {
            LinearListNode* nextNode = currentNode->next;
            if (NULL == nextNode) {
                return;
            }
            if (nextNode->value == value) {
                currentNode->next = nextNode->next;
                free(nextNode);
                list->length--;
            } else {
                currentNode = nextNode;
            }
        }
    }
    
    // 打印线性表
    void listPrint(LinearList* list) {
        if (NULL == list) {
            return;
        }
        printf("list{\n");
        printf("\tlength = %d;\n", list->length);
        printf("\tvalue = [");
        LinearListNode* currentNode = list->header;
        for (int i = 0; i <= list->length - 1; i++) {
            currentNode = currentNode->next;
            printf("%p", currentNode->value);
            if (i <= list->length - 2) {
                printf(",");
            }
        }
        printf("];\n\t}\n\n");
    }
    

    main.m 代码如下

    #import <Foundation/Foundation.h>
    #import "HZPLinearList.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // 创建线性表
            LinearList* list = listCreat();
            
            // 添加数据
            for (int i = 0; i < 5; i++) {
                listAdd(list, (LinearListNodeValue)i);
            }
            listInsert(list, 0, (LinearListNodeValue *)3);
            listInsert(list, 1, (LinearListNodeValue *)4);
            listPrint(list);
            /*
            list{
            length = 7;
            value = [0x3,0x4,0x0,0x1,0x2,0x3,0x4];
            }
             **/
            
            // 删除数据
            listRemove(list, 2);
            listPrint(list);
            /*
            list{
            length = 6;
            value = [0x3,0x4,0x1,0x2,0x3,0x4];
            }
            **/
            
            // 修改数据
            listSet(list, 0, (LinearListNodeValue)4);
            listPrint(list);
            /*
            list{
            length = 6;
            value = [0x4,0x4,0x1,0x2,0x3,0x4];
            }
            **/
            
            // 查询数据
            LinearListNodeValue value = listGet(list, 0);
            NSLog(@"value = %p", value);
            /*
            value = 0x4
            **/
            
            // 删除指定数据
            listRemoveValue(list, (LinearListNodeValue)4);
            listPrint(list);
            /*
            list{
            length = 3;
            value = [0x1,0x2,0x3];
            }
            **/
            
            // 清空线性表
            int length = listLength(list);
            NSLog(@"length = %d", length);
            listClear(list);
            listPrint(list);
            /*
            length = 3
            list{
            length = 0;
            value = [];
            }
            **/
            
            // 销毁线性表
            listRelease(list);
            listPrint(list);
            /*
            list{
            length = 0;
            value = [];
            }
            **/
        }
        return 0;
    }
    

线性表 HCGLinearList && HZPLinearList 函数执行效率分析

  • 线性表 HCGLinearList(顺序存储结构)和 HZPLinearList(链式存储结构)各个函数的时间复杂度如下

    函数HCGLinearList(顺序存储结构)HZPLinearList(链式存储结构)
    LinearList* listCreat()O(1)O(1)
    void listRelease(LinearList* list)O(1)O(n)
    void listClear(LinearList* list)O(1)O(n)
    int listLength(LinearList* list)O(1)O(1)
    int listCapacity(LinearList* list)O(1)- - - -
    void listInsert(LinearList* list, int index, LinearListNodeValue value)O(n)O(n)
    void listAdd(LinearList* list, LinearListNodeValue value)O(n)O(n)
    void listRemove(LinearList* list, int index)O(n)O(n)
    void listSet(LinearList* list, int index, LinearListNodeValue value)O(1)O(n)
    LinearListNodeValue listGet(LinearList* list, int index)O(1)O(n)
    void listRemoveValue(LinearList* list, LinearListNodeValue value)O(n)O(n)
    void listPrint(LinearList* list)O(n)O(n)
  • 函数执行效率分析
    对比 HCGLinearList(顺序存储结构) 与 HZPLinearList(链式存储结构)各个函数的时间复杂度,我们发现:

    1. HCGLinearList(顺序存储结构)中,除了 listInsert、listAdd、listRemove、listRemoveValue、listPrint 的时间复杂度为 O(n) 外,其余函数的时间复杂度均为 O(1)
    2. HZPLinearList(链式存储结构)中,除了 listCreat、listLength 的时间复杂度为 O(1) 外,其余函数的时间复杂度均为 O(n)

    虽然 HCGLinearList 的总体执行效率明显高于 HZPLinearList,但是这并不能说明 顺序存储结构 优于 链式存储结构。顺序存储结构与链式存储结构,各有优缺点:

    1. 顺序存储结构:存储单元的内存地址是连续的。适用于:对数据遍历和查找频繁 && 对数据插入和删除较少 的场合。
    2. 链式存储结构:存储单元的内存地址可以是连续的也可以是不连续的,它不要求逻辑上相邻的元素在物理地址上也相邻。适用于:对数据遍历和查找较少 && 对数据插入和删除频繁 的场合。

    Question
    那是什么原因造成 HZPLinearList(链式存储结构)在插入数据和删除数据的性能上没有高于 HCGLinearList(顺序存储结构)呢?
    Answer
    问题出在函数调用方法的设计上。
    HCGLinearList 为 顺序存储结构,存储单元的内存地址是连续的,操作数据可以通过索引(index)。
    HZPLinearList 为 链式存储结构,存储单元的内存地址是不连续的,如果通过索引操作数据,则必然会进行遍历,这样就体现不出链式存储的优势。因此,链式存储结构 操作数据应该通过节点(LinearListNode),而不是通过索引(index)

XCode 使用汇编

在 XCode 下使用汇编的方式有两种:

  1. 内联汇编:汇编代码与高级语言代码混合使用
  2. 外联汇编:用专门的文件编写汇编代码,高级语言通过函数调用汇编代码。
  • 内联汇编

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
            int num1 = 10;
            int num2 = 20;
            int result = num1 + num2;
            
            asm
            (
             "pushq %rax\n"
             "pushq %rdi\n"
             "pushq %rsi\n"
             
             "movq $0x1122, %rdi\n"
             "movq $0x3344, %rsi\n"
             "movq %rdi, %rax\n"
             "addq %rsi, %rax\n"
             
             "popq %rsi\n"
             "popq %rdi\n"
             "popq %rax\n"
             );
            
            NSLog(@"result = %d", result);
        }
        return 0;
    }
    
  • 外联汇编

    外联汇编使用步骤:

    1. 新建汇编代码文件,使用汇编代码实现相应的函数功能
    2. 在高级语言中导入相应的函数,然后调用

    HcgFunc.h 文件如下:

    #ifndef Sum_h
    #define Sum_h
    
    int sum(int a, int b);
    
    #endif /* Sum_h */
    

    HcgFunc.s 文件如下:

    /*
     <<新建汇编代码文件>>
     新建汇编代码文件的步骤:
     New File ... - macOS - Assembly File
     汇编代码在 macOS/Unix 下后缀为 .s,在 Windows 下后缀为 .asm
     
     注意:
     汇编代码文件(.s 文件),也是代码文件,并非真正的 以 0 和 1 表示的汇编代码
     .s 文件也需要经过编译器编译才能生成对应的汇编代码
     我们可以在 Build Phases - Compile Sources 下看到,我们新建的汇编文件也会参与编译,
     因为 .s 文件也是源文件,并非目标二进制代码文件
      
     
     <<编写汇编代码>>
     .text : 表示代码段
     .data : 表示数据段
     .global 标号 : 在.s文件里面定义的函数,默认是私有函数,外界调用不到,使用(.global 标号)将函数声明为全局函数
     _sum : 汇编代码里面的标号为 _函数名,高级语言调用汇编代码的功能时,直接使用函数名( 在汇编代码里面,如果是函数的实现,则需要在函数名前面加一个下划线 _ )
    
     通过编译器生成的汇编代码,在调用函数进行传参时,存储参数的寄存器是有顺序的:
     di si dx cx 8d 9d
     MacBook 的 CPU 为 64 位的 CPU,其专门用来存储函数形参的寄存器有6个
     当源函数传递给目标函数的形参超过6个,则会将剩余的形参 push 到源函数的栈空间中
    
     esi : e 表示 32 位寄存器
     rsi : r 表示 64 位寄存器
     
     movl : l 表示操作 32 位
     movq : q 表示操作 64 位
     
     
     <<导入汇编函数并使用>>
     高级语言中导入汇编函数的方式有两种:
     1.为汇编文件 HcgFunc.s 新建一个相应的头文件 HcgFunc.h,在 HcgFunc.h 中声明 HcgFunc.s 里面实现的函数,然后在高级语言中导入 HcgFunc.h,高级语言通过 HcgFunc.h 声明的函数调用相应的汇编代码。
     2.在高级语言中,通过 extern 导入相应的函数,进行使用
     extern int sum(int a, int b);
     */
    
    .text
    .global _sum
    
    _sum:
    movq %rdi, %rax
    addq %rsi, %rax
    retq
    

    main.m 文件如下:

    #import <Foundation/Foundation.h>
    //#import "HcgFunc.h"
    
    extern int sum(int num1, int num2);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {	
            /*
            关于如何传参数
            sum(10, 20) 必然生成如下汇编指令
            movq $10, %rdi
            movq $20, %rsi
            call _sum
             */
            int result = sum(10, 20);
            NSLog(@"result = %d", result);
        }
        return 0;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值