标签:
C++ -- 指针
1、指针基础
指针是一个变量,其存储的是值的地址,并不是值本身。要获得一个变量的地址,要通过地址运算符&实现。
eg:
int a = 4; int *p = &a;
这里指针p就存储了a的内存地址,p指向变量a的地址。指针表示一个变量地址,要想通过指针获取值,需要*运算符,称为间接值或解除引用,将其应用于指针,可以获取该地址存储的值。如*p,*符号后面必须是指针。
#include <iostream> using namespace std; int main() { int v = 4; int *p; p = &v; cout << "v = " << v << endl; cout << "v = " << *p << endl; return 0; }
结果如下:
可知,通过变量名 v 和 *p 访问的是同一个内存中的同一个值。变量v表示值,可以用&符号获取变量地址;变量p是指针,可以用*符号来获取值。这就像硬币的正反面,不管通过哪中方式,访问的都是同一块内存、同一个值,就如同不管是从硬币的哪个面看,看到的都是同一个硬币。这里,v 和 *p 完全等价。
要声明一个指针,必须指定其类型:
如要声明一个int型的指针,int* p;
如要声明一个double型的指针,double* p1;
如int* p,我们说p的类型是指向 int 的指针
C语言中,使用这种int *pr;这里强调的是 *pr 是一个int类型的值。而在C++中,经常使用int* pr;这里强调int*是一种类型,意为指向int的指针。其实这两种声明风格对编译器来说并无不同,只是各自理解的方面不一样。我们还可以这样声明,int*pr;都是可以的。
在C++中,int * 是一种复合类型。
如果有人要声明两个指针,或许会这样做:int* pr, pr1。这中声明是错误的,这样声明其实是声明了一个int型指针pr和一个int型变量pr1。对于每个指针变量,都需要使用一个*。从这方面来看,以C的风格来声明多个指针或许会更容易理解些:int *p, *p1;
由于已将p声明为一个指向int的指针,所以当让它指向一个double类型变量的地址时,编译是通不过的。
int v = 4; double d = 5.4; int *p, *p1; p = &v; p1 = &d;// error
我们要注意,在使用指针取值运算(*)之前,一定要将指针初始化为一个确定的、适当的地址。
2、通过new来分配内存
我们知道,变量是编译期间分配的有具体名称的内存;而指针是为可以通过名称来直接访问的内存提供了一个别名。指针的真正用途在于,在运行阶段分配未命名的内存以存储值。在此种情况下,因为变量是没有具体名称的,所以只能通过指针去访问内存。
动态分配内存,要通过new运算符实现。
int* p = new int;
通过在new运算符后加具体类型,我们就告知了编译器我们具体需要什么类型的内存,然后编译器就会根据这种类型在平台中所占的字节数来分配一块内存,并返回其地址。所以new int,实际上有两个操作,一个是分配内存,一个是返回内存的地址。最后就是将该地址赋给了指针p。跟一般的方式相比,这里的内存块并没有具体的名称,我们只知道它的地址,并通过地址访问其中的值。这里我们称,指针p指向一个数据对象。
这种声明方式的通用格式是
type* pointer_name = new type;
eg:
#include <iostream> using namespace std; int main() { int a = 4; int *p = &a; int *q = new int; *q = 4; cout <<"a value = "<< a << endl; cout <<"a address = " << p << endl; cout << "*q value = " << *q << endl; cout << "q address = " << q << endl; return 0; }
结果:
在此,需要指出,通过new分配的内存块和一般常规变量声明分配的内存块存储的区域是不同的。前者存储在堆中,后者都存储在栈中。在使用完new分配的内存后,要记得使用delete释放内存。
#include <iostream> using namespace std; int main() { int a = 4; int *p = &a; int *q = new int; *q = 4; cout <<"a value = "<< a << endl; cout <<"a address = " << p << endl; cout << "*q value = " << *q << endl; cout << "q address = " << q << endl; delete q;//-- return 0; }
这样可以防止内存泄露。记住,new 和delete 一定是成对出现的。不能用delete来释放声明变量所获得的内存。
3、指针和数组
C++中,创建动态数组很简单:
int *p = new int[20];
因为是数组,所以这里new返回的是数组中第一个元素的内存地址。我们知道,在C++中,数组名称其实也是一个指向数组首元素地址的指针,所以我们可以将指针p看做是数组的名称。但这两者之间还是有一些区别。
#include <iostream> using namespace std; int main() { //两种方式给数组赋值 int* p = new int[3]; //将指针看做数组名,给数组赋值 p[0] = 1; p[1] = 2; p[2] = 3; cout <<"p[1] value = " << p[1] << endl; //使用指针方式给数组赋值 *p = 4; *(p+1) = 5; *(p+2) = 6; cout << "*(p+1) value = " << *(p+1)<< endl; return 0; }
结果如下:
其中*(p+1) = 5;可以看做两部分来分析,初始时,p是指向数组首元素的地址, (p+1)表示指针指向了第二个元素的地址,假设int型在机器中是占4个字节,那么此时指针p已经移动了4个字节,指向了第二个元素的内存的地址;后在使用*,来为这个内存赋值。
#include <iostream> using namespace std; int main() { int* p = new int[3]; p[0] = 1; p[1] = 2; p[2] = 3; cout <<"p[0] value = " << p[0] << endl; p = p + 1; cout << "p[0] value = " << p[0] << endl; p = p - 1; delete []p;// 释放动态分配的数组内存,new动态分配内存时有方括号,这里就要加方括号,否则就不加。 return 0; }
结果:
对空指针使用delete是安全的。
4、指针的算数及与数组名的区别
指针是一种类型的变量,支持算数操作。指针算数有+、-两种。由前面的内容可知,将指针变量加1后,其增加的值等于指向的类型在系统中占用的字节数;减1也是类似的。具体的例子在前面的代码中已经涉及到,就不在赘述。要说明的是,如果指针p指向一个数组首地址,p = (p+1),这时p就指向了数组的第二个元素的地址。其次,对于数组名称是不支持算数操作的,虽然它可以看成指向其首元素的地址的指针;因为数组名称是一种常亮。
在使用数组表示法(a[ i ])时,C++都执行这样的转换:arrayName[ i ] becomes *(arrayName + 1)。
其次,指针和数组还有一个区别:对数组应用sizeof运算得到的是数组的长度,而对指针应用sizeof得到的是指针的长度,并不是数组的长度,也不是指针指向的类型的长度。
5、指针和类型组合
假设有如下结构体,我们可以声明这个类型的变量,通过成员运算符 . 来访问其成员变量:
struct str {int year;}; str s1, s2; s1.year = 2; s2.year = 3;
我们也可以声明指向str类型的指针,来访问成员变量:
struct str {int year;}; str s1, s2; str* p = &s1; str* p1 = &s2; p->year = 2004; p1->year = 1998;
-> 成为间接成员运算符。
同时,也可以创建指针数组。
标签:
原文地址:http://www.cnblogs.com/xzmblog/p/4557064.html