a[i]==i[a]==*(i+a)==*(a+i)
在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个元素?"。无论对方说能与不能,我都很乐意进一步问"为什么能/不能?"从而挖掘出其对汇编及编译过程的理解深度。 通常,优秀的程序员能回答得富有计算机思维(即使他判定为不能, 比如"我觉得不能,编译器应该不支持这种怪诞的用法..."),而那些机械的程序员一般会选择放弃思考为什么能/不能。
随机推荐
- Android-SDCard外部存储文件读写
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses ...
- Windows安装mysql8.0
一.下载并解压 地址:https://dev.mysql.com/downloads/mysql/ 如下图: 下载解压后 二.创建my.ini文件 在D:\mysql\mysql-8.0.13-win ...
- Consul ACL
consul自带ACL控制功能,看了很多遍官方文档,没有配置步骤https://www.consul.io/docs/internals/acl.html 主要对各种配置参数解释,没有明确的步骤,当时 ...
- windows Win7如何设置多用户同时远程登录
windows Win7如何设置多用户同时远程登录 1.创建一个用户 密码永不过期 2.在本地组策略编辑器里面,依次展开计算机配置--->管理模板--->Windows组件---> ...
- OI计算几何 简单学习笔记
学习平面几何,首先我们要会熟练地应用向量,其次也要知道一些基本的几何知识.(其实看看数学课本就可以了吧) 因为是看的蓝书,所以很多东西做了引用.(update:还参考了赵和旭dalao的讲义) 下面先 ...
- Linux--多用户登录服务器端口抓包
以root身份登录1.新建用户组用命令groupadd test2.添加用户useradd -d /home/test/bei_1 -s /bin/sh -g test -m bei_1此命令新建了一 ...
- 在红帽RHEL7.0里配置网卡的四种方法
第一种方法 :采用vim编辑器来配置: 1. 如下图的步骤所示: 2. 输入这个命令后进行配置成下方图片里的内容: 3. 然后退出vim 编辑器,然后重新启动一下网络服务配置: 4.这些配置完后 ...
- 526. Beautiful Arrangement
Suppose you have N integers from 1 to N. We define a beautiful arrangement as an array that is const ...
- 吴裕雄 python 机器学习——高斯贝叶斯分类器GaussianNB
import matplotlib.pyplot as plt from sklearn import datasets,naive_bayes from sklearn.model_selectio ...
- form在模版中的渲 染方式
链接:https://www.jianshu.com/p/46b2aa2d5a23 form.as_p 渲染表单为一系列的p标签,每个p标签包含一个字段: <p> <label fo ...