c++ ,asm一些无聊的事情

本文探讨了编译器处理字符串字面量的方式、使用非法指针调用成员函数的现象、函数内部的调试编译行为、函数调用的具体过程等主题。通过对实际案例的汇编级分析,揭示了编译器在不同场景下的工作原理。

参考,http://blog.youkuaiyun.com/feixiaoxing/article/details/6751534


1.编译器在翻译一些语句是,和想象的不同

    char a[]="hello";
00E535B8  mov         eax,dword ptr [string "hello" (0E55868h)] 
00E535BD  mov         dword ptr [ebp-10h],eax 
00E535C0  mov         cx,word ptr ds:[0E5586Ch] 
00E535C7  mov         word ptr [ebp-0Ch],cx 
    char b[]="12345678";
00E535CB  mov         eax,dword ptr [string "12345678901" (0E55874h)] 
00E535D0  mov         dword ptr [ebp-24h],eax 
00E535D3  mov         ecx,dword ptr ds:[0E55878h] 
00E535D9  mov         dword ptr [ebp-20h],ecx 
00E535DC  mov         dl,byte ptr ds:[0E5587Ch] 
00E535E2  mov         byte ptr [ebp-1Ch],dl 
    char d[]="123456789012345678901234567890";
00E535E5  mov         ecx,7 
00E535EA  mov         esi,offset string "123456789012345678901234567890" (0E55AC8h) 
00E535EF  lea         edi,[ebp-4Ch] 
00E535F2  rep movs    dword ptr es:[edi],dword ptr [esi] 
00E535F4  movs        word ptr es:[edi],word ptr [esi] 
00E535F6  movs        byte ptr es:[edi],byte ptr [esi] 

第一句,6字节,拆分为,一个dword,一个word;

第二句,9字节,拆分为,两个dword,一个byte;

第三句,31字节,拆分为,7个dword,一个word,一个byte

也就是能凑成dword的,都用dword操作。


2.用非法指针调用成员函数

#include <iostream>

using namespace std;

class Apple
{
public:
	void print()
	{
		cout<<"NULL->print()"<<endl;
		return;
	}
};
void test2()
{
	((Apple*)0)->print();
}

int main()
{
	test2();
	return 0;
}

上面的代码是可以正常运行的,输出
NULL->print()


3.函数内部

debug编译运行的代码中,函数开头,总会有这样的汇编片段

void test()
{
00E014C0  push        ebp    
00E014C1  mov         ebp,esp 
00E014C3  sub         esp,110h
00E014C9  push        ebx  
00E014CA  push        esi  
00E014CB  push        edi  
00E014CC  lea         edi,[ebp-110h] 
00E014D2  mov         ecx,44h 
00E014D7  mov         eax,0CCCCCCCCh 
00E014DC  rep stos    dword ptr es:[edi] 
...
这两句,

sub  esp,110h

lea  edi,[ebp-110h]

中的数字一般相同。

如果我没猜错的话,应该是为了使未初始化的栈内存空间填充为OxCCCC。

另外,110h=44h*4,可能是函数局部变量空间的大小,也就是该函数在栈上所占的空间(例子中还有16字节用来保存寄存器,开始4字节,结尾12字节,中间夹着110字节)。

函数结束,汇编片段

}
00AC1703  pop         edi  
00AC1704  pop         esi  
00AC1705  pop         ebx  
00AC1706  mov         esp,ebp 
00AC1708  pop         ebp  
00AC1709  ret        

4.函数调用

void test5(int a, int b)
{
	int c=a+b;
}
int test6(int a, int b)
{
	int c=a+b;
	return c;
}
调用片段

	test5(1,2);
00191BD8  push        2    
00191BDA  push        1    
00191BDC  call        test5 (191271h) 
00191BE1  add         esp,8 
	a=test6(3,4);
00191BE4  push        4    
00191BE6  push        3    
00191BE8  call        test6 (19126Ch) 
00191BED  add         esp,8 
00191BF0  mov         dword ptr [a],eax 
test6的完整翻译

int test6(int a, int b)
{
00191750  push        ebp  
00191751  mov         ebp,esp 
00191753  sub         esp,0CCh 
00191759  push        ebx  
0019175A  push        esi  
0019175B  push        edi  
0019175C  lea         edi,[ebp-0CCh] 
00191762  mov         ecx,33h 
00191767  mov         eax,0CCCCCCCCh 
0019176C  rep stos    dword ptr es:[edi] 
	int c=a+b;
0019176E  mov         eax,dword ptr [a] 
00191771  add         eax,dword ptr [b] 
00191774  mov         dword ptr [c],eax 
	return c;
00191777  mov         eax,dword ptr [c] 
}

5.未完待续



### 解释 `asm("nop");` #### 1. **什么是内联汇编?** 在 C 或 C++ 中,可以通过 `asm`(或 `_asm` 在某些编译器中)关键字嵌入汇编语言代码。这种方式称为**内联汇编**,它允许开发者直接插入低级机器指令来控制硬件资源或优化性能。 例如,在 GNU GCC 编译器中,可以使用如下语法: ```c asm("assembly instruction"); ``` 这里的 `"assembly instruction"` 是具体的汇编指令字符串。 --- #### 2. **`nop` 指令的作用** `nop` 是 "No Operation" 的缩写,意为空操作。它是几乎所有 CPU 架构都支持的一条简单指令,其作用是什么都不做,仅仅消耗一个时钟周期(具体取决于架构)。常见的用法包括: - **占位符**:用来填充代码段以便对齐内存地址。 - **延迟**:通过执行多个 `nop` 来引入短时间的延迟。 - **调试工具**:帮助分析程序的行为,尤其是在单步调试过程中。 - **防止优化**:有时编译器会删除看似无意义的代码片段;而插入 `nop` 可以阻止这种情况发生。 --- #### 3. **具体例子** 下面是一个包含 `asm("nop")` 的完整代码示例: ```c #include <stdio.h> int main() { printf("Before NOP\n"); asm("nop"); // 插入一条空操作 printf("After NOP\n"); return 0; } ``` 在这个例子中,当程序运行到 `asm("nop")` 这一行时,CPU 实际上并不会做任何事情,但它确实占用了一个指令周期。这通常不会对外部功能产生影响,但在某些情况下可能会有细微的时间差异。 --- #### 4. **适用场景** ##### (1)**代码对齐** 在嵌入式开发中,有时候需要确保某段代码从特定边界开始执行(如 4 字节对齐),此时可以在必要位置添加若干个 `nop` 指令作为填充物。 ##### (2)**生成固定延时** 对于实时性要求较高的系统,可能需要用精确的方式制造短暂等待时间。尽管现代方法更倾向于利用定时器外设完成任务,但对于极其简短且可预见间隔的需求而言,连串排列多条 `nop` 仍不失为一种可行策略。 ##### (3)**抑制过度优化** 一些复杂的算法如果完全依赖高级别描述语言表达出来的话容易受到编译期变换干扰导致最终效果偏离预期设定轨迹——这时候手动加入少量诸如本案例所示类型的原生命令序列便能有效避免类似问题出现。 --- #### 5. **注意事项** 虽然 `asm("nop")` 看起来很简单,但也需要注意以下几点: - 平台相关性:不同体系结构下的汇编语法有所不同,请确保你的指令适合目标平台。 - 性能开销:尽管每个单独的 `nop` 开支很小,但如果滥用大量此类冗余操作则会对整体效率造成负面影响。 - 维护难度提升:过多地混杂高级别的 C/C++ 和底层汇编会使源文件变得更加难以阅读理解以及后期维护更新困难加剧等问题存在风险。 因此除非绝对必要,否则尽量减少直接编写汇编代码的机会。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值