在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. NLP常用开源/免费工具

    一些常见的NLP任务的开源/免费工具, *Computational Linguistics ToolboxCLT http://complingone.georgetown.edu/~linguis ...

  2. centos 安装jenkins

    1.Java jdk安装 安装之前先检查一下系统有没有自带open-jdk 命令: rpm -qa |grep java rpm -qa |grep jdk rpm -qa |grep gcj 如果没 ...

  3. iis 部署webapi常见错误及解决方案

    iis 部署webapi常见错误及解决方案 错误一: 原因:asp.net web api部署在Windows服务器上后,按照WebAPI定义的路由访问,老是出现404,但定义一个静态文件从站点访问, ...

  4. 网易对象存储NOS图床神器

    本文来自网易云社区 注:使用过程中有什么问题或建议欢迎到如下链接提交:http://note.youdao.com/noteshare?id=6321086fa4d7a6c003656354c1aa6 ...

  5. 4.Python的版本

    Python2: 英文支持没问题,中文报错,默认编码:ascii码 显示中午需要添加代码在首行:# -*- encoding:utf -8 -*- 用户交互  raw_input python2 里还 ...

  6. GEEK UNINSTALLER

    geek.exe https://geekuninstaller.com/download Event log message indicates that the Windows Installer ...

  7. selenium爬取qq空间,requests爬取雪球网数据

    一.爬取qq空间好友动态数据 # 爬取qq空间好友状态信息(说说,好友名称),并屏蔽广告 from selenium import webdriver from time import sleep f ...

  8. vue element-ui 表格筛选,点击筛选数据

    <el-table-column prop="productEnvVersion" label="运行环境" :filters="this.ru ...

  9. [ActionScript 3.0] 获取TextFiled字符边框

    /***** * getCharBoundaries () 方法 :返回一个矩形,该矩形是字符的边框. * getCharIndexAtPoint() 方法基于鼠标单击的 localX 和 local ...

  10. 网络CCNA基础了解

    关于网络 CCNA.CCNP.CCIE 中的 CCNA 一.逻辑与.或.非 AND --> "与"计算 1 AND 1 = 1(取严) 1 AND 0 = 0 0 AND 1 ...