VS2013-调试汇编代码-生成asm文件-结构体内存布局-函数参数压栈-调用约定

20 篇文章 0 订阅


  有时需要查看下底层汇编代码的编写,或者采用高级语言编写底层的汇编代码,这时可以采用C语言或者C++语言代写,然后查看生成的汇编代码。

1.生成asm文件

  项目->属性->c/c+±>输出文件->汇编程序输出
  在项目中会有生成后缀为*.asm 的文件。里面有注释,有利于程序分析。如下图所示:
在这里插入图片描述
  生成之后在Debug目录下有对应文件的汇编代码,如下图所示:
在这里插入图片描述

2.使用反汇编方式

  在调试模式下可以直接查看,点击调试->>>窗口->>反汇编 快捷键 CTRL+ALT+D。

3.结构体内存布局

  一、内存对齐的原因
  1.平台原因(移植原因):一些资料上是这样说的,“不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些特定地址处取某些特定的数据,否则就会抛出硬件异常”。也就是说在计算机在内存读取数据时,只能在规定的地址处读数据,而不是内存中任意地址都是可以读取的。
  2.效率原因:正是由于只能在特定的地址处读取数据,所以在访问一些数据时,对于访问未对齐的内存,处理器需要进行两次访问;而对于对齐的内存,只需要访问一次就可以。其实这是一种以空间换时间的做法,但这种做法是值得的。
  结构体内存对齐规则:
  1.第一个成员在结构体变量偏移量为0 的地址处,也就是第一个成员必须从头开始。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 为编译器默认的一个对齐数与该成员大小中的较小值。vs中默认值是8 Linux默认值为4(当然可以通过#pragma pack修改),但修改只能设置成1,2,4,8,16。
  3. 结构体总大小为最大对齐数的整数倍。(每个成员变量都有自己的对齐数)
  4.如果嵌套结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含嵌套结构体的对齐数)的整数倍。

struct struct1 {
	char c1;
	char c2;
	int i;
};

int main()
{
	struct1 sobj1;
	sobj1.c1 = 'a';
	sobj1.c2 = 'b';
	sobj1.i = 9;
	printf("%d", sizeof(struct1));
}

  c1是char型,占一个字节,第一个成员即 c1 在结构体变量偏移量为0 的地址处。
  c2是char型,占一个字节,要对齐到对齐数的整数倍的位置。对齐数 = 编译器默认的一个对齐数与该成员大小中的较小值,vs中默认值是8,取较小值1,char类型的对齐数是1,所以对齐到1 的整数倍,那就是偏移量为1开始的地址空间。
  i是int类型,占四个字节,要对齐到对齐数的整数倍的位置。int类型的对齐数就是 4,所以对齐到4 的整数倍。
在这里插入图片描述

4.调用约定与函数参数压栈

  VC 中默认调用是 __cdecl 方式,Windows API 使用 __stdcall 调用方式,在 DLL 导出函数中,为了跟 Windows API 保持一致,建议使用 __stdcall 方式。
  调用约定跟堆栈清除密切相关。如果写一个汇编函数,给 C/C++ 调用,在 __cdecl 方式下,则汇编函数无需清除堆栈,在 __stdcall 方式下,汇编函数需要在返回(RET)之前恢复堆栈。
  C 语言有 __cdecl、__stdcall、__fastcall、naked、__pascal。
  C++ 语言有 __cdecl、__stdcall、__fastcall、naked、__pascal、__thiscall,比 C 语言多出一种 __thiscall 调用方式。
  1、__cdecl
  __cdecl调用约定又称为 C 调用约定,是 C/C++ 语言缺省的调用约定。参数按照从右至左的方式入栈,函数本身不清理栈,此工作由调用者负责,返回值在EAX中。 由于由调用者清理栈,所以允许可变参数函数存在,如int sprintf(char* buffer,const char* format,…);。
  2、__stdcall
  __stdcall 很多时候被称为 pascal 调用约定。pascal 语言是早期很常见的一种教学用计算机程序设计语言,其语法严谨。参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在EAX中。
  3、__fastcall
  顾名思义,__fastcall 的特点就是快,因为它通过 CPU 寄存器来传递参数。他用 ECX 和 EDX 传送前两个双字(DWORD)或更小的参数,剩下的参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在 EAX 中。
  4、naked
  naked 是一个很少见的调用约定,一般不建议使用。编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回结果,此调用约定必须跟 __declspec 同时使用。例如定义一个求和程序,如__declspec(naked) int add(int a,int b);。
  5、__pascal
  这是 pascal 语言的调用约定,跟 __stdcall 一样,参数按照从右至左的方式入栈,函数自身清理堆栈,返回值在EAX中。VC 中已经废弃了这种调用方式,因此在写 VC 程序时,建议使用 __stdcall 代替。
  6、__thiscall
  这是 C++ 语言特有的一种调用方式,用于类成员函数的调用约定。如果参数确定,this 指针存放于 ECX 寄存器,函数自身清理堆栈;如果参数不确定,this指针在所有参数入栈后再入栈,调用者清理栈。__thiscall 不是关键字,程序员不能使用。参数按照从右至左的方式入栈。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值