有很多人被指针应当如何理解给难住了,其实单说理解指针,并不多难
我将尽可能简略但易懂地解释指针,希望对你有所帮助
I.理解C语言中的存储
C语言中,最小的单位是bit(比特),它用来储存0或1。
因为电极有正负两极,所以以0、1为主体的二进制就成为不二之选。
而8个比特位就是一个字节(1 byte)。
0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 |
当我们说将数据存到一个地方时
其实它所存的地方都有一个编号,用来表示这数据存在哪
一串串的数据储存起来
就跟一栋楼一样一层层的
我们说的电脑32位,就是说电脑的地址线有32根
再加上二进制
说明内存能存的数据组成数量有个
64位同理
II.地址
如上所述,每一个数据都有它的地址
这个地址要用来查找数据在哪
这样以后你要用到数据时
直接通过地址来调取数据就好了
int main()
{
int a = 10;
&a;//按F10,在调试、窗口、监视里可以找到&a的地址
//a在此处的地址是0x0000000000000004。为什么有0x呢?这是十六进制的标志。
return 0;
}
这一串代码是在干什么?
int是在向内存申请一块空间用来存放a的数据
而&就是告知地址
按住F10
在逐步找到监视之后
你可以清楚地发现a的地址&a是多少
在这里a的地址是0x0000000000000004
0x是表明后面的数字是以16进制的形式出现的
0000000000000004这一串就是告知a的数据储存在哪里
储存的地方就是一个叫0000000000000004的地方
相信你也知道地址是怎么回事了
那这和指针有什么关系呢?
III.指针的实质
数据是需要一个地址来指向它的
这样计算机就知道怎么调用这个数据了
但问题是我们人要获取这个地址还是麻烦些的
于是就想到可 以用一个“盒子”来储存这个地址
以后程序员只要找到“盒子”
就也可以反向推出地址,进而推到数据
这个“盒子”就是指针
你也可以理解成它是地址的地址
那它是如何工作的?
#include <stdio.h>
int main()
{
int a = 0;
int* p = &a;//*是声明变量是指针,用指针来储存a变量的地址
*p = 20;//这里的*相当于侦探,照着地址反向追踪。其表达的是指针p所指的对象是多少,这里也就是将a改为了20。
printf("%d\n", a);
return 0;
}
我们可以看到
我们先用int为a申请了一个空间,并将a初始化为0
紧接着我们将a的地址,也就是&a
存储到了指针p里面
而int*则是表明后面的p是个指针,而不是普通的变量名称
由此,我们创立了个指针p
它储存了&a
而下一行的*p则是表示指针p所指向的对象
由于指针p存的是&a
自然我们顺着地址回去找到的对象就是a
*p = 20;其实就变身为了a = 20;
因此最后输出的结果就是20,而不是0
IV.由地址找数据
指针类型也是有大小的
看看这一段代码:
#include <stdio.h>
//int main()
//{
// printf("%zu\n", sizeof(char*));//%zu是专门打印sizeof的结果的。
// printf("%zu\n", sizeof(short*));
// printf("%zu\n", sizeof(int*));
// printf("%zu\n", sizeof(long*));//在64位系统上,指针变量都是8字节;32位系统上,指针变量都是4字节。
// return 0;
//}
最后输出的结果要么都是4,要么都是8,这是为什么?
还记得前面的a的地址吗?
0x0000000000000004
我们再来审视一下它
0x是16进制的标识
我们就把它先去掉不记
剩下的就是0000000000000004
那为什么是这样?
int main()
{
int a = 10;
&a;//按F10,在调试、窗口、监视里可以找到&a的地址
//a在此处的地址是0x0000000000000004。为什么有0x呢?这是十六进制的标志。
return 0;
}
这就要回到这个代码
让我们想想int占4个字节
我们存储自然就是32个bit位
0000 0000 0000 0000 0000 0000 0000 0000
我们的a的值是10
那么按照二进制,就是1010
储存在这64位中就会改变成
0000 0000 0000 0000 0000 0000 0000 1010
那二进制要转成十六进制呢?十六进制是0~9、a、b、c、d、e
不用10、11之类的是因为10其实和之前的0、1重合了
所以用a、b......来代替
那转成十六进制,自然就是
0 0 0 0 0 0 0 a
也就是00 00 00 0a
那这是什么东西呢?
自然就是我们储存的数据
这好像和0x0000000000000004没有关系
因为0x0000000000000004是个假地址!
是的,由于程序没有真正运行,所以它的地址是虚假的
要获得一个真地址怎么办?
#include <stdio.h>
int main()
{
int a = 0;
a = 10;
printf("%p\n", &a);//打印地址用%p,而且一定是&a而不是a,因为&才告诉地址。
return 0;
}
在printf那行设置断点再调试
用鼠标点击该行左侧的灰色边栏(行号旁边),会出现一个红色圆点,表示断点已设置。
断点之后调试
得到了真实地址0x000000FC00BFF914
再将真实地址输入到内存窗口
选择列为4
你发现地址后面跟着的就是0a 00 00 00
眼不眼熟?
是的,这就我们推理出来的00 00 00 0a倒序的样子
倒序是因为在内存中存储是小端序(低字节在前)
也就是说这段0a 00 00 00
就是我们储存的a = 10
这也就是数据
我们至此通过地址找到了原本储存的数据
这下你知道为什么需要一个指针了吧?
因为指针的本质也相当于一个地址
只不过这个地址是由我们来设的
我们通过设置指针的名字(比如p)
再将变量的地址存到p里面
这样,我们就可以通过调用指针p
来完成这一系列的由地址找数据
只不过这些过程我们不需要再看到了
我们只需要在代码里调用p
余下的留给计算机去做。
如果你学过结构体和函数
那你应该可以看懂这段代码
#include <stdio.h>
struct Lee
{
float height;
char sex[10];
float weight;
int tele;
};
void print(struct Lee* ps)
{
printf("%f\n %s\n %f\n %d\n", (*ps).height, (*ps).sex, (*ps).weight, (*ps).weight);//这里的(*ps).可以替换成ps->。
}
int main()
{
struct Lee s = { 178,"male",80.1,14596238451 };
print(&s);
return 0;
}
在这里,我们就把结构体s的值赋予到指针ps里面了
从而完成数据的调用
这就是指针的作用。