浅谈指针的偏移

记得当初学习指针的时候,总是把指针和地址混为一谈,总以为说到指针就是指某个地址而已,后来加强对各类指针的认识以后,才认识到指针不止是简单的地址。

指针是一种类型,通过类型可以声明一个变量并保存一个内存地址,不同类型的指针对保存的地址会有不同的解释,编译器根据指针的类型(对应的偏移量)解引用出相应的数据。

首先在32位程序设计里,指针大小为4bytes,满足2^32 寻址范围。

到底偏移多少:

曾经探究过一个问题,代码如下:

	int a[4][2] = { 0, 1, 2, 3, 4, 5 ,7,8 };
	printf("a = %p,a + 1 = %p\n", a, a + 1);
	printf("range = %d\n", a + 1 - a);

运行结果:

a = 00E7FC58, a + 1 = 00E7FC60

range = 1

问题来了,为何a + 1 - a 不等于8,等于1呢?

汇编代码:

	printf("range = %d\n", a + 1 - a);
00035DDF  lea         eax,[ebp-20h]
00035DE2  lea         ecx,[a]
00035DE5  sub         eax,ecx
00035DE7  sar         eax,3
00035DEA  mov         esi,esp
00035DEC  push        eax
00035DED  push        3DA54h
00035DF2  call        dword ptr ds:[41204h]  

即,编译器对减去过后的eax进行处理,eax = 8 ,sar eax,3  后eax右移三位编程最后结果1。

思考:

对于指针的操作,编译器是不是早已内嵌完成一套偏移运算呢?

我们来看以下代码:

	int a[4][2] = { 0, 1, 2, 3, 4, 5 ,7,8 };
	printf("*(*(a + 0) + 0) =  %d\n", *(*(a + 0) + 0));
	printf("*(*(a + 1) + 0) =  %d\n", *(*(a + 1) + 0));
	printf("*(*(a + 1) + 1) =  %d\n", *(*(a + 1) + 1));

输出结果很简单,但是在指针解引用编译器如何处理呢?

	printf("*(*(a + 0) + 0) =  %d\n", *(*(a + 0) + 0));
00DF5DC5  push        eax
	printf("*(*(a + 1) + 0) =  %d\n", *(*(a + 1) + 0));
00DF5DDB  mov         esi,esp
00DF5DDD  mov         eax,dword ptr [ebp-20h]
	printf("*(*(a + 1) + 1) =  %d\n", *(*(a + 1) + 1));
00DF5DF6  mov         esi,esp
00DF5DF8  mov         eax,dword ptr [ebp-1Ch]  

可见,编译器自动找到了相应的地址并取出了我们需要的元素,内嵌完成了一套寻址的偏移运算。

探究:

以下代码偏移规则是怎样的?

	int a[4][2] = { 0, 1, 2, 3, 4, 5 ,7,8 };
	printf("*(a + 0) + 0 =  %p\n", *(a + 0) + 0);
	printf("*(a + 1) + 0 =  %p\n", *(a + 1) + 0);
	printf("*(a + 1) + 1 =  %p\n", *(a + 1) + 1);

结果:

*(a + 0) + 0 =  0060FE1C

*(a + 1) + 0 =  0060FE24

*(a + 1) + 1 =  0060FE28

实质上 *(a ) + m是一个 int* 类型,所以每加1偏移也就是 4bytes。

再来看:

	int a[4][2] = { 0, 1, 2, 3, 4, 5 ,7,8 };
	printf("a =  %p\n", a);
	printf("a + 1 =  %p\n", a + 1);

结果:

a =  00C7FEA0

a + 1 =  00C7FEA8

实质上a 是一个 int (*)[2]指针,偏移就是 2 * 4bytes。

再来看:

	int a[4][2] = { 0, 1, 2, 3, 4, 5 ,7,8 };
	printf("&a =  %p\n", &a);
	printf("&a + 1 =  %p\n", &a + 1);

结果:

&a =  00C2F96C

&a + 1 =  00C2F98C

实质上&a是一个 int (*)[4][2]指针,&a + 1也就偏移了32bytes,跨越了一个a[4][2]的长度,

总结:

我在想,用什么样的方式才能很好解释指针偏移这个问题,想到一点就是:你需要判断这个指针到底是一个什么类型,我们通过指针类型就可以轻松算出偏移大小。

时间: 2024-10-06 00:11:14

浅谈指针的偏移的相关文章

浅谈指针的比较

一.前言 有人说指针是C语言的灵魂,也有人说没学好指针就等于不会C语言. 虽然在现代C++中一般都是推荐尽量避免使用原生的raw指针,而是以smart pointer 和reference替代之.但是无论怎样,对于C/C++来说,指针始终是个绕不过去的坎.究其原因,是因为C/C++都是支持面向底层操作的语言,而面向底层操作就得能操纵内存,这个时候就需要指针了.为什么呢?个人觉得指针实际上就是对机器语言/ASM中的通过虚拟地址操作内存的这一行为的一种抽象. 例如 movl %eax, (%edx)

浅谈指针

指针:一个用来存储数据存储地址的变量. int a=10;  a变量里面存储的数据0 1按照int 的长度解析为数据 int *p=&a; p变量里面存储的数据0 1解析为地址,只是这个地址(这个地址是这段空间的首地址)标号的那段空间里面存储的是a的值,这里的p是给这段地址的取的名字用来给程序员看的,这里的a是给这段地址里面存储内容所取得名字来给程序员看的.

【C语言】 浅谈指针

指针是就是地址,是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址. 要搞清一个指针需要搞清指针的四方面的内容:指针的类型.指针所指向的 类型.指针的值或者叫指针所指向的内存区.指针本身所占据的内存区.让我们分别说明. 首先,先罗列出几种常见的类型: int p;   //这是一个普通的整型变量int *p;  //首先从P处开始,先与*结合,所以说明P是一个指针,然后再与int结合,说明指针所指向的内容的类型为int型.所以P是一个返回整形数据的指针 int p[3];  //首先从

浅谈指针和引用

我们知道用指针和引用来定义函数形参的时候,都可以直接改变参的值.那么指针和引用有哪些区别呢? 我们先根据引用和指针的定义展开:引用是某个变量或者对象的别名,而指针则存储的是一个机器码地址,这个地址是某个具体变量或者对象的地址.因此区别有: 1)指针可以为空,但是引用不行 2)声明指针可以不指向任何对象,因此使用指针之前必须做判空操作,而引用则不必 3)引用一旦声明后,就不可以改变指向:但是指针可以,如++操作符,指针则指向下一个对象,而引用则改变的是指向对象的内容 4)引用的大小是所指变量的大小

Go浅谈指针

一:指针的定义 简单来讲,指针就是存放变量的地址,通过"*"号来读取地址里面存放的值 二:举例说明 由上图可知,由于指针指向的是内存地址,即变量存储值得真实地址,所以修改指针,即修改原变量数值. 原文地址:https://www.cnblogs.com/louis181214/p/10197785.html

浅谈RAII&智能指针

关于RAII,官方给出的解释是这样的"资源获取就是初始化".听起来貌似不是很懂的哈,其实说的通俗点的话就是它是一种管理资源,避免内存泄漏的一种方法.它可以保证在各种情况下,当你对对象进行使用时先通过构造函数来进行资源的分配和初始化,最后通过析构函数来进行清理,有效的保证了资源的正确分配和释放.(特别是在异常中,因为异常往往会改变代码正确的执行顺序,这就很容易引起资源管理的混乱和内存的泄漏) 其中智能指针就是RAII的一种实现模式,所谓的智能就是它可以自动化的来管理它所指向那份空间的资源

转: 浅谈C/C++中的指针和数组(二)

转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html 浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组的一些区别,然而在某些情况下,指针和数组是等同的,下面讨论一下什么时候指针和数组是相同的. C语言标准对此作了说明: 规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针: 注:下面几种情况例外 1)数组名作为sizeof的操作数 2)使用&取数组的地址 规则2:下标总是与指针的偏移量

转:浅谈C/C++中的指针和数组(一)

转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242138.html 浅谈C/C++中的指针和数组(一) 指针是C/C++的精华,而指针和数组又是一对欢喜冤家,很多时候我们并不能很好的区分指针和数组,对于刚毕业的计算机系的本科生很少有人能够熟练掌握指针以及数组的用法和区别.造成这种原因可能跟现在大学教学以及现在市面上流行的很多C或者C++教程有关,这些教程虽然通俗易懂,但是在很多关键性的地方却避而不谈或者根本阐述不清楚,甚至很

浅谈为什么只有指针能够完成多态及动态转型的一个误区

c++多态由一个函数地址数组Vtable和一个指向Vtable的指针vptr实现. 具体来说,类拥有自己的vtable,类的vtable在编译时刻完成. 每个对象有自己的vptr指针,该指针初始化时指向对象所实现的类的vtable. 关于向上转型的误区: 通常对于向上转型的理解是这样的,当子类对象向上转型(允许隐式)成父类对象时,实际上只是将子类对象暂时看做父类对象,内部的数据并未改变. 对于没有虚函数的对象,这句话是正确的,但是,当引入虚函数后,这样的理解是有问题的,实际上,向上转型的过程中,