指针学习笔记

Posted Sunnix

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了指针学习笔记相关的知识,希望对你有一定的参考价值。

c语言的指针神秘而强大,刚开始接触的时候非常不能理解,随着对c语言、处理器和内存的慢慢熟悉,指针也是一点一点揭开神秘的面纱。现将一些个人的理解和思考,以笔记的形式保存下来,以便以后翻阅。

在我们编程的时候,申明一个变量,或者类的实例,都需要在内存中占用一定的空间,来保存这些数据。处理器要保存这些数据,首先需要一个地址,以便在再次用到这个变量的时候能读出保存在其中的值。就像人一样,假设你用一个仓库来存储东西,那么你将东西放置在仓库的某个地方以后,你就需要记住这个地址,以便在你重新用到这个东西的时候能准确无误的找到它。

那么假设我们在编程的时候有这样一条语句

char a=10;

那么处理器在内存中是如何保存这个变量呢,假设随便找个地址保存,通俗的来讲,应该是这样的


当然实际中应该是二进制的,那么0X 0000 0000 就是这个地址,和实际中某一栋楼的某个房间的门牌号是类似的,0X0A就是这个地址保存的内容,也就是现实中这个门牌号所对应的房间中存放的东西。

一个地址中可以保存8bit也就是一个字节的内容,所能保存的数据范围就是0--255,如果是有符号的,那就是-128--127。显然这个数据范围有点小,那么要保存一个int类型的数据就需要连续的四个地址。假设编程中有这样一条语句

int a=328; 通俗来讲,应该是这样的(将328写成16进制,按高地位写进相应的地址)


那么指针也是一样的,保存一个指针也需要4个字节的内存(各平台不同),就像现实中有一个门牌号,这个门牌号对应的房间中存放着相应的东西。只不过不同的类型,翻译的意义也就不一样,如果是int类型,那么房间的内容就翻译为值为xx的整数,如果是指针类型,那么房间的内容就翻译为一个地址。

假设这样一个程序:

#include <iostream>
using namespace std;
int main()
	
	int a = 10;
	int *p;
	p = &a;
	cout << p << endl;
	cout << &p << endl;
输出如下:

那么内存中的结构就是这样的


其实有时候指针难理解就是因为申明的时候带个 * ,如果把这个星号和基本类型连在一块就很好理解了,比如 int* p,p也是一个普通的变量,它的类型是int* ,它的内容中保存的是一个地址,这个地址指向一个变量,这个变量的类型是int。还有一个地方就是指针内容中保存的这个地址,其实说白了就是一个32bit(平台差异,有所不同)的二进制数据,如果把它翻译理解成为一个整数,就是0--(2^32)-1中任意一个,可以是10,也可以是100、456132,只要不超出数据范围就行。也可以在编程的时候强制数据转换,将一个地址转换为一个int类型的整数。假设有这样一段代码:

#include <iostream>
using namespace std;
int main()
	
	int a = 10;
	int b;
	int *p;
	b = (int)&a;
	cout << &a << endl;
	cout << b << endl;
	cout << *(int*)b<< endl;
输出是这样的:

第一行就是变量a的地址,0X 0036 FD00,在程序中我们将a的地址强制转换为int类型,赋值给b,输出b,也就是3603712,因为b是一个整型变量,所以输出的时候是10进制显示,将3603712写成16进制就是0X 0036 FD00,在最后一行中,又将b的数据类型强制转换为 int*(和int一样,int*也是一种数据类型),然后用间接引用运算符*,取出这个地址保存的内容,也就是a的值10。

有关指针有很重要的两个运算符,一个是地址运算符&和间接引用运算符*。&很好理解,就是取出变量在内存中的地址,间接引用运算符*取出一个地址中保存的内容,很像读操作。在嵌入式编程中,假设知道某一段flash的地址,就可以用*运算符读出其中的内容。

和指针密切相关的一种数据结构就是数组,平时用数组的时候可能还不会觉得,其实数组也是和地址密切相关的,只不过这些底层的操作已经很好的被封装了。假设有这样一条语句,int num[3]=1,2,3,那么就会在内存中开辟出连续12字节长度的内存,每四个字节保存一个int类型的变量。num就是这一段内存的首地址。假设有这样一段代码

#include <iostream>
using namespace std;
int main()
	
	int num[3] =  1, 2, 3;
	cout << num << endl;

输出是这样的

那么内存中的结构就是这样的

给0X 0032 FA60这个地址起个别名就叫做num,也就是数组名,也可以说数组名就是这段内存的首地址。假设我们进行num[2]运算,[ ]也是一种运算符,它会将这段首地址偏移n*sizeof(t)个字节,取出其中保存的内容,其中n就是[ ]运算符中的数,t就是数组的类型,如果是int,那么就是sizeof(int)。既然数组的名字就是数组的首地址,那么就可以将这个地址赋值给指针,接着对指针的操作和对数组的操作就是等效的。假设有这样一段代码

#include <iostream>
using namespace std;
int main()
	
	int num[3] =  1, 2, 3;
	int* p;
	p = num;
	cout << p[1] << endl;
	cout << *(num + 2) << endl;
将num赋值给指针p以后,对指针的操作和对数组名的操作是等效的。num是一个地址,那么对地址+2,这和普通的+运算符是不一样的,相当于普通的+运算符加n*sizeof(t),n就是+运算符后面的操作数,t就是数组的类型,本例中是int,等于是将num的地址偏移8,也就是到了上图中0X 0032 FA68这个位置,再用简介引用操作符*取出其中的内容,也就是3。其实就等效于num[2]。

以上是关于指针学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

[学习笔记]C语言中关于指针的详解1

C学习笔记 - 指针

Binder学习笔记—— 智能指针

Binder学习笔记—— 智能指针

Binder学习笔记—— 智能指针

《深入理解C指针》学习笔记--- 指针之外