C++ 指针详解(2)

来源:互联网 发布:2333网络语是什么意思 编辑:程序博客网 时间:2024/04/30 07:54
一、指针运算符*和 &地址运算符
eg:     y=&x;
         z = *y;
         x 的值为1000,x 的地址为55
1.&地址运算符是一元运算符,反回它的操作数的内存地址.y 将接收到地址55.
2.*指针运算符是一元运算符,它是&运算符的相反形式,*运算符能反回位于其操作数所指定的地址的变量的值.即只要*后面是地址(任何形式),*都返回地址对应的值。例中z 的值为1000,*y 把由y 所指向的内存的地址的变量x 的值赋给z。*运算符可理解为“在地址中”,则z=*y 可描术为“z 接收了在址址y 中的值。
3.其实可以把*y 当成一个变量来使用,即可以为*y 赋值等,例如*y=100;(*y)++;等,但要注意的是对*y 的操作相当于是对此指针指向的地址中的变量的操作,即对*y=100 的赋值语句,相当于是x=100,而(*y)++则相当于x++。
二、指针的运算
1.++,――运算符,假设int 型x 的地址为200,且int 型占4 个字节,定义int *p;p=&x;则p++的地址将是204,而
不是201,因为当指针p 的值增加时,它都将指向下一个int 型数据.减少时也是这样,p――则,p 的地址将是196.
2.+,-,运算符,注意两个指针不能相加.例int *p;p=&x;假设x 的地址为200,则p+9 的指针地址将是200+4*9=236,
即p 指向了从当前正指向的元素向下的第9 个元素.
3.两指针相减,同类型的一个指针减去另一个指针的值将是两个指针分开的基本类型的元素的个数.
三、指针和数组
1.在C++语言中使用没有下标的数组名会产生一个指向数组中第一个元素的指针.如 char x[20];char *p;p=x;此语句
说明将x 数组的第一个元素的地址赋给指针p. *(p+4)和x[4]两句都可以访问数组中第5 个元素,而p+4 就是指向第五个元素的指针.
2.p[i]语句相当于*(p+i)或x[i]即数组中第i+1 个元素的值
3. 指针数组的声明形式int *p[10];该语句声明了10个指针数组,每个数组中存储一个整数值地址.p[2]=&x;语句为指针变量的第
三个元素赋予x变量的地址,现在要访问x变量的值需要编写*p[2].即访问指针数组第三个元素的地址指向的变量的值.
4.以些来推二维数组。例如
#include <iostream>
using namespace std;
int main()
{ int x[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
int y[4]={13,14,15,16};
int (*p)[4]=&y; //必须要有&运算符,因为这时的y只是一个有四个元素的名字和int i中的i是一样的只是个名字,所以必须要有&运算符
cout<<p[0][1]<<endl;//输出p所指向的第一行的第二个元素,即y的第二个元素14。
cout<<(*p)[1]<<endl;//输出p所指向的第二个元素,即y的第二个元素14。
cout<<*p[0]<<endl;//输出p所指向的第一行的第一个元素,即y的第一个元素13。
cout<<(*p)[5]<<endl;//输出p所指向的第六个元素的值,这里超出数组y的范围,因为y只有一行元素,只有四个值,所以为随机值
cout<<*p[1]<<endl;//输出p所指向的第二行的第一个元素,这里p只有一个数组元数y,所以输出随机值。
cout<<p[0]<<endl; //输出p所指向的第一行的地址,即&y的地址
cout<<y<<&y<<endl;//输出y和&y的地址,这里是相同的值
cout<<p[1]<<"\n";//输出p所指向的第二行的址址,这里超出数组y的范围,为随机值。
p=x; //把数组x的地址赋给指针p
cout<<p[1][1]<<endl;//输出p所指向对象的第二行的第二个元素的值,即x的第二行第二个元素的值6。
cout<<(*p)[5]<<endl;//输出p所指向的元素的第六个元素的值,即x的第二行第二个元素的值6。
cout<<*p[1]<<endl;//输出p所指向元素的第二行第个一元素的值,即x的第二行第一个元素的值5。
cout<<p[1]<<"\n";//输出p所指向元素的第二行的地址,即x[1]的地址。
p=x+1;//或者p=&x[1];
cout<<p[1][1]<<endl;//输出p所指对象的第二行第二个元素的值,这里p是指向x[1]的地址的,所以输出的将是x的第三行第二个元素的值10。
cout<<(*p)[5]<<endl;//输出p所指对象的第六个元素的值,即p所指元素的第二行第二个元素,即x的第三行第二个元素10。
cout<<*p[1];//输出p所指向元素的第二行的第一个元素的值,即x的第三行第一个元系的值9。
}
结果:
四、指向指针的指针
int **x,*y,z;
z=25;//假如z的地址为50
y=&z;//z的地址赋给指针y ,y=50,假如y的地址为100
x=&y;//指针y的地址赋给指向指针的指针x, x=100
cout<<**x ;//输出z的值25
cout<<*x ;//输出y的值或z的地址50
cout<<x; //输出y的地址&y或**x的值100
cout<<*y ;//输出z的值25
五、const 指针(不能给它修饰的变量赋值,但可以通过指针指向的变量来间接的改变常量指针的值)
eg: int x=1; //x的地址为50
语句p*pxint x=1; const int *p =&x;50 可以修改 int y=2;p=&y,此时p和*p都变1  不能修改可以改变。x=2,此时*p=2,但p不变const int x=1;const int *p=&x;50 不可修改1 不可修改不可修改const int x=1;int *p=&x;出错  只能常量指针才可以指向常量  int x=1; int *const p=&x;50 不可修改*p=3;则x=3.但p不变可以改变。*p对应改变,但是p不变int x=1;const int *const p=&x;50 不可修改不可修改
可以改变。x=2,此时*p对应改变但p不变
5.当const指针用作函数形参时int hyong(const int *p, int x)意味着,函数不能修改传递给指针p的变量的值。
六、new
1、int *p=new int //未初始化(说明:见笔记static and extern)
2。动态创建数组:int *p=new int [11];创建动态数组时必须有[]方括号,且里面要有创建的维数,但该数组的第一维可
以是一个复杂的表达式。int (*p)[102]=new int [4][102]。
3. 动态创建对象的初始化:int *p=new int(102) 该语句表明由p指向的新创建你对象被初始化为102。
    默认初始化:类型名后面跟一对空的圆括号初始化, int *p=new int (); 把对象的值初始化为0
   int *ps=new string(); //string类调用默认构造函数初始化该对象
    cls *pc=new cls();
4.耗尽内存:如果程序用完了所有可用的内存,new表达式就有可能失败,系统将会抛出名为bad_alloc异常。
5.可以在一个函数内使用new运算符分配内存,而在另一个函数中使用delete释放内存空间.delete只能用于释放前次
使用new分配的空间,不要使用delete来释放不是new分配的内存,不要使用delete释放相同的内存两次,例如:int *p=new int [10];delete [] p;删除数组必须要有[]方括号。如int *p=new int ; int *x=p; delete x;将是合法的.如果指针的值为0,则在其上作delete操作是合法的,但没有任务意义。
6.悬垂指针:执行delete p后,p还指向原来他指向的对象的地址,然而p所指向的内容却已经被释放了,因此p不再有效而变得没有定义了。指针为空,0.这样的指针称为悬垂指针。悬垂指针往往导致程序错误而且很难检测出来。
7.静态联编:如果通过声明来创建数组,则在程序被编译时为他分配内存空间,占用了内存。在编译时给数组分配内存被称为静态联编。意味着数组是在编译时加入到程序中的。但使用new时,如果在运行阶段需要数组,则创建他,还可以在程序运行时选择数组的长度,这被称
为动态联编。意味着数组是在程序运行时创建的,叫做动态数组,使用静态联编时必须在编写程序的时候
指定数组的长度,使用动态联编时,程序将在运行时确定数组的长度。
14.const 常量对象的动态分配和回收:与其他常量一样,动态创建的const对象必须在创建时初始化,并且一经初始化,
其值不能再修改。例如:const int *p= new const int(111);删除方法为:delete p;尽管程序员不能修改const 对象的值,
但可以撤消对象本身。
15.注意:不能在空闲存储区上创建内置类型(int,float,char,bool)元素(除类数组string外)的const数组。因为我们不能初始化用new创建的
内置类型数组的元素。如const int *p=new const int [11];将是错误的。
16.注意:如果用new分配了资源,而没有用delete释放该资源的话,那么用new分配的资源将一指被占用。
17.常见错误:如果对某个指针动态分配了内存,又把另一个变量的地址付给这个指针,这时这个指针就指向了一个静
态地址,而不是原先的动态地址。不能用delete删除一个指向静态地址的指针。比如int *p =new int(1),这时指针p指向一个动态内存可以对他进行
delete删除,但如果再执行语句int a=2; p=&a;这时原先指向的动态内存地址变成了指
向现在的静态内存地址,如果这时对指针p进行delete操作就会出错,delete只能删除动态指针。对类成员变量delete调用析构函数来删除
例:动态分配对象new
class hyong
{ public: int a,b,c;   hyong (){a=b=c=0;}   hyong( int i){a=b=c=i;}   ~hyong(){cout<<"xigou" << "\n";}  };
int main()
{ hyong *p= new hyong;   
hyong *p1= new hyong(); cout<<p->a<<p1->a<< "\n"; //输出两个0,都调用默认构造函数初始化指针。
int *p2=new int ;      int *p3= new int ();    
 int *p4= new int (1); //对于内置类型来说p2没有被初始化得到的是一个随机值,p3被为0,p4被为1。
cout<<*p2<< "\n"<<*p3<< "\n" <<*p4<<"\n" ;
int i=10;  
delete p4;  
cout<<*p4<< "\n"; //输出0. p4现在是悬垂指针,delete只是释放掉了指针p4所指向地址的内容,指针p4仍然可以再指向其他地址。
p4=&i; 
cout<<*p4<< "\n" ; //可以对悬垂指针p4重新赋地址。
const int *p5=new int ; const int *p6= new int (4)  ;
cout<<*p5<<*p6<< "\n"; //输出一个随机值和4,const常量必须在声明时初始化。
int *p7=new int [2];   p7[0]=5;p7[1]=6;    
cout<<p7[0]<<p7[1]<< "\n" ;//定义动态数组,动态数组不能在声明时初始化。
//const int *p8=new int[2];  //错误,因为动态数组不能在声明时初始化,而const又必须要求在声明时初始化,发生冲突,出错。
delete p1;      delete p;   
//delete p,p1;   //注意,如果使用该语句将则只调用一次析构函数。
delete p2,p3,p4,p5,p6;    delete [] p7;
}
0 0