一个指针是一个特定数据类型的存储地址。
与其他类型变量一样,指针变量也必须要先声明。指针变量的一般形式为:
<数据类型>*<变量名>;
其中,<数据类型>是指针所指对象的类型,在C++中指针可以指向任何C++类型。<变量名>是指针变量名。
指针中使用两种特殊的运算符——*和&
一元(单目)运算符&用于返回其操作对象的内存地址,其操作对象通常作为一个变量。
例如,下面的代码定义整型指针p和整型变量a,并把变量a的地址赋给指针p
int *p;
int a=18;
p=&a;
第二个与指针有关的运算符是*,它与&运算符作用相反。作为一元运算符的*用于返回其操作数所指对象的值,因此,该运算符需求其操作对象为一个指针。
例1 下面程序说明了指针变量的工作原理。
#include<iostream>
using namespace std;
int main()
{
int* a;
int x = 5;
a = &x;
cout << &a << endl;//①
cout << a << endl;//②
cout << &x << endl;//③
cout << endl;
int y = 10;
*a = y;
cout << &a << endl;//④
cout << a << endl;//⑤
cout << &y << endl;//⑥
cout << x << endl;//⑦
cout << endl;
return 0;
}
上述程序中在:首先将指针a指向x的地址①中输出的是指针a的地址,②中输入的是指针a指向的值的地址,③中输出的是变量x的地址。之后将变量y的值赋给指针a,也就是将y的值赋给x④中输出指针a的地址,⑤中输出a指向的值的地址,也就是x的地址,⑥中输出变量y的地址,⑦中输出更改后x的值。
5.1 指针与地址
5.1.1 指针说明
指针是用所指对象类型来表征的。
注:在使用任何指针变量之前必须先给他赋一个指向合法具体对象的地址值。
错误示范:
int *p;
int a=1;
*p=a;
在此赋值前,指针变量p没有指向一个具体对象。由于p中有一个随机值该值将破坏内存中某个地址空间的内内容,严重时将导致程序挂起或机器死机。
又例如:
char *p;
cin>>*p;
同样是错误的,因为指针s没有指向一个具体对象(空间),因此无法存储读入的字符串(将破坏内存中某段空间的内容)。
使一个指向一个具体对象的方法有:
1.使用new运算符(malloc和alloc等函数)给指针分配一个具体空间。
2.将另一个同类型的指针给它以获得值。
3.通过&运算符指向某个对象。
5.1.2 指针运算
尽管指针中存放的是变量的地址,但在C++中指针只能进行如下运算。
(1)指针和整形量可以进行加减
若p为指针,n为整型量,则p+n和p-n是合法的,同时p++也是合法的,它们的结果同指针所指对象类型有关。
例2 指针与整型相加运算
#include<iostream>
using namespace std;
int main()
{
int* p;
int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
double* q;
double b[10] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 };
p = a;//在此a即为整形数组a的首地址
q = b;//在此b为double型数组b的首地址
for (int i = 0; i < 10; i++)
{
cout << p + i << "的值为:" << *(p + i) << " " << q + i << "的值为:" << *(q + i) << endl;
}
return 0;
}
注:对于大多数32位编译器来说,int类型的数据长度为4个字节,double类型的数据长度为8个字节,因此,指针p将按4个位置增加,指针q将按照8个位置增加。
(2)若p1、p2为指针,当p1和p2指向同一类型时可以进行赋值。
如:p1=p2;//p1、p2指向同一对象。
注:该语句使得两指针p1和p2指向同一空间,若其中一个指针所指空间被删除(释放),则另一个指针所指空间亦被删除,这就是指针悬挂问题。
因此在删除(释放)一个指针所指的空间时,一定要确保所有指向该空间的指针都已不再需要,防止指针悬挂。
(3)两个指向同一类型的指针,可进行==,>,<等关系运算,其实就是等级比较。
(4)两个指向同一数组成员的指针可进行相减,结果为两个指针之间相差元素的个数。
注:两指针不能相加。
(5)几组常见的指针运算表达式比较
1.p++和p+1的区别:指针p++结果为p指向下一个元素;p+1结果为下一个元素的指针,但p本身不变。
2.y=*p+1和y=*(p+1)的区别:*p+1结果为取p所指对象内容加1;*(p+1)结果为p指针加1,并取结果所指对象内容。
3.y=(*p)++和y=*p++的区别:(*p)++为先取指针p所指对象内容进行运算(因为运算符++为后置运算),然后对指针p所指对象内容加1;*p++为先取指针p所指对象内容进行运算,然后指针p加1。
5.2 指针和数组
在C++中,指针和数组的关系密切。实际上,数组的参数传递、数组元素的存取,都可通过指针操作完成。指针和数组常常可以互换。
在C++中,数组的名字就是指向该数组第一个元素(下标为0)的指针,即该数组第一个元素的地址,也即该数组的首地址。
例如:
int a[10],x;
int *p;
p=&a[0];
则:x=*p; x=a[0]; x=*a;
这三条语句是等价的,都是取数组的第一个元素,即下标为0的元素。
一般情况下,一个数组元素的下标访问a[i]等价于*(a+i)。但要特别注意:数组名和指针(变量)是有区别的,前者是常量,即数组名是一个常量指针,而后者是指针变量。我们不能改变常量的值,也不能取常量的地址。
数组名可作为参数进行传递。当将数组名传给函数时,实际上传递的是数组的开始地址(即数组第一个元素的地址)。
指针运算要比数组运算快的多。此外使用指针的另一个原因是在大量数据传输时,传递指针要远比传输数据本身效率高得多,如在函数参数传递及函数返回值时。当然,使用指针会给程序带来隐患(如指针悬挂问题),同时还使得程序的可读性降低(用数组实现的程序要比用指针实现的程序的可读性要好)。
对于字符串常量,可以把它看做一个无名字的数组,C++编译程序会自动为它分配一个空间用来存放这个常量,字符串常量的价值本身就是指向这个无名字数组的第一个字符指针,其类型就是字符指针。
例3 用数组实现计算一个字符串长度的函数
#include<iostream>
using namespace std;
int strlen(char s[])
{
int n = 0;
while (s[n] != 0)
{
n++;
}
return n;
}
int main()
{
char a[100];
cin >> a;
cout << strlen(a) << endl;
return 0;
}
5.3 空指针,无类型指针和const指针
1.空指针
不指向任何数据的指针成为空指针,其地址为0。由于地址0的这个特殊用途,地址0处不能用于储存数据。可以用指针常量NULL(其值为0)来初始化一个指针变量,使之成为空指针。
2.无类型指针
可以用void来定义一个指针 变量,成为无类型指针或void指针。
无类型的指针不予任何特定的数据类型相关联,但却可以用来指向任何类型的数据。任何类型的指针可以赋值给无类型的指针,但反过来却不可以。无类型的指针用在一些特殊场合。
3.const指针
利用const,同样可以吧一个指针变量声明为const变量,成为const指针或符号指针常量。
有两种不同的const指针:
①指针所指向的数据为常量,例:
const char *s="你好";
s="Hello!";//正确!改变的是指针本身:让s改为指向,另一个字符。
*s='Y';//错误!试图改变s所指的数据。
定义这样的指针时可以不初始化。
②指针本身为常量,例:char *const s="你好";
s="Hello!";//错误!试图改变的是指针本身。
*s='Y';//正确!改变s所指的数据。
也可以是上述两种情况的综合:指针本身和指针所指向的数据都禁止改动。
例:const char*const s="你好";//或char const *const s="你好";
参考《全国计算机等级考试二级教程——C++语言程序设计》