• 沒有找到結果。

4.5.1 指针数组

如果一个数组的元素是指针类型数据,则该数组称为指针数组。一维指针数组的定义形 式为:

类型符 *数组名[常量表达式];

其中,数组名之前的*表明定义指针数组,类型符表明数组元素所指对象的数据类型,即 元素指针的基类型,方括号中常量表达式的值表示数组元素的个数。例如

int *p[10];

定义了一维指针数组 p,它有 10 个元素,每个元素都是指向 int 型变量的指针变量。和普 通一维数组名一样,数组名 p 也代表第一个元素即 p[0]的地址。

注意,在*与数组名之外不能加圆括号,否则变成指向一维数组的行指针变量(前面已详 析)。如:

int (*p)[10];

则定义指向含 10 个整型元素的一维数组的行指针变量。

类似地可定义多维指针数组。

指针数组适合表示一批指针数据。例如,对多个字符串进行排序,这些字符串可用一个 字符型的二维数组存储,每行存储一个字符串。排序时一般要多次交换两个字符串在数组中的 位置,这样较费时。如用指针数组,让其元素指向各串,就不必交换两个字符串的位置,而只 交换指针数组中两个元素的指向。

【例 4.13】编写 void sort(char *p[],int n),用选择法(在例 4.2 中已介绍)将指针数组 p 元素值按元素所指字符串大小从小到大排序,即让 p[0]指向最小的串,p[n-1]指向最大的串,

而不改变字符串的存储位置。编写 main()等相关函数构成完整程序。要求从键盘输入各字符串,

且输出排序结果。

分析:从要求可知,sort()开始执行时,若数组 p 中 6 个元素及其指向的字符串如图 4-18

(a)所示,sort()执行后,6 个元素的指向发生改变,结果如图 4-18(b)所示。此处对数组 p 选择排序,每次确定 p 两元素值大小关系时,不是比较两元素本身,而是比较两元素指向的串。

因此将例 4.2 中的数组元素比较运算改用 strcmp(p[k],p[j])即可。

程序如下:

//*****ex4_13.cpp*****

#include <iostream>

#include <string>

using namespace std;

#define N 6 void main()

{ void sort(char *[],int),write(char *[],int);

char *name[N],str[N][30];

int i;

for(i=0;i<N;i++)

{ name[i]=str[i]; //str[i]指向二维数组 str 的第 i 行首字节

(a) (b)

for(i=0;i<n;i++) cout<<p[i]<<endl;

cout<<endl;

}

对于上例而言,若待排序的各字符串在编写程序时就能确定,则也可用它们来初始化 name 数组如下:

char *name[]={"Russia","America","Japan","France","Britain","China"};

这样,name 的元素分别指向大括号中的各字符串。

图 4-19 多级指针示意图 例如:

int **pp;

定义了指针变量 pp,它能指向另一个指针变量,该指针变量又能指向一个整型变量。pp 的前面有两个“*”号,由于指针运算符“*”是按自右向左顺序结合的,因此**p 相当于*(*p),

可以看出(*pp)是指针变量形式,它前面的“*”表示指针变量 pp 指向的又是一个指针变量,“int”

表示后一个指针变量指向的是整型变量。

要定义图 4-19 所示的变量,可如下定义:

int a=22,*ap=&a,**pp=&ap;

ap可用*pp 表示,a 可用*ap 和**pp 表示。

类似地可定义三级指针,例如:

char ***p;

编程时根据需要来确定指针变量的级数。

多级指针同样可以进行前面介绍的指针运算,只是指向关系多了一层而已,参加运算的 两指针级别要一致。

指针数组元素的地址是多级指针,指针数组名代表首元素的地址,是多级指针常量。如 同一维数组元素及其地址可以用指针变量表示(见表 4-1)一样,一维指针数组元素及其地址 也可以用二级指针变量表示。前面 4.3.2 节说过,一维数组名和指针变量作函数形参时可以通 用,类似地,一维指针数组名和二级指针变量作函数形参时也可通用,如例 4.13 中函数 sort() 的形参 p 的说明 char *p[]也可写成 char **p。

4.5.3 带形参的 main 函数

前面介绍的程序中,main 函数都没有形参,其实,main 函数也可以带形参,用来接收来 自命令行的实参。这个命令行是指运行 main 函数所在程序的命令。

带形参的 main()函数的一般形式是:

int main (int avgc, char *argv[] ) // 或 main (int avgc, char **argv) { … }

在操作系统命令状态下,启动、运行一个程序的命令行格式:

程序文件名 参数 1 参数 2 …… 参数 n

程序文件名和各参数之间用空格分隔。程序文件名、各参数都是字符串,统称为命令行参 数。此处字符串可以不带双引号,若字符串本身含有空格,则要用双引号括起来。

程序中的 main 函数利用其形参接收命令行的实参信息。其中 argc 表示命令行中参数的个 数,程序文件名字符串也算作其中一个,argv 是一个指向字符串的指针数组(或二级指针)。

argv[0]指向命令行中第一个字符串,即程序文件名串,argv[1]指向命令行中第二个字符串,其 余依次类推。

下面通过实例说明命令行参数是如何传递的。

【例 4.14】编写程序,要求输出运行该程序时所输入的命令行参数。源程序文件名为

&ap pp

&a ap,*pp

22 a,*ap,**pp

ex4_14.cpp,经编译连接后生成的可执行程序为 ex4_14.exe。

//*****ex4_14.cpp*****

#include <iostream>

using namespace std;

int main(int argc,char *argv[]) //或 main(int argc,char **argv) { int k;

cout<<"argc=" <<argc<<endl;

for(k=0;k<argc;k++)

cout<<"argv"<<k<<":"<<argv[k]<<'\n';

cout<<"\n";

return 0;

}

若运行该程序时输入的命令行是:

ex4_14 good better best 则输出结果:

argc=4 argv0:ex4_14 argv1:good argv2:better argv3:best

一旦命令行输入完毕,操作系统接收并分析命令行内容,辨别出其中字符串个数并将个 数(4)传给形参 argc;将四个字符串:ex4_14、good、better、best 保存到特定位置,并将它 们首地址分别传给字符指针数组元素 argv[0]、argv[1]、argv[2]、argv[3],如图 4-20 所示。

argv argv[0] e x 4 _ 1 4 \0 argv[1] g o o d \0 argv[2] b e t t e r \0 argv[3] b e s t \0

图 4-20 命令行参数指针数组示意图

4.6 引用

程序可以用变量名直接访问变量,也可以用指针变量间接访问变量,另外,C++中,程序 还可以采用引用(reference)来访问变量。用引用作函数参数和返回值可以扩充函数传递数据 的能力。

4.6.1 变量的引用

变量的引用就是变量的别名。一个引用总依附于某个实体,如变量、类对象(后面章节介绍),

定义引用时必须进行初始化,说明是谁的引用,换句话说,总是为某个特定实体定义引用,例如:

int a;

int &ar=a;

为变量 a 定义了一个引用 ar,ar 也表示 a 的存储单元,此处&是引用声明符,不是取地址 运算符,&前面的 int 说明 ar 是整型变量的引用,即被引用的变量应该是整型变量。

可以在定义变量的同时为它定义引用,上面两行也可合并为:

int a, &ar=a; //还可同时初始化 ar 引用的变量:int a, &ar=a=100;

后续程序中变量 a 都可用它的引用 ar 替换,例如:

int *p=&ar; //&ar代表&a,定义指向 a 的指针变量

int &ar2=ar; //为引用 ar 定义引用 ar2,因 ar 代表 a,相当于为 a 再定义另一引用 ar2 ar=100; //等效于 a=100;

cin>>ar; //等效于 cin>>a;

cout<<ar; //等效于 cout<<a;

指针变量的引用定义格式如下:

int *q, *&qr=q; //还可同时初始化 qr 引用的指针变量:int a, *q, *&qr=q=&a;

此后 qr 就可作 q 用。

前面已介绍,变量或存储单元的间接访问形式为“*指针”,也可以为间接访问形式的变 量或存储单元定义引用,例如:

int a, *p=&a, &ar=*p; //定义 ar 是*p 的引用,而*p 是 a 的间接访问形式,故 ar 也就是 a 的引用 注意,不能定义引用数组,也不能定义指向引用的指针,下面的引用定义有语法错误。

int &refer[10], &*pr ;

变量引用的作用主要体现在作函数参数和返回值的情况。

4.6.2 引用作函数参数

前面多次提到,在函数调用时,实参的值传递给形参变量,函数在执行过程中改变了形 参的值,但对应的实参不会被改变,这就是所谓的函数参数值单向传递现象。为了让被调用函 数可以改变主调函数中变量的值,4.2.4 节介绍了用指针作函数参数的方法,此时实参向形参 传递指针值,被调函数通过形参变量间接访问主调函数中的变量。若将函数形参说明为变量的 引用(或引用型变量),也能达到修改实参变量的目的,而且编程更简洁。对于例 4.7 而言,

将 swap 函数修改如下:

void swap(int &r1,int &r2) //说明形参为变量的引用 { int t;

t=r1;

r1=r2;

r2=t;

}

相应的函数调用语句改为:

swap(a,b);

定义 swap()时说明形参为变量的引用,但没指定是哪一变量的别名。调用函数时,系统把 实参 a、b 变量名(非值)传递给形参 r1、r2,r1、r2 分别

变为 a、b 的引用(别名),即 r1、r2 也分别表示实参 a、b 对应的存储单元,如图 4-21 所示;此时,swap()修改形参 r1、r2 就是修改实参 a、b。此处本质上也是传递地址,与 指针变量作形参不同的是,此时形参不需占用临时存储空 间,而是直接引用 a、b,用引用作函数参数显得表达简单、

自然。

交换

实参 a,别名 r1

实参 b,别名 r2

图 4-21 交换两引用型形参的值

与引用型形参对应的实参可以是变量,如上面调用 swap(a,b)中的实参 a、b 是已定义的变 量;另外,实参也可以是指针所指向的变量或(动态)存储单元,例如,假设指针 x、y 已经 指向内存单元,则调用 swap(*x,*y)将交换 x、y 所指内存单元的值。

4.6.3 引用作函数返回值

函数也可返回变量(或存储单元)的引用,其定义一般格式是:

类型名 & 函数名(形参表) {

return 变量、变量的引用或用“*指针”形式表示的存储单元;}

return后的变量不能是本函数中的局部变量,因为函数执行完毕返回主调函数后,函数中 的局部变量全部释放,主调函数再用其引用去访问它,不能保证结果正确。例如,下面的函数 在编译时会有警告提示:

int & minp(int &x,int &y) {int q;

q=x<y?x:y;

return q;

}

因为该函数试图返回其局部变量 q 的引用。return 后的变量应该是主调函数可寻址或可见 的变量或内存单元,如全局变量。该函数可改为

int & minp(int &x,int &y) { return x<y?x:y;}

该 minp()被调用时,x、y 变成实参变量的引用,显然,主调函数可找到实参变量。

习题 4

一、选择题

1.设有 int x[][3]={ {0},{1,2},{3,4,6},{5}}; 则 x[1][1]的值是( )。

A.3 B.2 C.6 D.4

2.在下面的二维数组定义中,正确的是( )。

A.int a[3][]; B.int a[][3];

C.int a(3)(3); D.int a[][3]={{1,2},{3}};

3.下面哪个定义或语句序列能使 p 指向 a?( )

A.int a,*p=a; B.int a,*p; *p=a;

C.int a,*p; *p=&a; D.int a,*p=&a;

4.若有:double a[10], *pa=a;,要将 10 赋值给 a 中的下标为 5 的元素,不正确的语句是( )。

A.pa[5]=10; B.*(pa+5)=10;

C.*(a[0]+5)=10; D.a[5]=10;

5.设有 int a[10], *p=&a[4] ; 则下面哪种表示与 a[9]不等价?( )

A.*(a+9) B.*(p+5) C.p[5] D.p+5

6.设有 int a[10], *p=a+5 ; 则下面哪种表示与 a[3]不等价?( )

A.*(a+9) B.*(p-2) C.p[-2] D.p+2

7.设有 int a[20], *p=a; 则下面哪个与 a[1]不等价?( )

A.p[1]; B.*++p; C.*(a+1); D.*++a;

8.已知 char a[][20]={"hunan","jiangxi","shandong"};,语句 cout<<a[3];得到的输出是( )。

A.a B.shandong C.输出结果不确定 D.数组定义有错

9.若函数形参是数组,则对应的实参( )。

A.只能是数组名 B.只能是指针

C.任何类型的数据 D.可以是数组名或指针

10.若函数形参是指针变量,则对应的实参( )。

10.若函数形参是指针变量,则对应的实参( )。

相關文件