C++指针与数组的混合运算(补充指针的内容) - 范文中心

C++指针与数组的混合运算(补充指针的内容)

11/16

指针的计算

1、指针与数组(对指针的透彻理解)

1、两条基本准则:

a 、首先要明白,指针运算符的作用,我用一言以概之,你在哪里使用都不会错。指针运算符*的作用是求出*后面所指地址里的值。因此只要*后面的变量表示的是一个地址就可以使用*运算符,来求出这个地址中的值,你不用管这个地址的表示形式是怎样的,只要是地址就可以使用*来求出地址中的值。

b 、[ ]这个运算符的的运算法则是,把左侧的地址加上[ ]内的偏移量然后再求指针运算,注意有[ ]运算符的地方就有个隐含的指针,比如x[2]表示的就是将指针x 偏移2个单位量后再求指针运算。也就说x[2]与*(x+2)是相等的。

2、对数组的讲解:

a 、对数组名的理解:

大家都知道数组名是一个指针,但是这个指针是一个常量,也就是说不能改变他所指向的地址,比如你不能把另一个变量的地址赋给数组名,比如a=&i; 其中a 是数组名,这就是错误的。再比如char a[11]; a=”hy”;这也是错误的,因为a 是数组名是常量,不能把字符串常量”hy”的首地址赋给数组名以此来改变数组名所指向的地址,这是很多人容易范的错误,要把字符串赋给数组应是这样char a[11]=”hy”; 这是初始化,初始化不会改变数组名所指向的地址。b 、一维数组

我们基本上都是这样理解一维数组的数组名的,即一维数组的数组名表示的是数组的首地址,比如一维数组a[11]中的数组名a 就表示数组的首地址。首先在这里要明白,这种理解是错误的,因为数组名不是表示的是数组的首地址,他是表示的数组中第一个元素的地址,因此数组名a 其实表示的是&a[0]的地址,而表示数组的首地址的是&a,因为a 表示的是一个包含有4个元素的一维数组,因此&a表示的就是一个包含有4个元素的一维数组的地址,也就是说&a的地址中包含了4个元素。注意:a 和&a表示的地址是相同的,使用输出语句可以很明显的看到。也就是说数组中第一个元组的地址和数组的首地址是相等的,但是他们的意义却有很大不同,在后面的计算中你将会看到。

c 、二维数组:

比如二维数组b[3][4],我们首先从一维开始分析,众所周知b[0],b[1]分别表示的是二维数组中第一行第一个元素和第二行第一个元素的地址,也就是说b[0]是与&b[0][0]等价的,b[1]是与&b[1][0]等介的。因此数组名b 就与&b[0]等价的,因为b[0]包含有4个元素,因此数组名b 或者&b[0]表示的是二维数组中第一行所包含的一维数组的地址,说简单一点,就是说二维数组名是二维数组中第一行的行地址。因此二维数组名b 所包含的地址中包含有二维数组中第二维中元素的个数的一维数组,也就是b 的地址中包含一个含有4个元素的一维数组的地址(也就是所谓的数组的数组了) 。

d 、N 维数组:通过上面对一二维数组的语言描术,可以总结一些规律(因为维数大于3的数组就难以用语言来描术,只能通过数学计算来解决地址问题) :

1:数组名a 是与&a[0]等价的,不管是多少维数组,这条规律都是必定的。

2:a[][][][](中括号中的下标省掉,只要括号中的下标不超过界限都适合本规律) 只要不是表示的最后一维,也就是

说他表示的是地址而不是具体的元素,则a[][][][]都与&a[][][][][0]等价的。

e 、为什么我们要把a[]解释为&a[][0]:

在计算时你就会看到,a[]并不是表示的真正的地址,而&a[][0]才是表示的真正的地址,也就是说对于a[1]+1不等于a[2],因为a[1]并不是表示的具体的地址,因为他所表示的地址是&a[1][0],因此a[1]+1=&a[1][0]+1=&a[1][1],我们看到了,只有把a[]表示为&a[][0]才能和后面的整数直接相加,这一点非常重要,一定要记住。

2、指针和[ ]的混合计算法则(以下假设b 为二维数组b[3][4]):

1、首先要明白,指针运算符的作用,我用一言以概之,你在哪里使用都不会错。指针运算符*的作用是求出*后面所指地址里的值。因此只要*后面的变量表示的是一个地址就可以使用*运算符,来求出这个地址中的值,你不用管这个

地址的表示形式是怎样的,只要是地址就可以使用*来求出地址中的值。

2、[ ]这个运算符的的运算法则是,把左侧的地址加上[ ]内的偏移量然后再求指针运算,注意有[ ]运算符的地方就有个隐含的指针,比如x[2]表示的就是将指针x 偏移2个单位量后再求指针运算。也就说x[2]与*(x+2)是相等的。

3、对于b[1]这样的地址,一定要表示为&b[1][0]再进行偏移计算,比如对于b[1]+1,这不是直接在对b[1]加1,也就是b[1]+1不等于b[2],因为b[1]表示的是第二行行1个元素的地址,对其加1,应该表示的是第二行第二个元素的地址,也就是&b[1][1],而b[2]则表示的是第二行第一个元素的地址,因此错误,所以在计算时应把b[1]转换为&b[1][0]之后,才能直接进行地址的偏移,也就是说b[1]+1=&b[1][0]+1=&b[1][1],这样才能得到正确的结果,并且不会出错。

4、对于有小括号的地方,一定不要省略小括号。比如(&b[1])[1]与&b[1][1]将表示的是不同的结果,第二个是显然的,对于第一个(&b[1])[1]=*((&b[1])+1)=*(&b[1]+1)=*(&b[2])=b[2],可以看到,表示的是第3行第1个元素的地址,因此这两个的结果是显然不一样的。因此对于(b+1)[1]这样的运算,不能省略小括号,即(b+1)[1]=(&b[1])[1]=*((&b[1])+1)=*(&b[1]+1)=*(&b[2])=b[2],如果省略了小括号,则是(b+1)[1]=&b[1][1],这将是不易发现的错误。因此这是两个完完全全不同的符案。

5、对于指针,只要指针指向了数组,不管他是多少重指针,都可以使用这些计算法则。

注意:对于有指针运算符*和下标运算符[]的混合运算时,最好使用计算表达式的方式采用数学的方法来得出正确的结论,因为用语言来描术这类的综合运算,弯过来弯过去的很容易出错,如果用数学方式,结合C 中的运算符优先级来运算,只要弄懂指针的首地址,然后从优先级高的运算符开始运算,这样得到的结果是不会出错的。比如,对于(*(b+1))[1]其中b 是二维数组b[3][4]。如果用语言去描术很容易出错,如果掌握好下面的数学计算方法,然后逐步计算,就很容易得出正确答案,而且简单易懂,只要把数组b 表示的地址弄懂,也就是b=&b[0],然后再计算,就很容易得到答案了。下面先用语言描术一些基本理论,然后再讲解具体的计算方法。

下面讲解一些示例(假设b 为二维数组b[3][4]):

例1:

*(b+1)

=*(&b[0]+1)

=*(&b[1])

=b[1]

例2:

*(*(b+1)+1)=*(*(&b[0]+1)+1)

=*(*(&b[1])+1)

=*(b[1]+1)

=*(&b[1][0]+1)

=*(&b[1][1])

=b[1][1]

例3:

(*(b+1))[1]

=(*(&b[0]+1))[1]

=(*(&b[1]))[1]

=(b[1])[1]

=*((b[1])+1) //把b[1]拆分为*(b+1);

=*(&b[1][0]+1)

=*(&b[1][1])

=b[1][1]

例4:

(b+1)[1]

=(&b[0]+1)[1]

=(&b[1])[1]

=*(&b[1]+1)

=*(&b[2])

=b[2]

例5:

*(b+1)[1]

=*(&b[0]+1)[1]

=*(&b[1])[1]

=*(*(&b[1]+1)) //注意[ ]运算符优先级比*指针运算符高,把以应先计算(&b[1])[1]=*(&b[1]+1)

=*(*(&b[2]))

=*(b[2])

=*(&b[2][0])

=b[2][0]

方法2:*(b+1)[1]=**((b+1)+1)=**(b+2)=*(*(b+2))=*(*(&b[0]+2))=*b[2]=b[2][0]

例6:

(*(b+1))[5]

=(*(&b[1]))[5]

=(b[1])[5]

=*(b[1]+5)

=*(&b[1][0]+5)

=*(&b[1][5])

=b[1][5]

3、对于多重指针的计算。

比如int a[11]={0}; int *p1=a; int **p=&p1; 注意,因为int **p=&p1与int **p; p=&p1相同,可以看到p=&p1因此在计算的时候,只有出现p 的地方,都可以把其替换为&p1,同理p1=a;因此可以把p1替换为a ,最后可以把p 替换为&a来计算,这样的话,在对多重指针的计算时就比较简单了。比如*p+1=*(&a)+1=a+1=&a[0]+1=&a[1],同理p+1=&a+1;当然&a+1将是一个不明确的地址,这样看实际情况来处理。如果a 不是数组,而是一个整型常量,比如int a;则不存在指针的相加减运算,因为对整型变量的地址偏移,必定会得到一个不明确的地址。指针指向整数变量(其他非数组变量也是类似的) 只能有p+0这样的运算,只要p+1就会偏移到不确定地址。

4、对于指针数组的计算int *p[4]

对于指针数组的计算可以采用二维数组的计算法则:比如int *p[4],则*p+1=*(&p[0])+1=p[0]+1=&p[0][0]+1=&p[0][1];因此*p+1表示的是指针p[0]所指向的第2个元素的地址,但要注意,如果p[0]不是指向的数组,则这是错误的表达式,比如p[0]=&i; 其中变量i 是int 型的,则*p+1将会是一个不可预测值的地址,使用*(*p+1)将得到一个不可预测的值,如果p[0]指向的是一个一维数组,比如p[0]=a; 其中a 是一个一维数组,则*p+1表示的就是数组a 中的第2个元素(下标为1) ,使用*(*p+1)将会输出这个值。因此&p[0][1]应把p[0]整体理解为一个指针,你也可以把p[0]使用不是数组类型的指针来代替,比如q ,则p[0][1]就是q[1],然后再来看q 是指向的什么。因此对于指针数组同样可以使用上面的法则进行计算,但要把计算之后的结果p[][]进行相应的简单理解,即,应把p[]理解为是一个指针。

5、数组指针与二维数组讲解:

下面我们将以y[4]={1,2,3,4}这个一维数组为例来层层讲解,指针和数组的关系。

1、数组指针:

定义形式为:int (*p)[4];表示定义了一个指向多维数组的指针,即指针p 指向的是一个数组,这个数组有4个元素,对这个指针p 的赋值必须是有4个int 元素的数组的地址,即只要包含有四个元素的数组的地址都能赋给指针p ,不管这个数组的行数是多少,但列数必须为4。即int y[4],x[22][4];都可以赋给指针p 。赋值方式为p=&y或p=x,对于&y和二维数组数组名前面已讲过,&y中的地址是一个包含有4个元素的一维数组的地址,二维数组名x 也是一个含有4个元素的一维数组的地址,因此可以这样赋值。而这样赋值将是错误的p=y,或者p=x[0]; 因为y 和x[0]的地址只包含一个元素,他们不包含一个数组,因此出错。

2.注意()必须有,如果没有的话则,int *p[4];则是定义了一个指针数组,表示每个数组元素都是一个指针,即p[2]=&i;指向一个int 型的地址,而*p[2]则表示p[2]所指向的元素的值。

3.初始化数组指针p :

a 、当把int y[4]赋给指针p 时p=y将是错误的,正确的方式为p=&y因为这时编译器会检查赋给指针p 的元素是否是含有四个元素的数组,如果是就能正确的赋值,但语句p=y中的y 代表的是数组y[4]第一行第一列的元素的地址也就是&y[0]的地址,因此y 指向的地址只有一个元素,而指针p 要求的是有4个元素的数组的地址,因此语句p=y将出错。而&y也表示的是一个地址,但&y包含了数组y[4]的第一行的所有元素,在这里&y包含有4个元素,因此p=&y才是正确的赋值方法,在这里要注意,数组的某行的行地址是第本行的第一个元素的地址是相同的。

b 、把x[22][4]赋给指针p 有几种方法,方法一:p=x;我们这样来理解该条语句,首先x[0]表示的是二维数组x[22][4]的第1行第一列元素的地址,这个地址包含一个元素,这是显而易见的,而数组名x 也表示一个地址,但这个地址包含的是一个数组(即数组的数组) ,这个数组是包含4个元素的数组,这4个元素就是数组x 第一行的4个元素,也就是说x 表示的是数组x[22][4]的第1行的行地址,即数组名x 就包含了数组x[22][4]第1行的4个元素,因此这种赋值方式是正确的。这时指针p 就相当于是数组名一样,比如p[2][1]访问的就是数组x 的第3行的第2个元素。

c 、方法二:p=x+1或者p=&x[1];注意必须要有地址运算符&,同理语句&x[1]表示的是数组x[22][4]第2行的行地址,因为x[1]表示的是数组x[22][4]第的第2行第1列的元素的地址,因此&x[1]表示的就是数组x 的第2行的行地址,因为&x[1]这个地址包含了一个数组,这个数组的起始地址是从x[1]这个地址开始的,这,即数组x[22][4]

中x[1]这一行的4个元素。在这一行中包含了4个元素。这时指针p 的起始地址是&x[1],所以p[0][1]不再是访问的x 的第一行第二个元素,而是访问的x 的第二行第二个元素。

d 、注意,再次提示,数组的某行的行地址是与本行的第一个元素的地址是相同的。

4、数组指针的计算

数组指针的计算与上面介绍的指针与数组的计算法则相同,但计算之后表示成的二维数组形式p[1][2]要注意看指针p 的起始地址是哪一行,然后才能确定输出哪个元素,比如p 起始地址是二维数组的第1行,则p[1][2]表示的是第2行第3个元素,但若p 指向的是二维数组的第二行(也就是起始地址在二维数组第2行) ,则这时p[1][2]就表示的是第3行第3个元素了。

6、int (&p)[4]引用

int (&p)[4]引用和指针有一些差别因为对引用赋值必须是变量的名字,所以int y[4];int (&p)[4]=y;是正确的而用&y将得到一个错误的结果,同样int x[22][4]; int (&p)[4]=x[1];才是正确的,不能用x+1或者&x[1]来初始化引用。int (&p)[4]=x也将得到一个错误的结果,只能是int (&p)[4]=x[0];对于引用来说语句p[0][1]将是错误的,只能是p[0],p[1],依次访问p 所引用的对象的第一个元素的值和第二个元素的值,int (&p)[4]=x[1];p[1]将访问的是p 所引用的第二个元素的值也就是数组x 第二行第2个元素的值。


相关内容

  • 测绘程序设计课程实习报告模板
    一.实习目的 <测绘程序设计>是一门理论与实践并重的课程,课程设计是测量数据处理理论学习的一个重要实践环节,可以看做是在学习了专业基础理论课<误差理论与测量平差基础>课程后进行的一门实践课程,其目的是增强学生对测量平 ...
  • [程序设计基础]实验指导书
    实验1 C的实验环境和C语言的数据类型 ⒈ 实验目的 ⑴ 了解在具体的语言环境下如何编辑.编译.连接和运行一个C程序. ⑵ 通过运行简单的C程序,初步了解C源程序的特点. ⑶ 掌握C语言数据类型,熟悉如何定义一个整型.字符型和实型的变量,以 ...
  • 计算机软件资格考试
    计算机技术与软件专业资格(水平)考试 报考指南 1.什么是计算机技术与软件专业技术资格(水平)考试? 计算机技术与软件专业技术资格(水平)考试(以下简称计算机软件资格考试),是国家人事部和信息产业部对全国计算机与软件专业技术人员进行的职业资 ...
  • 中兴公司计算机专业应届生笔试试卷(03)
    本试卷分三部分,公共部分.C++部分和JAVA 部分,其中公共部分为必考部分, C++与JAV 部分,两者选一完成.试卷满分100分. 公共部分(50分) 1:作业调度程序从处于___ D ____状态的队列中选择适当的作业投入运行.(3分 ...
  • 数据结构导论试题1
    全国2004年10月高等教育自学考试 1.要将现实生活中的数据转化为计算机所能表示的形式,其转化过程依次为( ) A.逻辑结构.存储结构.机外表示 B.存储结构.逻辑结构.机外表示 C.机外表示.逻辑结构.存储结构 D.机外表示.存储结构. ...
  • 嵌入式经典面试题
    嵌入式经典面试题六: 关键字static的作用是什么? 这个简单的问题很少有人能回答完全.在C语言中,关键字static有三个明显的作用: 1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变. 2) 在模块内(但在函数体 ...
  • 矩阵旋转反射
    C++课程设计实验报告 姓名 汤铃铃 学号 0701510421 班级 07015100 任课教师 时间 08.10.11 评定难易级别 B教师指定题目 4-1 矩阵旋转反射 实验报告成绩 1. 实验内容: 1.1 程序功能介绍 实现矩阵的 ...
  • 数据结构试卷(附答案)
    计算机专业数据结构试题 一.单选题(每小题2分,共12分) 1.在一个单链表HL 中,若要向表头插入一个由指针p 指向的结点,则执行( ) . A . HL =p : p 一>next=HL B . p 一>next=HL :H ...
  • 汇编语言课后习题及答案 按章节 超全面
    `00第一章课外练习题 一.单项选择题: 1.从键盘输入的字符,在计算机内存储的是它的( A ) (A)二进制编码 (B)四进制编码 (C)八进制编码(D)十六进制编码 2.6位无符号二进制数能表示的最大十进制数是( B ). (A) 64 ...
  • 逆向 C -- 识别类及其构造函数 - FISH 的专栏 - CSDNBlog
    逆向 C++ 这些年来,逆向工程分析人员一直是凭借着汇编和 C 的知识对大多数软件进行逆向工程的,但是,现在随着越来越多的应用程序和恶意软件转而使用 C++语言进行开发,深入理解 C++ 面向对象方式开发的软件的反汇编技术就显得越发的必要. ...