二、指针的定义
每个指针都有一个与之关联的数据类型,该数据类型指定了指针所指向的对象的类型。例如,一个int型指针只能指向一个int型对象。
在C++中,使用*符号把一个标识符声明为指针
以下给出一些指针定义的例子:
int *i; // int型指针,i用于指向int型对象
double *d; // double型指针,d用于指向double型对象
string *s; // string型指针,s用于指向string型对象
vector<int> *v; // vector<int>型指针,v用于指向vector<int>型对象
注意,*符号除了可以放在标识符前,也可以放在数据类型后,如下指针定义风格也是合法的:
int* i; // int型指针,i用于指向int型对象
double* d; // double型指针,d用于指向double型对象
string* s; // string型指针,s用于指向string型对象
vector<int>* v; // vector<int>型指针,v用于指向vector<int>型对象
但是这种指针定义风格容易引起一种误解: 把 int*, double*, string*, vector<int>*当成了一种数据类型。如对于以下声明:
int* i1, i2;
很容易会被误解成: i1 和 i2 均为指向int型数据的指针。
但实际上,只有 i1 是指向int型数据的指针,而 i2 则是一个int型数据。
假如要在同一声明语句里声明两个统一数据类型的指针,可以是如以下例子:
int *i1, *i2;
也可以如此声明:
int* i1, *i2;
又或者是分开两句声明那就更不会容易引起混淆:
int *i1;
int *i2;
为了更好理解指针对象,建议声明指针变量时,将*符号紧贴在指针变量名的前面放置。
三、指针的初始化
一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一个对象;或者是0值(C++从C语言继承下来的预处理器变量NULL,其值为0)。若指针保存0值,表明它不指向任何对象。未初始化的指针是无效的,直到给该指针赋值后,才可以使用它。下列定义和赋值都是合法的:
int val = 1024;
int *p1, *p2, *p3, *p4;
p1 = &val;
p2 = p1;
p3 = 0;
p4 = NULL;
注意,避免使用未初始化的指针。在程序编译时,未初始化的指针错误往往不会被找出来,可往往到运行时,程序则是因为指针未初始化而崩溃。
对大多数的编译器来说,如果使用未初始化的指针,会将指针中存放的不确定值视为地址,然后操作该内存地址中存放的位内容。使用未初始化的指针相当于操纵这个不确定地址中存储的基础数据。因此,在对未初始化的指针进行解引用时,通常会导致程序崩溃。C++语言无法检测指针是否未被初始化,也无法区分有效的地址和由指针分配到的存储空间中存放的二进制位形成的地址。
对于初始化为0值的指针,编译器可以检测出来,程序可判断该指针并未指向一个对象。
所以建议在声明一个指针时,假如没有初始值,那最起码应该将该指针初始化为0值,避免错误。
最后,总结如下,声明指针时应该初始化指针,对指针进行初始化或赋值,只能是使用以下4种类型的值:
1. 0值常量表达式,例如,在编译时可获得0值的整型const对象或字面值常量0.
2. 类型匹配的对象的地址
3. 另一对象末的下一地址
4. 同类型的另一个有效地址。
对于上面第一种类型,简单举例说明一下,以下的声明是合法的:
const int val = 0;
int *p1, *p2, *p3;
p1 = val;
p2 = 0;
p3 = NULL;
但,以下声明是不合法的:
int cval = 0;
int *p = cval;
因为在上面一个例子中, val 是一个const整型常量,值为0,所以赋值给指针 p1 合法。但对于下面一个例子, cval并不是一个常量,它是一个整型变量,所以不能赋值给指针 p。
另外,再次提醒,由于指针的类型用于确定指针所指向对象的类型,因此初始化或赋值时必须保持类型匹配。指针用于间接访问对象,并基于指针的类型提供可执行的操作,例如,int 型指针只能把其指向的对象当作 int 型数据来处理,如果该指针确实指向了其他类型(如 double 类型)的对象, 则在指针上执行的任何操作都有可能出错。
给出以下一个例子:
#include <iostream>
using namespace std;
int main(){
int i1 = 9;
double d1;
double *p1;
p1 = (double*)(&i1);
d1 = *p1;
cout << d1 << endl;
return 0;
}
输出结果如下: