• C语言基础语法复习01-HelloWorld


    参考网站:
    https://www.runoob.com/cprogramming/c-tutorial.html 语法
    https://godbolt.org/ 对应的x64 msvc v19.36编译结果
    https://quick-bench.com/ 性能测试?
    https://chat.openai.com/ 对编译结果进行解释

    第一段是c语言源码,一般是基本的简单语法

    #include 
    int main()
    {
        printf("Hello, World! \n"); 
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第二段是以上代码对应的汇编源码:

    _DATA   SEGMENT
    COMM    ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9:QWORD                                                    ; `__local_stdio_printf_options'::`2'::_OptionsStorage
    _DATA   ENDS
    _DATA   SEGMENT
    $SG10424 DB     'Hello, World! ', 0aH, 00H
    _DATA   ENDS
    
    main    PROC
    $LN3:
            sub     rsp, 40                             ; 00000028H
            lea     rcx, OFFSET FLAT:$SG10424
            call    printf
            xor     eax, eax
            add     rsp, 40                             ; 00000028H
            ret     0
    main    ENDP
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    第三段为openai对第二段汇编结果的解释:

    _DATA   SEGMENT   声明了一个名为_DATA的数据段,这通常用于存放全局和静态数据。
    COMM    ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9:QWORD                                                    ; `__local_stdio_printf_options'::`2'::_OptionsStorage
    COMM(通讯段)的声明,表示这是一个公共的或外部的数据项。
    这个特定的数据项是QWORD(8字节或64位)大小,和内部的printf实现有关。
    _DATA   ENDS 数据段_DATA结束。
    _DATA   SEGMENT
    $SG10424 DB     'Hello, World! ', 0aH, 00H
    定义了一个名为$SG10424的数据,DB表示字节数据。
    换行符0aH和字符串结束标志00H。
    _DATA   ENDS
    
    main    PROC  开始定义main函数。
    $LN3:          一个局部标签,用于内部跳转。
            sub     rsp, 40                             ; 00000028H 
                    从栈指针rsp减去4016进制的28H),为局部变量和可能的函数调用预留空间。
            lea     rcx, OFFSET FLAT:$SG10424
                    将$SG10424的地址加载到rcx寄存器。
                    lea是"load effective address"的缩写,
                    rcx是在x64调用约定中用于传递第一个参数的寄存器,
                    所以这里其实是将字符串的地址传递给接下来的printf函数。
            call    printf 调用printf函数。
            xor     eax, eax
                    将eax寄存器清零。在x64调用约定中,函数的返回值是通过eax寄存器返回的,所以这里是设置main函数的返回值为0。
            add     rsp, 40                             ; 00000028H
                    将之前为局部变量预留的40个字节的栈空间释放。
            ret     0 返回,结束main函数。
    main    ENDP  main函数结束定义。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    第四段是一点总结:

    #include 并没有将未被调用的函数源码引入。
    
    main中没有局部变量,但存在sub     rsp, 40 ,原因可能是调用约定预留的、对齐要求、某种编译器优化策略、安全检查需求...这个并不明确。
    
    call    printf这一句是怎么知道跳转到哪执行?跳转到一个表中的位置,这个表在程序加载时由动态链接器填写。这个表称为全局偏移表 (GOT) 或程序查找表 (PLT)。
    这个条目会进一步查找GOT来找到函数的实际地址。当程序首次调用这个函数时,动态链接器会填写这个地址。随后的函数调用将直接使用这个地址,而不需要再次通过动态链接器。
    位置无关代码 (PIC): 这是用于动态链接的代码,可以加载到任意地址并仍然工作。PIC通常使用GOT,因为它允许代码在不知道它将在哪里运行的情况下被编译。
    实际也可以静态编译,此处直接是printf函数的已知地址。
    动态编译时,就是GOT、PLT机制。
    
    在MASM汇编语言中(Microsoft Macro Assembler),FLAT是一个特殊的段修饰符,它表示“默认平坦内存模型”。x86体系结构的早期版本(如16位的实模式)使用了段偏移地址模式。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    修改代码后,比较汇编的变更

    printf("Hello, World!\n%d\n",666); //变更点
    COMM    ?_OptionsStorage@ ...  //这一部分汇编没变
    $SG10424 DB     'Hello, World!', 0aH, '%d', 0aH, 00H  //字符串变更
            mov     edx, 666 ; 0000029aH //参数2倒着赋值
            lea     rcx, OFFSET FLAT:$SG10424 //参数1没变
            call    printf
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其它关联信息

    在x86-64体系结构中,以下是一些常用的64位寄存器的描述:
    
    RAX/RBX/RCX/RDX: 这些是通用寄存器,它们是32位x86架构中EAX, EBX, ECX, 和 EDX寄存器的64位版本。
    RAX (Accumulator Register): 主要用于算术、逻辑和数据传输操作。
    RBX (Base Register): 这是一个通用寄存器,但在某些情境下可以作为基地址指针。
    RCX (Counter Register): 主要用于“重复”指令和“循环”指令的计数器。
    RDX (Data Register): 通常与RAX一起用于乘法和除法操作。
    
    RSI/RDI: 也是通用寄存器,在32位架构中,它们是ESI和EDI。
    RSI (Source Index): 在字符串操作中经常用作源地址指针。
    RDI (Destination Index): 在字符串操作中经常用作目标地址指针。
    
    RBP/RSP: 这些是堆栈操作的寄存器,在32位架构中,它们是EBP和ESP。
    RBP (Base Pointer): 用作堆栈帧的基地址指针。
    RSP (Stack Pointer): 用作堆栈的顶部指针,它始终指向堆栈的当前位置。
    
    R8-R15: 这些是x86-64架构新引入的通用寄存器,为了增加更多的寄存器以提高性能。
    
    RIP: 指令指针。它指向下一个要执行的指令。
    
    段寄存器:CS (代码段), DS (数据段), SS (堆栈段), ES, FS, GS。
    在实模式下,它们用于访问内存地址。
    但在现代操作系统的保护模式和长模式下,它们的用途有所不同,尤其是FS和GS,它们通常用于特定的系统级任务。
    
    每个64位寄存器还有对应的32位、16位和8位的子寄存器。
    例如,RAX的32位子寄存器是EAX,16位子寄存器是AX,
    而8位的低字节和高字节分别是AL和AH。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • 相关阅读:
    第15篇ESP32 idf框架 wifi联网_WiFi AP模式_手机连接到esp32开发板
    vue调用打印功能(print)
    基于微信电子书小程序设计与实现 开题报告
    Python:AES+Base64的加密与解密(ECB模式)
    3.4、可靠传输
    Vue3 企业级优雅实战 - 组件库框架 - 4 组件库的 CSS 架构
    Coredump
    智慧实验室解决方案-最新全套文件
    5分钟快速上手maven
    Ajax,json
  • 原文地址:https://blog.csdn.net/weixin_43172531/article/details/132866312