指针初识(理解)
1.指针是什么?
2.内存是如何产生的。
3.数据在内存中是如何储存的。
4.指针类型的意义。
5.二级指针。
我们首先要知道指针是什么?
理解指针就要有两个要点:
1.指针是内存中一个元素的编号,也就是地址。
2.平时我们口头上说的指针,通常指的是指针变量,是用来存放指内存的地址的变量。
内存
平时我们的内存被划分为一块一块小的区域。
一个基本的内存单元的大小是一个字节。
而内存的编号就是我们的地址。
而我们的内存上的编号是怎么产生的呢?
也就是我的地址是如何产生的呢?
这就有点涉及我们的计算机原理了。
首先我们要知道我们的所谓的32位机器和64位机器是什么意思,其实就是我们32位机器有32根物理的电线(真实存在的),而当电线通电时就会产生高电频和低电频,计算机把高电频当作1,把低电频当做0。我们可以想象这样就可以产生32位2进制的数字。
00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000002
(省略)
011111111111111111111111111111111111
(省略)
1000000000000000000000000000000
(省略)
111111111111111111111111111111111111
而我们就把这样的二进制编号当做成了内存的编号,也就是地址。
我们可以计算一下到底有多少个地址:
2的32次方=4,294,967,296/1024=4,194,303/1024=4095/1024=4GB
(bite) (kb) (MB)
我们还可以利用我们的编译器来观看数据在我们内存中是如何储存的。
我们先创建一个变量
#include<stdio.h>
int mian()
{
int a = 0;
return 0;
}
我们通过在内存中观察变量,了解数据是在我们内存中如何存放的。
首先,我们进入调试,能看见各种内存其中??就都是数据,而第一列的十六进制的数字就都是内存的编号。
具体内存的地址我们也不知道,我们就只能利用我们的取地址符号(&)来帮助我我们找到变量a的具体地址。
这样我们就找到了变量a存放的地址:0x00DFF7F4
首先我们知道int类型是4个字节的所以内存中的数据分为4块:
0a 00 00 00(每一个字节是两个十六进制数字)
其中这个a代表的是十六进制的a,而十六进制的a正好就是我们十进制的10.
所以我们可以直观的看见数据是倒叙排放在我们的内存空间里面的。
至于为什么是倒叙我们也不做更深入的研究。
指针变量
我们前面已经说过指针就是地址,那么我们也就是到指针变量其实也就是存放地址的一个变量。
我们再用一段代码来表示。
#include<stdio.h>
int main()
{
int a = 0;
int* p =&a;
return 0;
}
在这里我们还需要利用编译器来观察一下。
我们可以清楚地看见当我们输入&a时,我们的&a就变成了int类型变量a的第一个地址。
因此,我们也可以清楚地知道指针变量取得是首地址!
问题随之而来了,我们的指针变量的大小是多少呢?
同样的我们用需要编译器来计算一下
#include<stdio.h>
int main()
{
int a = 10;
int* p = &a;
printf("%dn", sizeof(p));//输出为4
return 0;
}
我们很轻松的就能得到指针变量p的内存空间是4。
但是,这里我们需要强调一下,我们刚刚都是用32位机器跑的,但是如果我们换成64位机器,那么指针变量的内存也会随之改变。如果是64位机器,那么我们的内存的编号会变长,这样我们的指针变量的大小就会变成8个字节。
指针类型的意义
指针类型有什么呢?
int main()
{
int* p;
char* pp;
float* ppp;
double* pppp;
short* ppppp;
printf("%dn", sizeof(p));//输出4
printf("%dn", sizeof(pp));//输出4
printf("%dn", sizeof(ppp));//输出4
printf("%dn", sizeof(pppp));//输出4
printf("%dn", sizeof(ppppp));//输出4
return 0;
}
指针类型有各种各样的,但是当我们计算不同指针类型的大小时,我们就惊奇的发现原来32位机器下,各种指针的大小都是一样?这是为什么呢?
我们实验一下。
#include<stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
*pa = 0;
return 0;
}
我们来看一下在内存中a是如何变化的。
我们可以看见这段代码可以将变量中的a全部改完。我们再试试另一段代码。
#include<stdio.h>
int main()
{
int a = 0x11223344;
char* pa = &a;
*pa = 0;
return 0;
}
这段代码和之前那段代码就相差在指针的类型上。
我们来看看在内存中a的变化。
在这我们发现,char类型的指针变量只能改变一个字节的内存。所以我们可以知道,指针的大小和指针的类型其实没有关系,而有关系的是什么呢?
指针类型决定了在解引用的是能访问几个字节(指针的权限)
我们取地址的时候取到的是第一个地址,而指针类型代表了指针在解引用的时候从第一个地址开始能访问几个字节。
指针类型的第二个意义
我们依然是先上代码
int main()
{
int a = 10;
int* www = &a;
char* vvv = &a;
printf("%pn", www );
printf("%pn", www + 1);
printf("%pn", vvv);
printf("%pn", vvv + 1);
return 0;
}
我们在地址上可以观察到,www到www+1,地址往后进了4位
而vvv到vvv+1,地址只往后进了1位。
指针类型决定了向前或者向后一步,走多大距离,单位是字节
我们再来观察一段代码,让我们更好的了解指针。
int main()
{
int a[10] = { 0 };
printf("%dn", &a[0] - &a[9]);
printf("%dn", &a[9] - &a[0]);
return 0;
}
在这里我们用一个数组中的最后一个元素的地址减去一个第一个元素的地址。
那我们得到的结果是什么呢?
讲这段代码是想普及一个知识点,数组元素的地址是由低到高的。
而我们需要注意一点
指针相减的的前提是:两个指针指向同一块空间。(例如同一个数组)
而指针相减得到的是两个指针之间元素的个数。
二级指针
我们知道一个指针变量里面存储的是一个地址,而我们的指针变量也是要有地址。
int main()
{
int a = 0;
int* p = &a;
int** pp = &p;
int*** ppp = &pp;
printf("%pn", p);//一级指针
printf("%pn", pp);//二级指针
printf("%pn", ppp);//三级指针
return 0;
}
我们这就能打印出指针变量的地址。
我们也能通过多级指针来改变我们原始变量a的值。
int main()
{
int a = 0;
int* p = &a;
int** pp = &p;
int*** ppp = &pp;
***ppp = 20;
printf("%d", a);//输出20
return 0;
}
这时我相信很多人会对(*)这个符号感到困惑。
(*)这个符号我们之前提过,叫做解引用操作符
***ppp怎么理解呢?
我们进行分布理解
**(*ppp)=**(pp)=*(*pp)=*p=a;
因为ppp存放的是指针变量pp的地址,我们解引用ppp时,我们就找到了pp.
而我们pp中存放的是指针变量p的地址。*pp=p.
*p就能找到a.
好了,这就是我对于指针的初步理解。