我之前也写过一篇“对C++中指针的简单理解”,因为也是刚开始写博客,感觉弄得乱糟糟的,而且有种断章取义的感觉。所以今天打算写得细致一点(其实也就是照着书上搬的)。提到数组与指针我想大多数同我这样的渣渣一样,都觉得不好弄懂。以前老师上课吧,也没认真听,而且都是断断续续地在讲。现在自己从图书管里面借了一本书来学习,终于大致弄懂了(估计也就是皮毛);不过掌握一点总比不会好吧。指针这个东西对于学语言(我所知的特别C和C++)都有特别大的用处。如果不学好,就会出现 有可能某一天你看别人写的代码,里面全是指针,顿时你就傻眼了。我写这个的目的也是为了巩固自己所学的,顺便就当做个笔记,以便于以后查看。说实话,这个东西如果你不经常用到,就算你今天弄懂了,可能隔几天又忘得差不多了。好了,费话扯得差不多了,就开始进入正题吧!
1.计算机中的内存
说到指针就先简单说说计算机里面的内存是怎么回事吧。在计算机中,系统的内存就像是带有编号的小房间,如果想使用内存就需要得到房间号。如图1所示,定义一个整型变量i;之后计算机就分配给它4个字节,所以编译器为变量i 分配了编号从2001到2004的房间(这儿的编号只是我随意举例的,在计算机中地址一般类似于这样0044FA28,不过不影响我们对整体的理解);每个房间代表一个字节。
当为变量i=5赋值之后,就被连续地存储在系统的内存中,如图2所示(其实应该是2进制00000000 00000000 00000000 00000101)
在程序执行时,是根据变量与地址的对应关系,找到变量i的地址2001,然后从2001开始读取4个字节的数据放到寄存器中。由于通过地址能访问指定的内存存储单元,我们就可以说该地址“指向”该内存单元,之后我们就可以把地址形象的称为指针,即一个变量的地址称为该变量的指针。这也就是对指针概念的引出。另外,如果有一个变量专门用来 存放另一个变量的地址,它就是指针变量(其实指针也是一个变量,只是它仅用于存储变量)。
2.指针的声明与初始化
声明格式:
数据类型 *指针变量名
如:int *p;
初始化:
a.在定义时初始化:
int i;
int *p=&i; (其中&是取地址符,表示把i的地址取出来赋给p)
b.在定义之后赋值:
int i,*p;
p=&i; (注意,这种情况下赋值的时候就不要*了)
例1:
#include <stdio.h>
void main()
{
int i = 2015;
int *p = &i;//初始化的时候把i的地址赋给p
int *q;
q = &i;//初始化之后再把i的地址赋给q
printf("%d\n", *p);
printf("%d\n", *p); // 此时输出的结果一样均为2015
}
注:其中p,q是变量名,*p,*q 表示取变量p,q所指向内存单元的值
3.&*p和*&i(i为一个整型变量)的区别
如:int i=2015;
int *p=&i;
&和*的运算符优先级别相同,按自右向左的方向结合,因此&*p是先进行*运算,*p相当于变量i的值(2015),再进行&运算,&*p就相当于变量i的地址。*&i是先计算&运算答,&i就是取变量i的地址,然后进行*运算,*&a就相当于取变量i 所在地址的值(2015)。总之你只要记住两个运算符的作用,和结合方向就知道结果了!
4.指针的运算
因为指针也是变量,所以同样可以进行运算(本处就仅限于+-吧,我想其实剩除也行只是对于初学者意义不大)。
如图3所示,当定义变量i,j(int i=3,j=4),且此处假设计算机在分配内存的时候i,j变量之间是连续分配的(等后面说到数组的时候就更好理解了,这儿只是先提出一下)
如果int *p=&i; 之后,p就指向了101,p++之后,则p就指向了105,此处的加1并不是从101到102的变化,而是加了一个单位的距离,可以理解成单位“1”;这个例子在计算机上基本上是不能实现的,因为在分配内存的时候,几乎不可以是这样连续分配的。说到数组之后就容易理解了。
5指针与数组
先简单说一下什么是数组。数组,作为同名、同类型的元素的有序集合,被顺序存放在一块连续的内存中(注意这儿是连续,且不仅仅限于一维数组,N维数组在内存中也是是连续储存的,只是为人理解,人为的把它们的地址做了一些变化,如图4),而且每个元素的存储空间大小相同,比如是int 型的数组则每个元素的大小都是4个字节。(我只是搬运工哦,顺便加了一些自己的理解)
如图4.1所示,在定义一个一维数组int a[7] 之后,数组a就被分配了等大小(都是4个字节)的7个连续空间,从301-307;此时的数组名a就代表该数组的首地址(301,等价于&a[0]);之后我们就可以通过使用数组名和下标的方式来引用数组里面的值。接着说二维数,如图4.2所示,定义了个二维数组int b[4][3],数组b就被分配了等大小(都是4个字节)的12个连续空间,从401-412;注意,这儿的数组名就不代表首地址了,b[0]才代表该数组的首地址(401,等价于&b[0][0]),同样我们可以通过数组名和下标的方式来使用数组。同时,为了我们方便理解,才把二维数组(n维也是一样的理解)转化成图4.3的形式。此时,我们可以分别把b[0],b[1],b[2],b[3]叫作该每一行的行地址。
注: (1)b[n]表示第n行的首地址;
(2)&b[0][0]既可以看作是第0行的首地址,也可以看作是整个二维数组的首地址;同理&a[m][n]就表示第m行的第n个元素的地址;
(3)*(b[n]+m)表示第m行第n列的值,因为b[n]代表第n行的首地址,然后再向右移动4个单位,再用*运算符,就表示它的值了
例2.
#include <stdio.h>
void main()
{
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
for(int i = 0; i < 6; i++)
printf("%d ", *(a[0] + i)); //从首地址开始,一直往后移动
}
运行结果:
1 2 3 4 5 6 请按任意键继续. . .
6.指针的运算
最后我们再来说说指针的运算。就拿我自己的接触来说,指针的运算更多的就是在数组里面。其它地方几乎也没见过指针有运算的,说到底就是因为其它地方的变量分配的地址不是连续的,所以运算也是没有意义的。
接着用图4来说
在图4.1中,我们定义了一个数组a[7],且进行了初始化。此时再定义一个指针,int *p=a;(或int *p=&a[0]) 并把数组的首地址初始化为数组a的首地址,然后我们就可以通过指针p来操作数组了。
例3:
</pre><pre name="code" class="cpp">#include <stdio.h>
void main()
{
int a[7] = {1, 2, 3, 4, 5, 6, 7};
int *p = a;
for(int i = 0; i < 7; i++)
printf("%d ", *p++); //从首地址开始,一直往后移动
}
运行结果:
1 2 3 4 5 6 7 请按任意键继续. . .
注:(1)*p++ 相当于a[i++],表示输出p所指向的值之后,指针p下移一个单位(也就是4个字节,若定义的数组是long long a[7],单位长度就可能是8个字节了,一定要理解p+1的含义)
(2)*++p 相当于a[++i],表示指针先移动一个单位,然后输出p所指向的值
(3)*p++ 等价于 *(p++) 不要问为什么p++有括号之后就指针就应该先右移一个单位,再输出p指向的值,因为我也不知道,想那时还为这个和老师争了一下,让我自己拿电脑试,结果果然是这样。(开个玩笑,想知道为什么的可以去百度百度)
(4)同理*++p也等价于*(++p)
在图4.2中,我们定义了一个二维数组b[4][3],并也进行了初始化;此时再定义一个指针int *p=b[0](或int *p=&b[0][0])并把数组的首地址初始化为数组a的首地址,然后我们就可以通过指针p来操作数组了,当然你也可以从中间截取如:int *p=b[2];
例4:
#include <stdio.h>
void main()
{
int a[4][2] = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
int *p = a[0];
for(int i = 0; i < 8; i++)
printf("%d ", *p++); //从首地址开始,一直往后移动
p = a[2];
printf("\n");
for(int i = 0; i < 4; i++)//从第2行开始,一起往后移动
printf("%d ", *p++);
}
1 2 3 4 5 6 7 8
5 6 7 8 请按任意键继续. . .
好,数组与指针就先跟大家扯到这儿(不应该是和我自己扯到这儿,估计也没有看,留着以后自己慢慢回顾)! 未完待续!!!!!!!!!!
2015年4月9日11:34:25