10.24

一.认识寄存器

集成在CPU上.效率高,造价高,所以比较小.一般只是暂时存放.

二.常用寄存器

1.eax ebx ecx edx

2.edi eci

3.ebp esp

三.函数栈帧

ebp 和esp这两个寄存器中存放的是地址,这两个地址是为了 维护函数栈帧的

每一个函数调用,都要在栈区开辟一块空间

栈顶指针-----esp

栈底指         针---ebp

 

在vs13中,main 函数也是被其他函数调用

 

这是vs2022下.调用main函数的函数

三.函数栈帧的创建与销毁过程

STEP1 main函数栈帧的创建

1.压栈

注意:从下往上是由高到低执行的

将ebp的地址放到调用main函数的地址上

2.move

将esp和ebp的地址指向ebp上

3.sub-减

0E4H是一个16进制数

就是将esp的地址减去0e4h指向ebp往上0e4h的空间

这一块空间就是为main函数预开辟好的空间.

4.三次push-ebx.esi.edi

1.ebx

每次开辟空间,esp都会指向最上面的

2.esi-edi

注意,esi,edi地址一样

5.lea

lea- load effecitve address-加载有效地址的意思

把后面那个地址加载到edi中那个地址其实就是04eh

6.move

ecx指的是次数

eax右边的指的是数值--c

后面跟的h表示16进制

把右边的值放到左边的寄存器中

7 rep stos

word表示两个字节..dword就是表示四个字节

意思从edi位置向下的空间四个字节的空间,全部放进0cccccch的数值

到ebp结束(ebp是栈底)

STEP 2 函数主体的实现

一个内存空间四个字节

1.int a = 10;

意思把0AH的值放进[ebp-8]

其实这里就可以解释了为什么局部变量不初始化就会出现烫烫烫..就是因为是随机值.比如cccccc

2.int b = 20;

意思把14H的值放进[ebp-14h] 换算成十进制就是20 也就是ebp-8向上2个内存单位(12个字节-)

 

3.int c =0;

STEP.3函数的传参与局部变量的建立

1.把[ebp-14h]放到eax里

也就是把20(b)的值放到eax里

同时栈顶寄存器esp到上面

2.eax压栈

 

3.把[ebp-8]的值放入ecx里

也就是(10)a的值

4.压栈

同时esp向上移动

 

 

以上四个动作是在传参

5.call

调用

 

 

回来的时候需要通过这个地址找回来

STEP.4 分函数的实现

1.push

将main函数的栈底寄存器ebp放在上面

 

 

 

2.move

将栈顶寄存器esp移到栈底寄存器ebp上

 

3.sub

将esp寄存器往上加0cch的地址 这一步是为分函数创建新的空间

 

 

4.三次push

 

 

与之前雷同

 

5.lea-mov-mov-rep stos

将[ebp+FFFFFF34H]的值放入edi中初始化并把它以下的空间全部初始化

 

STEP 5 分函数的主体

 

1.初始化z

 

 

2.传参

 

 

把之前创立好的临时参数使用相加放入a的地址[ebp-8];

 

3.返回值

 

这里可以解决一个疑问.就是z被返回的时候会随着函数调用的结束销毁,但是返回值是放在寄存器eax/

STEP 6 分函数的销毁

1.pop

 

栈顶的三个寄存器分别pop出去,同时esp往下加一位

2.move

分函数的空间回收,只要把ebp的值赋值给espesp就指回去了

 

 

3.ebp寄存器的弹出

         

回到原来main函数栈底

 

 

4.ret

同时esp正好就指向了上次调用函数的下一个地址

 

 

STEP 7主函数继续实现

1.销毁形参

 

 

 

2.传递返回值

 

之前函数的返回值放到了eax里

这里就是将eax的值传递到[ebp-20h]里 也就是c的地址

四.思考

 

五.举例

 

 

数组下标为10 的地址隔两个内存单元就到了i的地址,如果越界访问数组下标为12的,会导致12与i同时占用一个地址空间

就导致了死循环

 

六.良好的编程习惯

1.学会使用msdn.观察返回值,参数

 

2.学会使用const

        

 

const 可以修饰指针//

//const 放在*的左边(const int* p;)//

//const修饰的是*p,表示p指向的对象不能通过p来改变,但是p变量中的地址是可以改变的//

//const 放在*的右边(int* const p;)//

//const 修饰的是p,表示p的内容不能被改变,但是p指向的的对象是可以通过p来改变的

const修饰指针变量的时候:

  1. const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改

变。但是指针变量本身的内容可变。

2. const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指

针指向的内容,可以通过指针改变。

3.assert

 

如果表达式结果为假.会终止程序并且会展示哪一行出现什么错误

char* My_strcpy(char* arr2, const char* arr1)
{
assert(arr1&&arr2);
while(*arr2++ = *arr1++)
{
;
}
return arr2;
}
int main()//模拟实现strcpy函数,就是把一个字符串的内容放到另一个字符串上 并且包括\0
{
char arr1[]="abcd";
char arr2[5] = { 0 };
My_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}

 

size_t 是一些C/C++标准在stddef.h中定义的,size_t 类型表示C中任何对象所能达到的最大长度,它是无符号整数。

 

七.类型

类型的意义:

  1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。
  2. 如何看待内存空间的视角。
  3. 对于整形来说:数据存放内存中其实存放的是补码。

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统

一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

 

 

八.大小端

 

1.大端字节序存储

把一个数据低位字节处的数据存放在高低处,把高位字节处的数据存放在低地址处

2.小端字节序存储

把一个数据低位处的字节的数据放在低地址处\

 

3.为什么有大端和小端:

为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元

都对应着一个字节,一个字节为8 bit。但是在C语言中除了8 bit的char之外,还有16 bit的short

型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32

位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因

此就导致了大端存储模式和小端存储模式。

4.题目

int check()
{
int i = 1;//00000000000000000000000000000000001
          //0x 00 00 00 01
char* p = (char*)&i;
if (p)
{
return 0;
}
return 1;
}
int main()//判断大小端
{
if (check())
{
printf("大端");
}
else
{
printf("小端");
}
return 0;
}

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值