c语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过,下面我来给大家详细的介绍指针的难以理解的概念以及一些注意的细节。
1.指针的概念
指针是c/c++中的一个变量,是存储它所指向那个单元的地址,它可以利用地址,将它的值直接指向存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。下面来列举些例子:
A:int *p;//指针
B:int **p;//指针的指针
C:int *p[3];//指针数组
D:int(*p)[3];//数组指针
E:int *add();//指针函数
F:int(*p)();//函数指针
G:int *(*p)[3];
2.指针的间接访问和指针的指针
在指针中,我们通过一个指针来访问它所指向的地址的过程称为间接访问或者解引用指针。对c有了解的人都知道用于间接访问的操作符为*。比如:
int *p;
int test = 1;
p = &test;
&为取地址符,&test即为获得test的地址。在前面我们说明了指针所存的是目标单元的地址,而p=&test这一句则为将test的地址赋给p指针。如果你熟悉了上面的操作那不妨看看下面这个表达式:
*&p = 25;
如果你的答案是将25赋给p,那么恭喜你,你答对了。现在我们来分析下这个表达式,首先&操作符与p结合获得p的地址,然后*操作符访问其操作数所表示的地址,在这个表达式中,操作数是p的地址,所以答案为将25的值赋给p。
好,当你理解了上述操作时,来看看指针的指针,所谓指针的指针,概念不过为指针指向的是另一个指针的地址,书写的形式参照A例。我们通过一个案例来了解下指针的指针:
#include<iostream>
using namespace std;
int main()
{
int a = 1;
int *b;
int **c;
b = &a;
c = &b;
cout <<"c中存的东西:" <<*c << " b的地址:" << b << endl;
system("pause");
return 0;
}
在vs上的输出结果为:
从这个案例可以看出,指针的指针存储的是指针的地址,而指针的指针的含义也可以从这个案例得出,即为指向指针的指针。
有些同学可能会对b指针取地址产生困惑,指针不是代表着地址吗?怎么还可以对它取地址?严格来说指针不能代表着地址,而是指针存着地址,而指针本身有一个地址。不管什么类型的指针,在内存中,指针在不同位的平台里有不同的固定大小,它在32位里是4个字节,而在16位里是2个字节。
3.指针与数组的关系以及指针运算
当谈及数组时,指针是不可被避免的,因为它俩的性质决定了它们之间的关系,在说它俩的关系前我们来说说指针的第一种运算:
第一种形式:指针+(-)整数
指针加上或者减去一个整数的结果是另一个 指针。而现在问题是,这个新的指针它指向的是哪里?答案是指向它所在内存的下一个或者上一个相同类型的指针。还有一个问题,比如int占据的内存空间不止一个字节,如果你将一个指向int类型的指针加1,会不会发生指针指向int值内部的某一个字节?其实当一个指针和一个整数在进行算术运算时,本身会根据合适的大小做出调整,这个调整就是把整数值和“合适的大小”相乘。举个例子:计算int型指针加2,这个2将根据int型的大小进行调整,这样下来,实际上加到指针上的整型值为8(4*2)。
而数组在内存空间中为连续的内存分配空间,我们可以定义一个指针使该指针指向数组的首元素地址,那么对指向数组首元素地址的指针加(减)运算自然而然的会将该指针指向下一个(上一个)数组的元素,从而可以用指针对数组进行操作,在说利用指针操作数组的案例前我们需了解下数组名,数组首元素地址,和数组的地址之间的关系:
#include<iostream>
using namespace std;
int main()
{
int a[10];
cout << "数组首元素地址:" << a << endl;
cout << "数组首元素地址:" << &a[0] <<endl;
cout << "数组的首地址:" << &a << endl;
system("pause");
return 0;
}
结果为:
初学者可能看到这里会惶恐并发出疑问,三个地址相等?其实在c/c++中数组名代表着数组的首元素地址,即案例中a[0]的地址,数组名始终代表着数组首元素的地址,n维数组也一样。而a与&a虽然值一样但是含义大不相同,一个是指向数组首元素地址,一个则是指向整个数组,为数组的首地址,两者都为指针,但是a+1后的地址值会偏移一个元素的长度,&a+1后的地址值会移一整个数组的长度,所以a与&a类型是不一样的。(切记数组首地址和首元素地址不等同)
现在来看看指针操作数组的案例:
#include<iostream>
using namespace std;
int main()
{
int a[3];
int *p = a;
for (int i = 0; i < 3; i++)
{
cin >> a[i];
}
for (int i = 0; i < 3; i++)
{
cout << *p++ << " ";
}
system("pause");
return 0;
}
定义了一个含有三个int型的数组,用p指向该元素首地址,进行指针运算,指针向下移,完成对数组的遍历。
第二种形式:指针-指针
只有当两个指针指向同一个数组中的元素时,才允许从一个指针减去另一个指针,指针相减的结果类型为ptrdiff_t,一种有符号的整数类型。相减的值为两个指针在内存中的距离除以数组元素的类型所得的整型值。这个值代表着两个指针距离,简单讲时两个指针所对应的元素的下标值相减。如:
#include<iostream>
using namespace std;
int main()
{
int a[3];
int *p2 = &a[0];
int *p1 = &a[1];
cout << p2 - p1<<" ";
system("pause");
return 0;
}
所得结果为:
因为是前面的元素减去后面的元素所以值是-1。
4.指针数组与数组指针
简单来讲指针数组是存着数组的指针,而数组指针是指向数组的指针,如:
C:int *p[3];//指针数组
D:int(*p)[3];//数组指针
C与D两例,很多人经常会在指针里分辨不出来谁是谁,其实我们可以用优先级来判别,先看C例,[]的优先级高于*,所以p先于[]结合形成含有3个元素的数组p[3],再与int *结合,说明数组中的元素是int型的指针。再看D例,()的优先级最高,*与p结合形成指针,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名一维数组,该指针指向这个匿名一维数组,数组长度为3。
先谈指针数组,其实指针数组与普通的数组没什么区别,只不过将普通数组的类型换成了指针而已,也就是说,指针数组里存的是指针,是指针的指针的数组形式。
再谈数组指针,因为数组指针也叫指向一维数组的指针,所以也叫行指针。它是用来指向整个数组的指针。模仿之前所讲的指针的定义,数组指针其实可以这样来定义:
int (*)[3] p;
int (*)[3]是指针类型,p是指针变量。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量p前移了而已。我们看看下面这个案例:
#include<iostream>
using namespace std;
int main()
{
int a[3] = { 1,2,3 };
int(*p)[3];
p = &a;//将数组首地址赋给p
for (int i = 0; i < 3; i++)
{
cout << (*p)[i] <<" " ;//输出为1,2,3
}
system("pause");
return 0;
}
为什么(*p)[i]可以表示数组中的元素呢?在之前说过,要访问指针中所指向的元素,我们需要对这个指针解引用,就是在指针前面加*,而在这里,p就是指针,所以要先解引用,而[]的优先级高于*,所以在这需要加上(),再通过[]来访问数组中某个位置的元素。那么,数组指针在多维数组里如何使用呢?其实在c/c++中,我们可以将所有的数组都看做一维数组,比如说二维数组看做一维数组时,这个一维数组里的每个元素也为一个一维数组,比如在二维数组里操作:
#include<iostream>
using namespace std;
int main()
{
int a[3][3] = { 1,2,3,4,5,6,7,8,9 };
int(*p)[3];
p = &a[0];
for (int i = 0; i < 9; i++)
{
cout << (*p)[i]<<" " ;
}
cout << endl;
system("pause");
return 0;
}
输出为:
#include<iostream>
using namespace std;
int main()
{
int a[3][3] = { 1,2,3,4,5,6,7,8,9};
int(*p)[3];
p = &a[0];//将数组首地址赋给p
for (int i = 0; i < 3; i++)
{
cout << (*p)[i]<<" " ;
}
cout << endl;
p++;
for (int i = 0; i < 3; i++)
{
cout << (*p)[i] << " ";
}
system("pause");
return 0;
}
输出为:
#include<iostream>
using namespace std;
int main()
{
int a[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
cout << a << " " << a[0]<<" "<<&a[0]<<" "<<&a<<" "<<&a[0][0];
system("pause");
return 0;
}
输出的结果会一模一样。其它n维数组与此类似。
5.函数指针与指针函数
指针函数其实就是返回值为指针的函数,像E例,在此不多加说明,而函数指针请参考我的另一篇文章:c/c++之函数指针。http://blog.csdn.net/hackersuye/article/details/78339625