C++语言中数组指针和指针数组彻底分析.docx

上传人:b****4 文档编号:11535216 上传时间:2023-03-19 格式:DOCX 页数:15 大小:31.29KB
下载 相关 举报
C++语言中数组指针和指针数组彻底分析.docx_第1页
第1页 / 共15页
C++语言中数组指针和指针数组彻底分析.docx_第2页
第2页 / 共15页
C++语言中数组指针和指针数组彻底分析.docx_第3页
第3页 / 共15页
C++语言中数组指针和指针数组彻底分析.docx_第4页
第4页 / 共15页
C++语言中数组指针和指针数组彻底分析.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

C++语言中数组指针和指针数组彻底分析.docx

《C++语言中数组指针和指针数组彻底分析.docx》由会员分享,可在线阅读,更多相关《C++语言中数组指针和指针数组彻底分析.docx(15页珍藏版)》请在冰豆网上搜索。

C++语言中数组指针和指针数组彻底分析.docx

C++语言中数组指针和指针数组彻底分析

C++语言中数组指针和指针数组彻底分析

近来在论坛中机场经常看到有关数组指针和指针数组的讨论。

这个是学习c++等语言中不可少的步骤,

不过向来指针的东西就是很有用但是也是很难用的东西,所以学习起来也不是很容易了。

近来本人也没有什么项目可以做的,所以就随便写的自己关于这些方面的理解,供同行参考,同时也可以把自己的错误理解暴露在阳光下,接受大家评判的洗礼。

file:

//PoweredByZosaTapo

file:

//dertyang@

################################

#                             #

#      基本知识              #

#                             #

################################

当然我们一切都是从最简单的内建类型开始,最后我会做一些推广。

先看一下基本的形式,我们从这里起步!

--------------指针----------------

inta=10;

int*p=&a;

-------------指针的指针-----------

intb=20;

int*p=&b;

int**p2p=&p;

-------------简单数组-----------------

intc[10];//整数数组,含有10个整数元素

         file:

//也就是说每一个元素都是整数

         

--------------指针数组--------------------

int*p[10];//指针数组,含有10个指针元素

           file:

//也就是说每一个元素都是指针

           

--------------数组指针--------------------

int(*p)[10];//数组指针,这个指针可以用来指向

            file:

//含有10个元素的整数数组

上面这些简单的形式是我们必须要首先理解,这个是基本的知识。

同时我们从上面也要得出一个很重要的知识提示:

c++语言层面上

关于变量声明的部分,后缀结合变量的优先级比前缀要高的。

看我们上面的例子的最后两个就明白了,我们为了实现数组指针的

声明我们不得不变通一下。

我们采用()来实现优先级的改变,实现了

数组指针的声明。

################################

#                             #

#     进一步提高知识         #

#                             #

################################

数组,数组的指针,指针的数组,概念太多了。

我接受概念一多的

时候,我就想把这些复杂的东西简单一下。

因为我太懒了,概念简化

一下,记住更容易一点。

所以我们这里要认识一下上面这些概念本质。

这样可以简化概念,减少记忆的难度。

先看一段程序。

#include

#include

usingnamespacestd;

intmain()

{

 intvInt=10;

 intarr[2]={10,20};

 

 int*p=&vInt;

 int**p2p=&p;

 

 int*parr[2]={&vInt,&vInt};

 int(*p2arr)[2]=&arr;

 cout<<"Declaration[intvInt=10]type=="<

 cout<<"Declaration[arr[2]={10,20}]type=="<

 cout<<"Declaration[int*p=&vInt]type=="<

 cout<<"Declaration[int**p2p=&p]type=="<

 cout<<"Declaration[int*parr[2]={&vInt,&vInt}]type=="<

 cout<<"Declaration[int(*p2arr)[2]=&arr]type=="<

 return0;

}

运行的结果如下:

(我在前面加了行号#XX)

#01Declaration[intvInt=10]type==int

#02Declaration[arr[2]={10,20}]type==int*

#03Declaration[int*p=&vInt]type==int*

#04Declaration[int**p2p=&p]type==int**

#05Declaration[int*parr[2]={&vInt,&vInt}]type==int**

#06Declaration[int(*p2arr)[2]=&arr]type==int(*)[2]

现在我们来分析一下结果。

因为我们已经具有了第一部分的基本知识,我们现在

可以很明确区别出来我们声明的类型。

这里主要有两个很重要的部分,我们不过

是就事讲事情,编译器是如何实现的原理不在这里讨论之列。

--------#02:

数组------------

现在看#02,想到了什么没有呀?

在编译器看来数组只是相对应类型的指针类型。

当我们把数组传递给函数作为参数的时候,传递的是指针,所以我们可以利用

参数来修改数组元素。

这个转化是编译器自动完成的。

voidf(int[]);

inta[2]={10,20};

f(a);//这行等价于编译器完成的函数转化f(int*p)

也就是说这里编译器自动完成了int[]类型到int*的转化,

注意是编译器完成的,也可以说是语言本身实现的,我们

对此只有接受的份了。

-------#05:

指针数组---------------

指针数组的编译器内部表示也是对应类型的指针。

------#06:

数组指针----------------

数组指针的编译器内部表示就是有一点特别了。

编译器(或者说是语言本身)有数组指针这个内部表示。

由于c++语言的类型严格检查的语言(当然还有一些是存在隐式类型转化的)

所以我们下面的写法是不能编译通过的。

{

file:

//---------编译不能通过--------------

intarr[3]={10,20};//注意是3个元素数组

int(*p2arr)[2]=&arr;//注意是指向2个元素数组的指针

file:

//---------编译不能通过--------------

}

################################

#                             #

#     初步小结               #

#                             #

################################

通过上面两个小节的内容,大家应该基本明白了,

数组,指针,指针数组,数组指针到底是怎么一回事情了吧。

-----------补充开始-----------------------

关于数组和指针的转化,以及我们使用指针(++,--)等来操作数组,

是基于数组在内存中是连续分布的。

但是我们使用“迭代器”的时候,情况是不一样的。

这个问题本文不讨论。

-----------补充结束---------------------

不过c++语言本身有很多诡异的地方(因为c++要考虑到跟c语言以及旧的c++版本兼容)。

内建类型的这些性质特征到了函数部分会有一点小的变化,不过如果你了解了编译器做了

什么以后的话,你也就不会太奇怪了。

不过关于函数部分的内容我下次再说了。

现在回到上面的内容。

我们这里还是讲一下内建类型。

显然一样类型的变量是可以互相赋值。

不过当然还有一些其他情况也是可以的,比如类型的宽化,关于类的继承体系问题等等。

当然了,不一样的类型一般来说是不能互相赋值,当然这里的例外就是强制转化,

类的继承体系等情况了。

看到这里就会明白下面的程序为什么会运行的了。

我这里也把下面的程序作为今天内容的总结:

#include

usingnamespacestd;

intmain()

{

 inta[2]={10,20};

 int*p=a;//根据上面说明,由于编译器的参与,两者类型转化后一致

 

 intvInt=10;

 int*parr[2]={&vInt,&vInt}; 

 int**p2p=parr;//上面分析,类型一致

 

 return0;

}

-------------函数指针部分下回再写了--------------------

上一次我们主要说明数组,指针,指针数组,数组指针这几个很基本的概念,

不过有一点上一次我没有提及,那就是我们使用typedef定义指向数组指针

的数组,这个叫法是不是很拗口呀,不过我们看具体的程序就清楚了。

file:

//----------数组--------------

inta[2]={10,20};

intb[2]={30,40};

file:

//简单的指向两个元素数组的指针

int(*p2arr)[2]=&a;

file:

//---------注意这里的变化--------

file:

//这里我们声明简单的类型P2ARR.可以这样描述

file:

//P2ARR是一种用户自定义的类型。

她的功能描述

file:

//是用来描述整数数组的,这个数组只能含有两个元素

typedefint(*P2ARR)[2];

P2ARRpp=&b;

pp=p2arr;//类型一致

file:

//这里我们定义指向指向数组指针的数组

P2ARRap2a[2]={&a,&b};

file:

//如果我们采用下面的写法则是错误的,原因看下面分析

file:

//P2ARRap2a[2]={a,b};//------错误写法-------

---------还有一点注意----------------

有人可以能会说,定义数阻指针的数组,下面这样的形式

好像也可以做到的。

int(*p2arr)[2][2];

咋一看,好像是的,但是仔细想想。

原来上面是定义

一个指针,这个指针是指向二维数组的指针,还是

数组指针,而不是数组指针数组。

这一点,应该很容易明白的。

上一次我讲到int[]数组声明与int*类型在函数参数等方面转换的例子。

这里有一点补充的,我们实际上现在还是不清楚数组来C++编译器层面的

表示,也就是我们不知道编译器使用什么样的结构形式来表示数组的。

所以

上面这些转换只能转换而已,我们不能有更多的假设。

----------我的想象,我的猜想--------

我是这样相像的,C++采用类似JAVA中Array类的形式来管理数组。

当然其中

有许多c++语言的特征,比如operator*,operator->等重载,但是这些类型

转换运算符的使用,只有编译器认为需要的时候才会调用的,因为数组表示本身

就是编译器内部结构的嘛。

目前到这里,我们也只能认为在函数参数使用等情况

下才发生int[]到int*的转换。

看下面的程序。

#include

usingnamespacestd;

voidchange(intpa[],intindex)

{

 pa[index]=200;

}

intmain()

{

 inta[3]={1,2,3};

 change(a,0);//这里编译器已经把inta[3],转换成int*指针传给了函数

            file:

//这个是语言实现细节,请参考《thec++programminglanguage》

 cout<

 return0;

}

但是我们上面的标注行

//P2ARR ap2a[2]={a,b};//------错误写法-------

则是应该这样理解:

数组就是数组,指针就是指针,虽然编译器需要的时候

会进行一定的转换,但是我们不能认为那些转换是“想当然“的。

上面这一行,编译器就没有为我们进行类型转换的,所以我们要自己动手了。

//P2ARRap2a[2]={&a,&b};//------正确写法-------

上面这些文字,主要一点就是说明了,有些行为是编译器完成的,而且还有一些

诡秘。

但是我们要知道编译器做了那些,那些是要完成自己做的。

重要的一点,

就是我们学会如何去简单使用typedef定义类型.

一、数组指针

(1)数组指针的声明

如:

int(*arrPtr)[10]=NULL;   //arrPtr为一指针,指向一个具有10个元素的数组,这些元素的类型为int型

注意:

上面的括号不能省略,如果没有括号,则变成一个指针数组:

int*arrPtr[10];表示arrPtr是一数组(而非指针),且该数组元素的类型为int型指针。

(2)数组指针的操作

*arrPtr表示arrPtr所指向的数组,因此(*arrPtr)[i]会返回数组的第i+1个元素值。

更进一步,(*arrPtr)[i]是等价于*((*arrPtr)+i)的,因此当i=0时,**arrPtr就表示数组第一个元素的值了。

(3)给数组指针赋值

注意以下例子:

inta[10];

int(*arrPtr)[10]=NULL;

arrPtr=a;      //错误:

指针类型符

原因是,数组的名字(比如上面例子中的a)会被隐式地转换为指针,但它指向的是数组的第一个元素,而不是指向整个数组(试想想,可以通过*(a+i)来获取某个元素的值,道理一样)。

因此上面的a会隐式地转换成int*类型(且指向a[0]),也就是说,若想把arrPtr指向数组a,需要作以下的显示类型转换:

arrPtr=(int (*)[10])a;   //10可省略

再看看以下列子:

int(*arrPtr)[10]=NULL;

intmatrix[3][10];

arrPtr=matrix;      //正确,因为matrix会隐式地转换为指向matrix[0][10]的指针,与arrPtr类型一致。

再往前走一步:

既然arrPtr=matrix,那么**arrPtr=**matrix,而**arrPtr表示的是matrix[0][10]第一个元素的值,因此**matrix也是表示matrix[0][10]第一个元素的值,即matrix[0][0],如此类推,如果matrix是一个多维数组(大于两维),那可通过**...**matrix这种形式来获取matrix[0][0]...[0][0]的值,即**...**matrix=matrix[0][0]...[0][0]。

二、指针数组

这比较常用,在此就不多赘述。

指针的指针,数组指针,指针数组(2008-11-1111:

26:

23)

标签:

杂谈 

分类:

编程学习

指针的指针

voidFindCredit(int**);

main()

        {

            intvals[]={7,6,5,-4,3,2,1,0};

            int*fp=vals;

            FindCredit(&fp);

            printf("%d\n",*fp);

        }

voidFindCredit(int**fpp)

        {

            while(**fpp!

=0)

            if(**fpp<0)break;

            else(*fpp)++;

        }

首先用一个数组的地址初始化指针fp,然后把该指针的地址作为实参传递给函数FindCredit()。

FindCredit()函数通过表达式**fpp间接地得到数组中的数据。

为遍历数组以找到一个负值,FindCredit()函数进行自增运算的对象是调用者的指向数组的指针,而不是它自己的指向调用者指针的指针。

语句(*fpp)++就是对形参指针指向的指针进行自增运算的。

但是因为*运算符高于++运算符,所以圆括号在这里是必须的,如果没有圆括号,那么++运算符将作用于二重指针fpp上。

指向指针数组的指针

    指针的指针另一用法旧处理指针数组。

有些程序员喜欢用指针数组来代替多维数组,一个常见的用法就是处理字符串。

char*Names[]=

        {

             "Bill",

             "Sam",

             "Jim",

             "Paul",

             "Charles",

             0

        };

main()

        {

            char**nm=Names;

            while(*nm!

=0)printf("%s\n",*nm++);

        }

先用字符型指针数组Names的地址来初始化指针nm。

每次printf()的调用都首先传递指针nm指向的字符型指针,然后对nm进行自增运算使其指向数组的下一个元素(还是指针)。

注意完成上述认为的语法为*nm++,它首先取得指针指向的内容,然后使指针自增。

    注意数组中的最后一个元素被初始化为0,while循环以次来判断是否到了数组末尾。

具有零值的指针常常被用做循环数组的终止符。

程序员称零值指针为空指针(NULL)。

采用空指针作为终止符,在树种增删元素时,就不必改动遍历数组的代码,因为此时数组仍然以空指针作为结束

 指向指针的指针,很早以前在说指针的时候说过,但后来发现很多人还是比较难以理解,这一次我们再次仔细说一说指向指针的指针。

先看下面的代码,注意看代码中的注解:

#include

#include

usingnamespacestd;

voidprint_char(char*array[],intlen);

//函数原形声明voidmain(void){

//-----------------------------段1----------

char*a[]={"abc","cde","fgh"};//字符指针数组

char**b=a;

//定义一个指向指针的指针,

并赋予指针数组首地址所指向的第一个字符串的地址也就是abc\0字符串的首地址

cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<

  //-----------------------------段2----------

  char*test[]={"abc","cde","fgh"};

//注意这里是引号,表示是字符串,以后的地址每加1就是加4位(在32位系统上)

  intnum=sizeof(test)/sizeof(char*);

//计算字符串个数

  print_char(test,num);

  cin.get();

   }

voidprint_char(char*array[],intlen)

//当调用的时候传递进来的不是数组,而是字符指针他每加1也就是加上sizeof(char*)的长度

  {

  for(inti=0;i

  {

  cout<<*array++<

  }  }  下面我们来仔细说明一下字符指针数组和指向指针的指针,段1中的程序是下面的样子:

  char*a[]={"abc","cde","fgh"};

  char**b=a;

  cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<

  char*a[]定义了一个指针数组,注意不是char[],char[]是不能同时初始化为三个字符的,定义以后的a[]其实内部有三个内存位置,分别存储了abc\0,cde\0,fgh\0,三个字符串的起始地址,而这三个位置的内存地址却不是这三个字符串的起始地址,在这个例子中a[]是存储在栈空间内的,而三个字符串却是存储在静态内存空间内的const区域中的,接下去我们看到了char**b=a;这里定义了一个指向指针的指针,如果你写成char*b=a;那么是错误的,因为编译器会返回一个无法将char**[3]转换给char*的错误,b=a的赋值,实际上是把a的首地址赋给了b,由于b是一个指向指针的指针,程序的输出

cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<

 

结果是

 

abccdefgh

可以看出每一次内存地址的+1操作事实上是一次加sizeof(char*)的操作,我们在32位的系统中sizeof(char*)的长度是4,所以每加1也就是+4,实际上是*a[]内部三个位置的+1,所以*(b+1)的结果自然就是cde了,我们这时候可能会问,为什么输出是cde而不是c一个呢?

答案是这样的,在c++中,输出字符指针就是输出字符串,程序会自动在遇到\0后停止。

我们最后分析一下段2中的代码,段2中我们调用了print_array()这个函数,这个函数中形式参数是char*array[]和代码中的char*test[]一样,同为字符指针,当你把参数传递过来的时候,事实上不是把数组内容传递过来,test的首地址传递了进来,由于array是指针,所以在内存中它在栈区,具有变量一样的性质,可以为左值,所以我们输出写成了:

 

cout<<*array++<

当然我们也可以改写为:

 

cout<

这里在循环中的每次加1操作和段1代码总的道理是一样的,注意看下面的图!

到这里这两个非常重要的知识点我们都说完了,说归说,要想透彻理解希望读者多动手,多观察,熟能生巧。

下面是内存结构示意图:

 

 

最近好像是跟指针卯上了,发现以前真的学得不好,太多的东西是模模糊糊的。

可能是因为S现在做的项目有太多地方使用到指针,而且有的时候用到复杂的指针,所以才觉得有必要好好的研究下,这样可以减轻S的负担,也为我以后做准备吧。

要搞清一个指针首先必须搞清指针的四方面的内容:

指针的类型,指针所指向的类型,指针的值或者叫

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 高等教育 > 教育学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1