课前热身|学习这门课前,你需要了解哪些知识?
该思维导图由 AI 生成,仅供参考
数据量单位:位、字节和字
- 深入了解
- 翻译
- 解释
- 总结
本文深入介绍了在学习C语言之前的重要基础知识,包括计算机基础知识、汇编语言和CPU指令集中的寄存器类型。作者首先解释了位、字节和字的概念,以及它们在计算机中的作用和区别。接着详细讲解了汇编语言的特点和使用方法,强调了汇编语言的低级特性和与具体平台的紧密关联。此外,还介绍了汇编语言中使用的助记符和不同参数形式的汇编指令。文章还指出了在x86-64平台上书写和解读汇编代码的方式,并解释了汇编指令与机器指令之间的对应关系。另外,文章还介绍了CPU指令集中的寄存器,包括通用目的寄存器、状态寄存器和系统寄存器等,并详细介绍了在汇编代码中如何使用这些寄存器。最后,文章提供了一个思考题,帮助读者巩固所学内容。整体而言,本文为读者提供了C语言学习前的重要基础知识,帮助他们更好地理解后续的课程内容。
《深入 C 语言和程序运行原理》,新⼈⾸单¥59
全部留言(28)
- 最新
- 精选
- ZR2021置顶于老师,为啥main函数里的asm("movl $0x1, %eax");是将1给eax的,src 跟dst反过来了好像
作者回复: 这又是一个好问题!其实这里我们在代码中使用的是 AT&T 的写法,是一种默认被编译器广泛支持的内联汇编写法。当然,我们也可以做适当的调整来使用 Intel 写法,比如这样: #include <stdio.h> int main(void) { register long num asm("rax") = 0x100000000; asm( ".intel_syntax noprefix \n\t" "mov eax, 1\n\t" // "mov ax, 1\n\t" ".att_syntax" ); printf("%ld\n", num); return 0; } 但这种方式的问题在于,对于某些汇编器可能没有很好的兼容性。
2021-12-07223 - 继业(Adrian)quizz: ``` mov eax, 0x1 # 0b1 inc eax # 0b10 sub eax, 10 # 0b10 - 0b1010 = 0xfffffff8 xor eax, eax # 0xfffffff8 xor 0xfffffff8 = 0x00000000 add eax, 1 # 0x00000001 mul eax # 0x00000001 mul 0x00000001 = 0x00000001 ``` answer: 0x00000001 = 1
作者回复: 完全正确!
2021-12-1220 - 糊糊请教老师, 文中 mov ebx, 1 ,它所对应的机器指令代码为二进制值 bb 01 00 00 00 ,问题有两个: 1、汇编助记符 mov 是如何被翻译为二进制的 ? 是通过在哪里查的表吗 ,如果是查的表,那表又存在哪里呢 2、 那根据机器码二进制能否推导出汇编代码呢?bb 01 00 00 00 ==》mov ebx, 1
作者回复: 很好的问题! 第一个问题:每一个汇编指令都有其对应的组成结构,汇编器会根据助记符的名称进行相应的转换。具体的转换细节可以参考官方手册。比如对于 x86-64:https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html 第二个问题:二进制状态下的机器指令识别可以通过每个指令对应的 OpCode 字节进行切分,然后再根据相应的结构解析出整体指令的结构。
2021-12-0736 - 权验证重写64位寄存器低32位/低16位/低8位的代码中如果不是按照文中那样给寄存器赋值,而是任意赋值,建议大家将printf("%ld\n", num);改为printf("%lx\n", num);,输出16进制数,这样才能观察到正确的结果,否则输出10进制数,一开始没注意的话会发现结果很奇怪 思考题: xor eax, eax是异或,eax自己与自己异或应该为0 add eax, 1,相当于0x0+0x1=0x1 mul eax之后eax的结果应该还是0x1,不知道理论分析是否正确,汇编还不是很熟
作者回复: 答案是正确的哈!
2021-12-065 - dog_brother按老师的程序去执行,第一种(asm("movl $0x1, %eax");)结果是1,第二种(asm("movw $0x1, %ax");)结果是4294967297。 ============================== ```c #include <stdio.h> int main(void) { register long num asm("rax") = 0x100000000; asm("movl $0x1, %eax"); // 第一种 // asm("movw $0x1, %ax"); // 第二种 printf("%d\n", num); printf("%ld\n", num); printf("%X\n", num); return 0; } ``` 按我这段的代码去执行,第一种(movl)和第二种(movw)的执行结果都是 ```shell 1 2 2 ```
作者回复: 关于这个问题我可以先给点提示,然后你再思考下看看。第一个提示就是把第一行 printf 中的 “%d” 改成 “%ld” 就可以输出正确的结果;第二个提示是 printf 的调用会影响寄存器 rax 的值。
2021-12-074 - jack123xor eax,eax 相当于把eax置零了,最后+1,然后mul相乘 默认与eax相乘,最后还是为1
作者回复: 没错!
2021-12-074 - cc在 x86-64 架构下,CPU 指令集架构(ISA)中一共定义了 16 个通用目的寄存器,这些寄存器最大可以存放 4 个字,即 64 位长的数据。在汇编代码中 ---- x86-64 下一个字长不就 64bit 了吗,为什么这里说 「4 个字,即 64 位长的数据」
作者回复: 这是一个很好的发现!我们修改了文章,稍后会更新。 简单来讲就是:字这个概念会在多个地方的多种不同场景下使用。而文中我们介绍的字实际上是指可以体现 CPU 硬件特征的那个字,我一般会称它为硬件字。而在 CPU 指令集中,字也被用来作为衡量数据大小的单位。x86 架构由于历史原因,会将指令集中出现的 WORD 定义为固定 16 位的大小。所以实际文中提到的 4 个字你可以理解为指令字,它的单位是 WORD,4 个字即对应 64 位大小。
2021-12-083 - 琥珀·因为我的环境有 warning: overflow in conversion from 'long long int' to 'long int' 的告警,所以把 register long num 改成了 register long long num #include <stdio.h> int main(void) { register long long num asm("rax") = 0x100000000; asm("movl $0x1, %eax"); // asm("movw $0x1, %ax"); printf("%llx\n", num); return 0; } 不知道这样理解对不对: 当某个指令需要重写寄存器的低 16 位或低 8 位数据时,寄存器中其他位上的数据不会被修改。所以asm("movw $0x1, %ax") 不会修改rax的值,这时num输出的值为0x100000001 而当指令需要重写寄存器低 32 位的数据时,高 32 位的数据会被同时复位,即置零。所以 asm("movl $0x1, %eax") 会将 rax 的结果置零后再写入1,这时num输出的值为0x1。
作者回复: 理解正确哈!
2021-12-132 - 墨eax = 1 eax = eax + 1 eax = eax - 10 异或操作令 eax = 0 eax = eax + 1 eax = eax * eax 最后eax = 1
作者回复: 正解!
2021-12-122 - 傻猫周大福在clang下,rax寄存器虽然被置位,但在此之前会将rax寄存器的值拷贝到栈中,在输出时不会把被置位后的rax输出,而是从栈中重新取出num的值
作者回复: 这是一个很好的发现!
2021-12-1132