当前位置: 代码迷 >> 综合 >> C语言督学营 高级笔记 (Day5~6) (汇编重点)
  详细解决方案

C语言督学营 高级笔记 (Day5~6) (汇编重点)

热度:59   发布时间:2023-12-11 20:53:56.0

individuality 一研为定

算法 内核 编译原理 设计模式

文章目录

  • 高级第五次 直播 内存 混合运算
    • 内存查看接口编写
    • 混合运算
    • 深入理解 const
    • 结构体对齐原理
  • 高级第六次 直播 汇编讲解
    • 指令格式与常用指令
    • 理解数组与指针对应的汇编
    • 汇编实战
    • 条件码
    • 函数调用原理(汇编)

高级第五次 直播 内存 混合运算

内存查看接口编写

  • 位运算的应用实战
#include <stdio.h>void show_memory_01(void* start, int memory_len)
{
    int i;char* p = (char*)start;for (i = 0; i < memory_len; i++){
    if (i % 4 == 0) //输出地址{
    printf("0x%p ", p + i);}printf("%x", (p[i] & 0x000000f0) >> 4);  //输出内存的数据printf("%x ", p[i] & 0x0000000f);if ((i + 1) % 4 == 0){
    printf("\n");}}
}void show_memory(void* start, int memory_len)
{
    int i;char* p = (char*)start;for (i = 0; i < memory_len; i++){
    printf("%x ", p[i]);if ((i + 1) % 4 == 0){
    printf("\n");}}
}
int main() {
    float f = 1.456;int arr[3] = {
     1,2,3 };show_memory(&f, sizeof(f));printf("\n");show_memory(arr, sizeof(arr));return 0;
}

内存查看效果如下,将在后期进行优化
在这里插入图片描述

  • 注意: 逻辑与 按位与的区别 && 与 & 的区别

参考链接

进行调试测试,注意要进行打断点
在这里插入图片描述
在这里插入图片描述

  • 注意: 计算机内存中存储的是补码

  • ff ff ff fa 代表负数,所以先转化为原码,然后转为10进制,就是把ff ff ff fa 除了符号位各位取反加一得到原码1000 0000 0000 0000 0000 0000 0000 0110 也就是-6

  • 求补码快捷方法:从高位开始按位去反,直到最后一个1(并且最后一个1保持不变)

#include <stdio.h>void show_memory(void* start, int memory_len)
{
    int i;char* p = (char*)start;for (i = 0; i < memory_len; i++){
    if(i%4==0) //输出地址{
     printf("0x%p ", p+i);}printf("%x", (p[i]& 0x000000f0)>>4);  //输出内存的数据printf("%x ", p[i]& 0x0000000f);if ((i + 1) % 4 == 0){
    printf("\n");}}
}int main()
{
    int i = 5, j = 7;printf("i & j=%d\n", i & j);printf("i | j=%d\n", i | j);printf("i ^ j=%d\n", i ^ j);printf("~i=%d\n", ~i);//位运算实战float f = 1.456;show_memory(&f, sizeof(f));int arr[3] = {
     1,2,3 };show_memory(arr, sizeof(arr));//异或交换两个数i = i ^ j;j = i ^ j;i = i ^ j;printf("i=%d,j=%d\n", i, j); //找出一个整数最低位为1的那个数 printf("最低位为1的值 %d\n", 12 & -12);return 0;
}

1.有两个变量a与b,在不使用第三个量的情况下,通过异或操作来交换这两个变量的值,这种交换相对于之前的加法交换有何优势?

i = i^ j ;
j = j ^i ;
i = i ^j ;
  • 找出一个数最低为位1 的那个数,解决方法:将其与自己的负数相与

0000 0101 => 5
1111 1011 => -5

混合运算

混合运算规则:不同数据类型转换级别

在这里插入图片描述

注意事项

  • 例子一:同时左右移位运算与分步左右移运算的区分
#include <stdio.h>
#include <stdlib.h>void big_int_mul()
{
    long long l;l = (long long)131072 * 131072;printf("%lld\n", l);
}int main()
{
    char b = 0x93 << 1 >> 1;printf("%x\n", b);b = 0x93 << 1; //赋值一瞬间发生了丢失b = b >> 1;printf("%x\n", b);big_int_mul();return 0;
}

整型运算按4个字节进行

0x93
0000 0000 0000 0000 0000 0000 1001 0011

0000 0000 0000 0000 0000 0001 0010 0110

b 0010 0110 0001 0011

  • 例子二:数据存储 131072 是int 类型再与131072 相乘存储不下
void big_int_mul()
{
    long long l;l = (long long)131072 * 131072;printf("%lld\n", l);
}
  • 例子三:浮点型常量默认按8字节运算
#include <stdio.h>
#include <stdlib.h>
//浮点型常量默认按8字节运算
int main()
{
    float f = 12345678900.0 + 1;double d = f;printf("%f\n", f);    // 12345678848。000000printf("%f\n", 12345678900.0 + 1);   // 12345678901.000000return 0;
}

深入理解 const

参考链接

#include <stdio.h>
#include <stdlib.h>void const_two()
{
    char str[] = "hello world";char str1[] = "how do you do";char* const ptr = str;//和普通变量一致,代表ptr被修改str[0] = 'H';puts(ptr);ptr[0] = 'n';  //合法puts(ptr);//ptr = "world"; //非法,编译错误,error C2166: 左值指定const对象}int main()
{
    const int i = 5;//i在下面的代码中不能修改,是常量char str[] = "hello world";const char* ptr = str;//这里代表ptr指向的空间不能被修改str[0] = 'H';  //操作合法puts(ptr);ptr = "world";//ptr[0] = 'n'; //操作非法,编译错误,提示error C2166: 左值指定const对象puts(ptr);//const修饰指针的第二种情况const_two();return 0;
}

结构体对齐原理

数据类型自身的对齐值如下:

  • 对于char型数据,其自身对齐值为1,对于 short型为2,对于int,float, double类型 ,其自身对齐值为4,单位字节。

高级第六次 直播 汇编讲解

指令格式与常用指令

  • 操作码字段:表征指令的操作特性与功能(指令的唯一标识)不同的指令操作码不能相同
  • 地址码字段:指定参与操作的操作数的地址码

架构:

1、英特尔:(AMD) x86 mov AL BL ; 是BL 放到 AL 中
2、龙芯 (Mips)
3、ARM 高通、苹果、华为
4、Powerpc IBM

不同架构间汇编指令差异很大

复杂指令集:变长 x86
精简指令集:等长 arm

1、C文件预处理后变为 i 文件
2、文件经过编译后变为s文件汇编文件
3、汇编文件经过汇编变为目标文件oj
4、Obj经过链接变为exe

常用指令

  • 汇编指令通常可以分为数据传送指令、逻辑计算指令和控制流指令,下面以 Intel格式为例,介绍一些重要的指令。以下用于操作数的标记分别表示寄存器、内存和常数

1、<reg>:表示任意寄存器,若其后带有数字,则指定其位数,如reg<32>表示32位 寄存器(eax、ebx、ecx、edx、esi、edi、esp或ebp);表示16位寄存器(ax,bx、cx或dx);表示8位寄存器(ah、al、bh、bl、ch、cl、dh、dl)

2、<mem>:表示内存地址(如[eax]、[var+4]或 dword ptr[eax+ebx]).
3、<con>:表示8位、16位或32位常数。表示8位常数;<con16>表示16位常数;<con32>表示32位常数。

  • 数据传送指令、算术和逻辑运算指令、控制流指令

汇编语言学习启航

理解数组与指针对应的汇编

在这里插入图片描述

在这里插入图片描述
main 去调用子函数是,前后所做的工作

汇编实战

  • 在转化为汇编代码时,所有的变量名将不在
  • 任何一个函数都是自己独立的栈空间

英特尔CPU 栈顶在低地址,栈底在高地址

(1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。

(2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

  • 关于 DWORD PTR 是什么意思:
    1、dword 双字 就是四个字节
    2、ptr pointer缩写 即指针
    3、[]里的数据是一个地址值,这个地址指向一个双字型数据

比如:mov eax, dword ptr [12345678] 把内存地址12345678中的双字型(32位)数据赋给eax

  • lea指令的作用:是 DWORD PTR_arrs[ebp]对应空间的内存地址值放到eax中

在这里插入图片描述

条件码

  • 编译器通过条件码(标志位)设置指令和各类转移指令来实现程序中的选择结构语句。

(1)条件码(标志位)
除了整数寄存器,CPU还维护着一组条件码(标志位)寄存器,它们描述了最近的算术或逻辑运算操作的属性。可以检测这些寄存器来执行条件分支指令,最常用的条件码有:

  • CF:进(借)位标志。最近无符号整数加(减)运算后的进(借)位情况。有进(借)位,CF=1;否则CF=0
  • ZF:零标志。最近的操作的运算结算是否为0。若结果为0,ZF=1;否则ZF=0
  • SF:符号标志。最近的带符号数运算结果的符号。负数时,SF=1;否则SF=0
  • OF:溢出标志。最近带符号数运算的结果是否溢出,若溢出,OF=1;否则OF=0
    可见,OF和SF对无符号数运算来说没有意义,而CF对带符号数运算来说没有意义。

常见的算术逻辑运算指令(add、sub、 imul, or、and、shl、inc、dec、not、sal等)会设置条件码。但有两类指令只设置条件码而不改变任何其他寄存器:cmp指令和sub指令的行为一样,test指令与and指令的行为一样,但它们只设置条件码,而不更新目的寄存器。

  • 之前介绍过的 jcondition条件转跳指令,就是根据条件码ZF和SF来实现转跳。

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int arr[3]={
    1,2,3};int *p;int i=5;int j=10;i=arr[2];p=arr;printf("i=%d\n",i);if (i < j){
    printf("i is small\n");}system("pause");
}

主体汇编代如下

;	COMDAT _main
_TEXT	SEGMENT
_j$ = -56						; size = 4
_i$ = -44						; size = 4
_p$ = -32						; size = 4
_arr$ = -20						; size = 12
__$ArrayPad$ = -4					; size = 4
_main	PROC						; COMDAT; 4    : {
    00000	55		 push	 ebp00001	8b ec		 mov	 ebp, esp00003	81 ec fc 00 0000		 sub	 esp, 252		; 000000fcH00009	53		 push	 ebx0000a	56		 push	 esi0000b	57		 push	 edi0000c	8d bd 04 ff ffff		 lea	 edi, DWORD PTR [ebp-252]00012	b9 3f 00 00 00	 mov	 ecx, 63			; 0000003fH00017	b8 cc cc cc cc	 mov	 eax, -858993460		; ccccccccH0001c	f3 ab		 rep stosd0001e	a1 00 00 00 00	 mov	 eax, DWORD PTR ___security_cookie00023	33 c5		 xor	 eax, ebp00025	89 45 fc	 mov	 DWORD PTR __$ArrayPad$[ebp], eax00028	b9 00 00 00 00	 mov	 ecx, OFFSET __8B08B150_main@c0002d	e8 00 00 00 00	 call	 @__CheckForDebuggerJustMyCode@4;--------------------------------------
; 下面代码主要实现 数组为什么数组名为其首地址
;--------------------------------------
; 5    : 	int arr[3]={
    1,2,3};00032	c7 45 ec 01 0000 00		 mov	 DWORD PTR _arr$[ebp], 100039	c7 45 f0 02 0000 00		 mov	 DWORD PTR _arr$[ebp+4], 200040	c7 45 f4 03 0000 00		 mov	 DWORD PTR _arr$[ebp+8], 3; 6    : 	int *p;
; 7    : 	int i=5;00047	c7 45 d4 05 0000 00		 mov	 DWORD PTR _i$[ebp], 5; 8    : 	int j=10;0004e	c7 45 c8 0a 0000 00		 mov	 DWORD PTR _j$[ebp], 10	; 0000000aH; 9    : 	i=arr[2];00055	b8 04 00 00 00	 mov	 eax, 40005a	d1 e0		 shl	 eax, 10005c	8b 4c 05 ec	 mov	 ecx, DWORD PTR _arr$[ebp+eax]00060	89 4d d4	 mov	 DWORD PTR _i$[ebp], ecx; 10   : 	p=arr;00063	8d 45 ec	 lea	 eax, DWORD PTR _arr$[ebp]00066	89 45 e0	 mov	 DWORD PTR _p$[ebp], eax; 11   : 	printf("i=%d\n",i);00069	8b 45 d4	 mov	 eax, DWORD PTR _i$[ebp]0006c	50		 push	 eax0006d	68 00 00 00 00	 push	 OFFSET ??_C@_05BKKKKIID@i?$DN?$CFd?6@00072	e8 00 00 00 00	 call	 _printf00077	83 c4 08	 add	 esp, 8; 12   : 	if (i < j)0007a	8b 45 d4	 mov	 eax, DWORD PTR _i$[ebp]0007d	3b 45 c8	 cmp	 eax, DWORD PTR _j$[ebp]00080	7d 0d		 jge	 SHORT $LN2@main; 13   : 	{
    
; 14   : 		printf("i is small\n");00082	68 00 00 00 00	 push	 OFFSET ??_C@_0M@KNINEIJI@i?5is?5small?6@00087	e8 00 00 00 00	 call	 _printf0008c	83 c4 04	 add	 esp, 4
$LN2@main:; 15   : 	}
; 16   : 	system("pause");0008f	8b f4		 mov	 esi, esp00091	68 00 00 00 00	 push	 OFFSET ??_C@_05PDJBBECF@pause@00096	ff 15 00 00 0000		 call	 DWORD PTR __imp__system0009c	83 c4 04	 add	 esp, 40009f	3b f4		 cmp	 esi, esp000a1	e8 00 00 00 00	 call	 __RTC_CheckEsp; 17   : }

函数调用原理(汇编)

关于栈的描述
在这里插入图片描述
汇编代码如下:

  00000	55		 push	 ebp00001	8b ec		 mov	 ebp, esp00003	81 ec cc 00 0000		 sub	 esp, 204		; 000000ccH00009	53		 push	 ebx0000a	56		 push	 esi0000b	57		 push	 edi0000c	8d bd 34 ff ffff		 lea	 edi, DWORD PTR [ebp-204]00012	b9 33 00 00 00	 mov	 ecx, 51			; 00000033H00017	b8 cc cc cc cc	 mov	 eax, -858993460		; ccccccccH0001c	f3 ab		 rep stosd; 5    :    int c;
; 6    :    c = *a; 0001e	8b 45 08	 mov	 eax, DWORD PTR _a$[ebp]00021	8b 08		 mov	 ecx, DWORD PTR [eax]00023	89 4d f8	 mov	 DWORD PTR _c$[ebp], ecx; 7    :    *a = *b;00026	8b 45 08	 mov	 eax, DWORD PTR _a$[ebp]00029	8b 4d 0c	 mov	 ecx, DWORD PTR _b$[ebp]0002c	8b 11		 mov	 edx, DWORD PTR [ecx]0002e	89 10		 mov	 DWORD PTR [eax], edx; 8    :    *b = c;00030	8b 45 0c	 mov	 eax, DWORD PTR _b$[ebp]00033	8b 4d f8	 mov	 ecx, DWORD PTR _c$[ebp]00036	89 08		 mov	 DWORD PTR [eax], ecx; 9    : }00038	5f		 pop	 edi00039	5e		 pop	 esi0003a	5b		 pop	 ebx0003b	8b e5		 mov	 esp, ebp0003d	5d		 pop	 ebp0003e	c3		 ret	 0

函数调用不传参
在这里插入图片描述

在这里插入图片描述

Tips:

  • 如下图
    在这里插入图片描述
    函数调用传参

指针的间接访问在汇编的体现

_TEXT	SEGMENT
_ret$ = -32						; size = 4
_b$ = -20						; size = 4
_a$ = -8						; size = 4
_main	PROC						; COMDAT; 12   : {
     00000	55		 push	 ebp00001	8b ec		 mov	 ebp, esp00003	81 ec e4 00 0000		 sub	 esp, 228		; 000000e4H00009	53		 push	 ebx0000a	56		 push	 esi0000b	57		 push	 edi0000c	8d bd 1c ff ffff		 lea	 edi, DWORD PTR [ebp-228]00012	b9 39 00 00 00	 mov	 ecx, 57			; 00000039H00017	b8 cc cc cc cc	 mov	 eax, -858993460		; ccccccccH0001c	f3 ab		 rep stosd; 13   :    int a,b,ret;
; 14   :    a =16;0001e	c7 45 f8 10 0000 00		 mov	 DWORD PTR _a$[ebp], 16	; 00000010H; 15   :    b = 64;00025	c7 45 ec 40 0000 00		 mov	 DWORD PTR _b$[ebp], 64	; 00000040H; 16   :    ret = 0;0002c	c7 45 e0 00 0000 00		 mov	 DWORD PTR _ret$[ebp], 0; 17   :    swap(&a,&b);00033	8d 45 ec	 lea	 eax, DWORD PTR _b$[ebp]00036	50		 push	 eax00037	8d 4d f8	 lea	 ecx, DWORD PTR _a$[ebp]0003a	51		 push	 ecx0003b	e8 00 00 00 00	 call	 _swap00040	83 c4 08	 add	 esp, 8; 18   :    ret = a - b;00043	8b 45 f8	 mov	 eax, DWORD PTR _a$[ebp]00046	2b 45 ec	 sub	 eax, DWORD PTR _b$[ebp]00049	89 45 e0	 mov	 DWORD PTR _ret$[ebp], eax; 19   :    printf("ret=%d\n",ret);0004c	8b f4		 mov	 esi, esp0004e	8b 45 e0	 mov	 eax, DWORD PTR _ret$[ebp]00051	50		 push	 eax00052	68 00 00 00 00	 push	 OFFSET ??_C@_07EGPJDCKD@ret?$DN?$CFd?6?$AA@00057	ff 15 00 00 0000		 call	 DWORD PTR __imp__printf0005d	83 c4 08	 add	 esp, 800060	3b f4		 cmp	 esi, esp00062	e8 00 00 00 00	 call	 __RTC_CheckEsp; 20   :    system("pause");00067	8b f4		 mov	 esi, esp00069	68 00 00 00 00	 push	 OFFSET ??_C@_05PDJBBECF@pause?$AA@0006e	ff 15 00 00 0000		 call	 DWORD PTR __imp__system00074	83 c4 04	 add	 esp, 400077	3b f4		 cmp	 esi, esp00079	e8 00 00 00 00	 call	 __RTC_CheckEsp; 21   :    return ret;0007e	8b 45 e0	 mov	 eax, DWORD PTR _ret$[ebp]; 22   : }00081	52		 push	 edx00082	8b cd		 mov	 ecx, ebp00084	50		 push	 eax00085	8d 15 00 00 0000		 lea	 edx, DWORD PTR $LN6@main0008b	e8 00 00 00 00	 call	 @_RTC_CheckStackVars@800090	58		 pop	 eax00091	5a		 pop	 edx00092	5f		 pop	 edi00093	5e		 pop	 esi00094	5b		 pop	 ebx00095	81 c4 e4 00 0000		 add	 esp, 228		; 000000e4H0009b	3b ec		 cmp	 ebp, esp0009d	e8 00 00 00 00	 call	 __RTC_CheckEsp000a2	8b e5		 mov	 esp, ebp000a4	5d		 pop	 ebp000a5	c3		 ret	 0000a6	8b ff		 npad	 2
$LN6@main:000a8	02 00 00 00	 DD	 2000ac	00 00 00 00	 DD	 $LN5@main
$LN5@main:000b0	f8 ff ff ff	 DD	 -8			; fffffff8H000b4	04 00 00 00	 DD	 4000b8	00 00 00 00	 DD	 $LN3@main000bc	ec ff ff ff	 DD	 -20			; ffffffecH000c0	04 00 00 00	 DD	 4000c4	00 00 00 00	 DD	 $LN4@main
$LN4@main:000c8	62		 DB	 98			; 00000062H000c9	00		 DB	 0
$LN3@main:000ca	61		 DB	 97			; 00000061H000cb	00		 DB	 0
_main	ENDP
_TEXT	ENDS
; Function compile flags: /Odtp /RTCsu /ZI
; File g:\code_2021\??±à????\??±à????2\main.c
;	COMDAT _swap
_TEXT	SEGMENT
_c$ = -8						; size = 4
_a$ = 8							; size = 4
_b$ = 12						; size = 4
_swap	PROC						; COMDAT; 4    : {
    00000	55		 push	 ebp00001	8b ec		 mov	 ebp, esp00003	81 ec cc 00 0000		 sub	 esp, 204		; 000000ccH00009	53		 push	 ebx0000a	56		 push	 esi0000b	57		 push	 edi0000c	8d bd 34 ff ffff		 lea	 edi, DWORD PTR [ebp-204]00012	b9 33 00 00 00	 mov	 ecx, 51			; 00000033H00017	b8 cc cc cc cc	 mov	 eax, -858993460		; ccccccccH0001c	f3 ab		 rep stosd; 5    :    int c;
; 6    :    c = *a; 0001e	8b 45 08	 mov	 eax, DWORD PTR _a$[ebp]00021	8b 08		 mov	 ecx, DWORD PTR [eax]00023	89 4d f8	 mov	 DWORD PTR _c$[ebp], ecx; 7    :    *a = *b;00026	8b 45 08	 mov	 eax, DWORD PTR _a$[ebp]00029	8b 4d 0c	 mov	 ecx, DWORD PTR _b$[ebp]0002c	8b 11		 mov	 edx, DWORD PTR [ecx]0002e	89 10		 mov	 DWORD PTR [eax], edx; 8    :    *b = c;00030	8b 45 0c	 mov	 eax, DWORD PTR _b$[ebp]00033	8b 4d f8	 mov	 ecx, DWORD PTR _c$[ebp]00036	89 08		 mov	 DWORD PTR [eax], ecx; 9    : }00038	5f		 pop	 edi00039	5e		 pop	 esi0003a	5b		 pop	 ebx0003b	8b e5		 mov	 esp, ebp0003d	5d		 pop	 ebp0003e	c3		 ret	 0
_swap	ENDP
_TEXT	ENDS
END