编译器为Visual Studio 2019
一维数组在汇编里看到其实就是简化版的声明n个变量而已,学过java的可以理解为一个语法糖?
int arr[5] = { 0,1,2,3,4 };
__asm{
mov dword ptr [ebp-3Ch],0
mov dword ptr [ebp-38h],1
mov dword ptr [ebp-34h],2
mov dword ptr [ebp-30h],3
mov dword ptr [ebp-2Ch],4
}
//等价于
int arr_0=0;
int arr_1=1;
int arr_2=2;
int arr_3=3;
int arr_4=4;
#include
void function() {
int x = 1;//mov dword ptr [ebp-8],1
int y = 2;//mov dword ptr [ebp-14h],2
int r;
int arr[5] = { 0,1,2,3,4 };
r = arr[1];
__asm{
mov eax,4
shl eax,0
mov ecx,dword ptr [ebp+eax-3Ch]
mov dword ptr [ebp-20h],ecx
}
r = arr[x];
__asm{
mov eax,dword ptr [ebp-8]
mov ecx,dword ptr [ebp+eax*4-3Ch]
mov dword ptr [ebp-20h],ecx
}
r = arr[x + y];
__asm{
mov eax,dword ptr [ebp-8]
add eax,dword ptr [ebp-14h]
mov ecx,dword ptr [ebp+eax*4-3Ch]
mov dword ptr [ebp-20h],ecx
}
r = arr[x * 2 + y];
__asm{
mov eax,dword ptr [ebp-8]
mov ecx,dword ptr [ebp-14h]
lea edx,[ecx+eax*2]
mov eax,dword ptr [ebp+edx*4-3Ch]
mov dword ptr [ebp-20h],eax
}
}
int main()
{
function();
return 0;
}
一个很显著的特征就是 [ e b p + e d x ∗ 4 − 3 C h ] [ebp+edx*4-3Ch] [ebp+edx∗4−3Ch],这个形式可能会绕晕?稍微调换一下位置就很明了了,像这样 [ e b p − 3 C h + e d x ∗ 4 ] [ebp-3Ch+edx*4] [ebp−3Ch+edx∗4], [ e b p − 3 C h ] [ebp-3Ch] [ebp−3Ch]是啥?不就是arr[0]的地址吗?至于为什么是乘以4,其实是因为这是一个int数组,如果换成short就是乘以2 [ e b p − 3 C h + e d x ∗ 2 ] [ebp-3Ch+edx*2] [ebp−3Ch+edx∗2],换成char就是乘以1 [ e b p − 3 C h + e d x ] [ebp-3Ch+edx] [ebp−3Ch+edx].
之所以开头说明了编译器是啥,是因为每个编译器在申请内存时申请的大小都不尽相同,当然不同的仅仅是大小而已,依然遵守原则的。之所以提到这一点,首先来看一个问题
char arr[3] = {1,2,3};与 char arr[4] = {1,2,3,4};哪个更节省空间,从反汇编的角度来说明你的观点
说到这个又不得不提一下本机尺寸,对应尺寸大小的数据在处理上会更快,例如32位机器在处理32位的数据时反而会比16位,8位的数据更快。编译器在内存大小和处理速度之间选择了处理速度。
接下来依然是一段实例代码
eg1:有3个char的数组arr1
#include
void function() {
char arr1[3] = { 0,1,2 };
}
int main()
{
function();
return 0;
}
eg2:有4个char的数组arr2
#include
void function() {
char arr2[4] = { 0,1,2,3 };
}
int main()
{
function();
return 0;
}
eg3:有5个char的数组arr3
#include
void function() {
char arr3[5] = { 0,1,2,3,4 };
}
int main()
{
function();
return 0;
}
可以看到eg1中的内存和eg2中的内存一样,而eg3中明明只是多一个char但是内存却多了一个DWORD(D0h-CCh=4h),这就是为了符合本机尺寸使得处理速度更快做出的调整。
那么问题的答案显而易见了,从反汇编的角度来看节省排名是arr2>arr1>arr3。
从汇编的角度来说,不管你是几维在汇编来看都是一维
int arr1[4] = { 0,1,2,3 };
__asm{
mov dword ptr [ebp-14h],0
mov dword ptr [ebp-10h],1
mov dword ptr [ebp-0Ch],2
mov dword ptr [ebp-8],3
}
int arr2[2][2] = { 4,5,6,7 };
__asm{
mov dword ptr [ebp-2Ch],4
mov dword ptr [ebp-28h],5
mov dword ptr [ebp-24h],6
mov dword ptr [ebp-20h],7
}
寻址方式就跟一维数组不太一样了
int arr1[4] = { 0,1,2,3 };
int n = arr1[3];
__asm{
mov eax,4
imul ecx,eax,3
mov edx,dword ptr [ebp+ecx-14h]
mov dword ptr [ebp-38h],edx
}
int arr2[2][2] = { 4,5,6,7 };
int m = arr2[1][0];
__asm{
mov eax,8---->2*4
shl eax,0
lea ecx,[ebp+eax-2Ch]
mov edx,4
imul eax,edx,0
mov ecx,dword ptr [ecx+eax]
mov dword ptr [ebp-44h],ecx
}
简单来说假设声明一个n维数组 a r r [ a 0 ] [ a 1 ] ⋅ ⋅ ⋅ [ a n ] arr[a_0][a_1]···[a_n] arr[a0][a1]⋅⋅⋅[an]
String str="int arr[a_0][a_1]···[a_n]";
String str="我们想要查找arr[m][m]···[m]的元素";
String str="定义一个mul(int num)运算符,表示a_(num)*a_(num+1)···*a_(n)"
那么寻址公式是这样的 ∑ x = 0 n m ∗ m u l ( x ) \sum_{x=0}^{n}{m*mul(x)} ∑x=0nm∗mul(x)