在C语言中,如果我们要访问一个数组的某个下标对应的元素,通常的写法是a[i]。但从汇编的角度看,写成i[a]一点问题都没有。

下面通过代码给出证明。

o foo1.c

 int main(int argc, char *argv[])
{
unsigned int a[] = {, , };
unsigned int n = sizeof (a) / sizeof (int); unsigned int sum = ;
for (unsigned int i = ; i < n; i++)
sum += a[i]; return sum;
}

o foo2.c

 int main(int argc, char *argv[])
{
unsigned int a[] = {, , };
unsigned int n = sizeof (a) / sizeof (int); unsigned int sum = ;
for (unsigned int i = ; i < n; i++)
sum += i[a]; return sum;
}

o foo3.c

 int main(int argc, char *argv[])
{
unsigned int a[] = {, , };
unsigned int n = sizeof (a) / sizeof (int); unsigned int sum = ;
for (unsigned int i = ; i < n; i++)
sum += *(i+a); return sum;
}

o 编译和运行

 $ gcc -g -Wall -std=gnu99 -m32 -o foo1 foo1.c
$ gcc -g -Wall -std=gnu99 -m32 -o foo2 foo2.c
$ gcc -g -Wall -std=gnu99 -m32 -o foo3 foo3.c
$ ./foo1; echo $? $ ./foo2; echo $? $ ./foo3; echo $?

o 反汇编后diff

1) foo1.gdb.out

 (gdb) disas /m main
Dump of assembler code for function main:
{
0x080483ed <+>: push ebp
0x080483ee <+>: mov ebp,esp
0x080483f0 <+>: sub esp,0x20 unsigned int a[] = {, , };
0x080483f3 <+>: mov DWORD PTR [ebp-0xc],0x1
0x080483fa <+>: mov DWORD PTR [ebp-0x8],0x2
0x08048401 <+>: mov DWORD PTR [ebp-0x4],0x3 unsigned int n = sizeof (a) / sizeof (int);
0x08048408 <+>: mov DWORD PTR [ebp-0x10],0x3 unsigned int sum = ;
0x0804840f <+>: mov DWORD PTR [ebp-0x18],0x0 for (unsigned int i = ; i < n; i++)
0x08048416 <+>: mov DWORD PTR [ebp-0x14],0x0
0x0804841d <+>: jmp 0x804842d <main+>
0x08048429 <+>: add DWORD PTR [ebp-0x14],0x1
0x0804842d <+>: mov eax,DWORD PTR [ebp-0x14]
0x08048430 <+>: cmp eax,DWORD PTR [ebp-0x10]
0x08048433 <+>: jb 0x804841f <main+> sum += a[i];
0x0804841f <+>: mov eax,DWORD PTR [ebp-0x14]
0x08048422 <+>: mov eax,DWORD PTR [ebp+eax*-0xc]
0x08048426 <+>: add DWORD PTR [ebp-0x18],eax return sum;
0x08048435 <+>: mov eax,DWORD PTR [ebp-0x18] }
0x08048438 <+>: leave
0x08048439 <+>: ret End of assembler dump.

2) foo2.gdb.out

 (gdb) disas /m main
Dump of assembler code for function main:
{
0x080483ed <+>: push ebp
0x080483ee <+>: mov ebp,esp
0x080483f0 <+>: sub esp,0x20 unsigned int a[] = {, , };
0x080483f3 <+>: mov DWORD PTR [ebp-0xc],0x1
0x080483fa <+>: mov DWORD PTR [ebp-0x8],0x2
0x08048401 <+>: mov DWORD PTR [ebp-0x4],0x3 unsigned int n = sizeof (a) / sizeof (int);
0x08048408 <+>: mov DWORD PTR [ebp-0x10],0x3 unsigned int sum = ;
0x0804840f <+>: mov DWORD PTR [ebp-0x18],0x0 for (unsigned int i = ; i < n; i++)
0x08048416 <+>: mov DWORD PTR [ebp-0x14],0x0
0x0804841d <+>: jmp 0x804842d <main+>
0x08048429 <+>: add DWORD PTR [ebp-0x14],0x1
0x0804842d <+>: mov eax,DWORD PTR [ebp-0x14]
0x08048430 <+>: cmp eax,DWORD PTR [ebp-0x10]
0x08048433 <+>: jb 0x804841f <main+> sum += i[a];
0x0804841f <+>: mov eax,DWORD PTR [ebp-0x14]
0x08048422 <+>: mov eax,DWORD PTR [ebp+eax*-0xc]
0x08048426 <+>: add DWORD PTR [ebp-0x18],eax return sum;
0x08048435 <+>: mov eax,DWORD PTR [ebp-0x18] }
0x08048438 <+>: leave
0x08048439 <+>: ret End of assembler dump.

3) foo3.gdb.out

 (gdb) disas /m main
Dump of assembler code for function main:
{
0x080483ed <+>: push ebp
0x080483ee <+>: mov ebp,esp
0x080483f0 <+>: sub esp,0x20 unsigned int a[] = {, , };
0x080483f3 <+>: mov DWORD PTR [ebp-0xc],0x1
0x080483fa <+>: mov DWORD PTR [ebp-0x8],0x2
0x08048401 <+>: mov DWORD PTR [ebp-0x4],0x3 unsigned int n = sizeof (a) / sizeof (int);
0x08048408 <+>: mov DWORD PTR [ebp-0x10],0x3 unsigned int sum = ;
0x0804840f <+>: mov DWORD PTR [ebp-0x18],0x0 for (unsigned int i = ; i < n; i++)
0x08048416 <+>: mov DWORD PTR [ebp-0x14],0x0
0x0804841d <+>: jmp 0x8048437 <main+>
0x08048433 <+>: add DWORD PTR [ebp-0x14],0x1
0x08048437 <+>: mov eax,DWORD PTR [ebp-0x14]
0x0804843a <+>: cmp eax,DWORD PTR [ebp-0x10]
0x0804843d <+>: jb 0x804841f <main+> sum += *(i+a);
0x0804841f <+>: mov eax,DWORD PTR [ebp-0x14]
0x08048422 <+>: lea edx,[eax*+0x0]
0x08048429 <+>: lea eax,[ebp-0xc]
0x0804842c <+>: add eax,edx
0x0804842e <+>: mov eax,DWORD PTR [eax]
0x08048430 <+>: add DWORD PTR [ebp-0x18],eax return sum;
0x0804843f <+>: mov eax,DWORD PTR [ebp-0x18] }
0x08048442 <+>: leave
0x08048443 <+>: ret End of assembler dump.

4) a[i] v.s. i[a]

5) i[a] v.s. *(i+a)

结论: a[i]==i[a]==*(i+a)==*(a+i)

分析: 在编译器的眼里,数组名a不过是一段连续内存的首地址。获取某个元素a[i]不过是在a对应的首地址上做偏移,找到对应的内存地址后从中取出其中的内容即可。

PS: 我在面试别人的过程中,如果求职的工程师说他懂汇编,我一般会问这样的问题,"能否在C代码中使用i[a]去访问数组a的第i个元素?"。无论对方说能与不能,我都很乐意进一步问"为什么能/不能?"从而挖掘出其对汇编及编译过程的理解深度。 通常,优秀的程序员能回答得富有计算机思维(即使他判定为不能, 比如"我觉得不能,编译器应该不支持这种怪诞的用法..."),而那些机械的程序员一般会选择放弃思考为什么能/不能。

随机推荐

  1. 疑难杂症--单回话下 WITH(NOLOCK)返回更多数据

    ​场景:某DBA在一个人操作数据库时发现,可提交读事务隔离级别下返回的数据少于未提交读事务隔离级别,确认没有其他事务修改数据. 解决方案1: 将数据查询放入一个新建的表,使用该表查询发现问题被消除. ...

  2. json 登陆协议分析

    登录方式有两种:1)用户名密码登陆,code 为 5401 (2) IMSI和TOKEN 登陆, code 为93 POST /tcpbus/mobile HTTP/1.1Host: clientac ...

  3. ASP.NET MVC 全局过滤器(FilterConfig)、标记在控制器上和方法上的筛选器执行顺序

    FilterConfig->控制器上的筛选器-->方法上的筛选器(大-->小,上-->下) 全局-->控制器->个别 尝试的时候记得把返回true protecte ...

  4. C# DataGridView添加右键菜单的简单应用

    首先,参考了下以下文章: https://blog.csdn.net/qin_zhangyongheng/article/details/23773757 感谢. 项目中要在DataGridView中 ...

  5. C# try catch finally

    抛出异常开销非常大(相对而言),所以不要过多的在程序中使用它们 既然finally一定是要执行的,即使try块中有return

  6. Dubbo与Hadoop RPC的区别

    本文来自网易云社区 RPC(Remote Procedure Call,远程过程调用)是一个计算机通信协议.该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编 ...

  7. 201621123012《Java程序设计》第12次学习总结

    作业12-流与文件 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 面向系统综合设计-图书馆管理系统或购物车 使用流与文件改造你的图书馆管理系统或购物车 ...

  8. Delphi XE8帮助中的REST相关内容。

    Delphi XE8的离线帮助是我见过的最好的Delphi帮助文档了,内容相当详细和丰富,几乎涵盖了Delphi的方方面面!! Delphi XE8的帮助文档在哪里?“XE8安装目录\Help\Doc ...

  9. spring里面的ioc的理解?

    spring里面的ioc就是控制反转,其实现核心是DI(依赖注入),控制反转不向以前java代码里面,通过new关键字来实现创建对象,这样每段代码之间的耦合度就比较高,为了降低每个小模块之间的耦合度, ...

  10. java FastJSON的使用

    1.JSON介绍 JSON(javaScript Object Notation)是一种轻量级的数据交换格式.主要采用键值对({"name": "json"}) ...