0
点赞
收藏
分享

微信扫一扫

从汇编看指针(待完善)

小暴龙要抱抱 2022-01-06 阅读 91
c语言

很多人在学习c语言的时候对指针的使用不太理解,我也一样,因此这篇文章主要总结我自己踩过的坑以及帮助没学过汇编的同学从汇编视角理解指针,我一直觉得只有从汇编视角看指针才是理解指针的最好方式。

知识点1:内存地址

内存是按字节编制的。32位即有2^32个寻址空间,每个地址空间含一个字节,最后也就是4GB大小。假设地址是在3位位模式(地址总线3根)下,地址即为000,001,010,011,100,101,110,111(只是例子,实际内存地址是16进制)。总共8个组合,也就是8个地址。每个地址上的字节是用两个16进制表示的,整好8位1个字节大小。

知识点2:大端小端
我们知道,在不同国家姓和名的顺序是不同的,比如在中国你叫张三,那在欧美国家人们往往称呼你为“三 张”。类似的案例出现在计算机中。小端序的意思即为低地址存放低位数据(以字节为单位,并不是完全颠倒的)。
在计算机中有大端小端两种存放数据的方式,以小端序为例,假设我们要在0000这个地址上存放一个int类型变量,变量值为4660(16进制表示为1234),然后我们要把这个变量存储在0000这个位置,那么接下来会怎么做呢?答:往上“爬格子”,从0000开始爬,0000将会存储60,而一个int需要两个地址也就是32位,还需要再往上爬一个地址,所以0001存储46。在计算机中呈现出来就是 60 46,这个变量的起始地址就是0000。如果此时我们取int的地址,这个地址就指向0000,将其赋给一个int型指针,这个指针就指向0000。在解引用读取的时候编译器会根据指针变量的类型自动帮你爬格子,比如int就爬两格,double爬四格(这也是导致了很多问题,比如指针强制造成的读越界,c语言中缺乏这样的保护机制)…

知识点3:副本

在函数main中调用另一个函数foo,b会在main中变成3吗?

int main()
{
	int b = 10;
	foo(b);
	return 0;
}
int foo(int a)
{
	a = 20;
	return 0;
}

答:不会,因为foo中的那个参数a实际是副本,对副本的修改不会影响main中的变量b。为什么呢?

我们用反汇编看一下,我挑选了关键的部分


00000000004004cd <foo>:
  4004cd:	55                   	push   %rbp
  4004ce:	48 89 e5             	mov    %rsp,%rbp
  4004d1:	89 7d ec             	mov    %edi,-0x14(%rbp)
  4004d4:	c7 45 fc 14 00 00 00 	movl   $0x14,-0x4(%rbp)
  4004db:	b8 00 00 00 00       	mov    $0x0,%eax
  4004e0:	5d                   	pop    %rbp
  4004e1:	c3                   	retq   

00000000004004e2 <main>:
  4004e2:	55                   	push   %rbp
  4004e3:	48 89 e5             	mov    %rsp,%rbp
  4004e6:	48 83 ec 10          	sub    $0x10,%rsp
  4004ea:	c7 45 fc 0a 00 00 00 	movl   $0xa,-0x4(%rbp)
  4004f1:	8b 45 fc             	mov    -0x4(%rbp),%eax
  4004f4:	89 c7                	mov    %eax,%edi
  4004f6:	e8 d2 ff ff ff       	callq  4004cd <foo>


call之前是main函数中的执行内容,注意%edi这个寄存器,在系统中参数传递往往通过寄存器传递,这几个寄存器是固定的,比如%edi的职责就是传第一个参数,请注意这个参数到foo中会发生什么。切换到foo中,可以看到edi把这个数值在栈上开了个空间传了进去,如果修改a,实际是修改当前函数栈上的-0x4(%rbp)这个空间的值,而不是我们保存在main栈上的int b。当函数返回时栈销毁,这块空间也没有意义。

解决办法:
既然修改的是副本,我们提供一种不访问副本,而是修改原来int的那个地址的办法就行了,这就是指针的作用。在参数中传int b的地址,然后在foo中解引用并赋值,此时赋给的就是int b中的那块地址,达到了修改int b的效果

举报

相关推荐

0 条评论