C++ 浅谈指针的用法:与const、一维数组、二维数组、typedef等结合

 

    指针一种特殊“复合类型”,它的定义依托一种其他类型。简单来说就是指向一个对象的变量,它保持的是这个变量的地址,通过这个指针可以访问该变量。定义方式: int *p、int**p、int (*p)[4]等。但指针作为一种直接操作内存的工具,在编写的代码的过程中,很容易出错,是大家公认的C++难点。这里有几点个人心得与大家分享一下:

   

  (1)指针与地址的区别

    指针与地址的区别,指针是一种类型,而地址是一个常量,这个常量可以赋值给该类型,就像int和4 的关系,因此用于指针的一些操作,不能简单的用于地址,如:*(指针)获取到这个指针所指向对象的值,而*(地址)是没有意义的。
      int Number=10; 
      int *pp=&Number;//取Number的地址
      cout<<*pp<<endl;//输出10
      cout<<*(0x0012ff60);//编译报错,没有意思,

   

   (2)指针与const的结合

     正如大家所知,const是用来定义常量的,即一旦定义不能修改。那么用它来修饰指针,就存在两种情况了:

     (2.1)指向const对象的指针:const int *cpr=&N(其中N为 const int N=10),这里的cpr是一个指向int类型的const对象,const 是限定所指向的对象而不是cpr本身。也就说cpr是可以改变的(cpr=0),但cpr所指对象是不能改变的(*cpr=10 是错误的),但然也可以把一个非const对象的地址赋值给cpr,int M=20;cpr=&M是可以的。但即使是这样也不能通过cpr去修改M的值(*cpr=10 是错误的),要想修改M的值,必须通过其他办法如直接M=25是合法的。反过来,要把一个const对象的地址赋值给一个非const指针,int cpr1=&N,这是编译会报错。

     (2.2)const指针 int *const con_per=&M,这里强调的是该指针是一个常量的不能再改变了,这种用法和引用有点相似(后面会介绍)。

    

   (3)指针与一维数组

     个人认为我们平时所说的指针是一种统称,其实指针还细分几种具体指针:(1)普通指针 int *p,可以指向一个变量,也可以指向以个数组;(2)数组指针int (*p)[4],指向数组首元素的地址的指针,即数组首元素地址的地址,(3)指针的指针int**p,在这里先结合一维数组重点讲解普通指针。其余两种会在后面讲解。

   int Number=10;
   int *pp=&Number;

   int int_arry[Max]={0,1,2,3,4,5,6,7,8,9};
   int *qq=int_arry;

   相信这两种情况学过C++的人都见过。没什么特别的,但有这么几个值值得大家注意:qq、int_arry、&int_arry[0]几个值是一样的,大家可以自己试试,但他们是有区别的,qq是一个变量,而int_arry、&int_arry[0]是一个常量。例外大家试试下面的语句:

        cout <<"&qq="<<&qq<<endl; //输出一个地址

        cout <<"&int_arry="<<&int_arry<<endl; //输出值和cout<<int_arry一样。

        cout <<"&(&int_arry[0])="<<&(&int_arry[0])<<endl; //编译报错

       大家可以自己试一试,这就引起一个问题直接输出数组名和输出数组名地址一样,这是为什么呢?我在网上查了一些资料,理解了一些,但还是决定不能很好理解,你们可以看看:

      数组名是指向第一个元素的地址,那么这个地址放在哪里?它自己是否有地址?  数组名是符号地址常量,仅仅是个标签,在编译时求值并存在编译器的符号表里面,  其值就是个内存地址;所以没有一个地方存放int_arry,可以认为程序没有给其分  配空间,数组名只是代表了那个数组空间;与指针不一样,指针指向一块空间,同 时指针本身也存储在某个空间;可以认为数组名存在在符号表里,符号表是编译器
  用的,我们管不到;int_arry和&int_arry值是一样的,但是二者类型是不一样的:
    int *qq=int_arry;-指向数组的指针,也是一个指针,直接指向数组的首元素
    int (*perter)[Max]=&int_arry;数组指针:指向数组首元素的地址的指针,其本质为指针

     本来对常量取地址是非法的,  但是标准组织没有定对数组名取地址是非法还是合法,所以因编译器而异,VC是合法的;一般说数组名是一个右值,本来不符合&的语法的,但是,数组却是一个对象,对一个数组对象取地址是合理的,C标准委员会经过衡量,认为维护一个对象的完整性 更重要, 因此允许&a,只不过,&a的意义,并非对一个数组名取地址,而是对一个数组对象取地址  。你可以定义一个多维数组,其区别就显而易见了!

 

    (4)指针与二维数组

      指针和二维数组的结合就与数组指针有关了。  int Arry_two[3][4]={   {0,1,2,3},   {4,5,6,7},   {8,9,10,11}  };int (*Row)[4]=Arry_two;正如上面所说,数组指针是指向数组首元素的地址的指针,即数组首元素地址的地址,其本质为指针。和一维数组一样,二维数组也有几个值值得讨论一下:Row、Arry_two、Arry_two[0]、&Arry_two[0][0],出了Row是变量外,其余常量。非常奇怪的是这几个值是完全一样的,大家自己可以试一试。那么他们之间到底是什么关系呢?

      Row= Arry_two,既然他们可以赋值,即他们的类型也该是一样,都为数组指针(在二维数组中我们给他取个别名:“行指针”),那么*Row(*Arry_two)会是什么,输出的结果和Row(Arry_two)完全一样。但这是他们的类型已经变量,*Row、*Arry_two、Arry_two[0]此时他们三个是同一类型了,都是普通指针了,int *p=*Row(*Arry_two、Arry_two[0]),他们指向这个二维数组中的一维数组了(二维数组中的一行)(同样给他们取个别名:“列指针”), 相信大家都有点明白了。那么试想我们要求取第i第j的值,怎么办呢?我总结了一些公式,大家可以看看:

       Arry_Tow==Arry_Tow[0];//第一行:行指针

      *Arry+j=Arry_Tow[0]+j;//第一行第j列:元素指针

      *(Arry+i)==Arry_Tow[i];//第i行:行指针

     *(Arry+i)+j==Arry_Tow[i]+j;//第i行第j列:元素指针

 

     第i行第j列:元素值(i,j>0)

    *(*(Arry_Tow+i)+j)==*(Arry_Tow[i]+j)==(*(Arry_Tow+i))[j]==Arry_Tow[i][j]
  

    第0行第j列:元素值(j>0)
   *(*Arry_Tow+j)==*(Arry_Tow[0]+j)==(*Arry_Tow)[j]==Arry_Tow[0][j]


    第i行第0列:元素值(i>0)
   *(*(Arry_Tow+i))==*Arry_Tow[i]==(*(Arry_Tow+i))[0]==Arry_Tow[i][0]


   第0行第0列值.*Arry_Tow[0]==*(Arry_Tow[0])
   **Arry_Tow==*Arry_Tow[0]==(*Arry_Tow)[0]==Arry_Tow[0][0]

  

 备注Arry_Tow[0][j]、Arry_Tow[i][0]、Arry_Tow[0][0]是Arry_Tow[i][j]的特例可以从它推导出。特别注意:指针[]运算与*运算的转换关系,上面Arry_Tow可以用Row替换,他们是等价的,但Row是变量Row++是合法,指向下一行了,而Arry_Tow++是非法的。

 

     (4)指针的指针   

       int Number=10;

       int *p=&Nmuber; 

       int **p=&p;  

 

     (5)指针数组

      指针数组:数组元素为指针的数组,其本质为数组。

int *per_Arry[4];
int var1=0,var2=2,var3=3,var4=4;
per_Arry[0]=&var1;// per_Arry[0]指向变量aa
per_Arry[1]=&var2;// per_Arry[1]指向变量bb
per_Arry[2]=&var3;// per_Arry[2]指向变量aa
per_Arry[3]=Arry_two[0];// per_Arry[3]指向数组Arry_two[0],是一个普通指针,对于二维数组中的“列指针”

cout<<"----------------------------------------------------------"<<endl
<<"指针数组:per_Arry"<<endl
<<"*per_Arry[0]="<<*per_Arry[0]<<endl
<<"*per_Arry[1]="<<*per_Arry[1]<<endl
<<"*per_Arry[2]="<<*per_Arry[2]<<endl
<<"*(per_Arry[3]+1)="<<*(per_Arry[3]+1)<<endl
<<"per_Arry[3][1]="<<per_Arry[3][1]<<endl
<<"----------------------------------------------------------"<<endl;

  

   (6)指针与引用 

     1、引用总是指向每个对象的,不能改变,定义是必须初始化。

     2、给引用赋值修改 是该引用所关联的对象的值,而不是是该引用与另一对象关联,引用一旦初始化就始终指向一个特定的对象。

      int var1=10,var2=20;

      int *p=&var1,*q=&var2;

      int &r1=var1,&r2=var2;

      p=q;//var1的值没变仍是10,而p本身变量了,它不再指向var1了而是指向var2了

     r1=r2;//var1的值改变成20了,而r1和r2的还是分别关联var1和var2

 

 时间有限,就写这些了,个人见解,仅供参考! 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值