第 6章 指 针 和 引 用
6.1 指 针
6.2 引 用
6.1 指 针
6.1.1 指针的概念所谓指针是一个存储单元的地址值,用来存放地址 ( 即指针 ) 的变量也称指针变量 。
在上下文意义明确的情况下,常常将指针变量也简称为指针 。
指针变量和普通变量一样占有一定的存储空间,但它与普通变量的区别在于指针变量的存储空间中存放的不是普通的数据,
而是一个地址值 —— 指针 。
6.1.1.1 指针的声明指针是一个变量,必须先声明后使用 。 指针声明的一般形式如下:
数据类型 * 标识符;
注意,指针声明语句中的数据类型是指针变量所指向的变量的数据类型,即指针变量所指向的存储单元中存储数据的数据类型,并不是指针变量本身的类型,任一指针变量本身数据值的类型都是 unsigned
long int。
6.1.1.2 指针变量运算符
1,取地址运算符 &
该运算符表示对,&”后面的变量进行取地址运算 。
指针变量是用来存放变量地址的变量,因此可以通过取地址运算符 &,将某一变量的地址赋值给指针变量 。 例如:
int a=2,*p;
p=&a;
指针变量 p 变量 a
0 0 3 4 7 F DF 0 0 3 4 7 F DF 2
图 6 - 1 指针变量 p 和变量 a 的关系
【 例 6.1】 变量地址及取地址运算符的使用 。
#include<iostream.h>
void main()
{
int i=1;
cout<<"i="<<i<<"\t 变量 i 的 地 址 为,
"<<&i<<endl;
}
运行程序,输出显示结果:
i=1 变量 i的地址为,0x0012FF7C
2,间接访问运算符 *
该运算符也称,指针运算符,或,取内容运算符,,它后面必须是一个指针变量,
表示访问该指针变量所指向的变量,即访问指针所指向的存储单元的内容 。
例如:
int i=1,*p=&i;
cout<<*p;
注意:
( 1) 不要将间接访问运算符,*” 与声明指针时的,*” 混为一谈 。 指针声明时的
,*” 是指针变量声明的标示,可以称为
,指针指示符,,而间接访问运算符,*”
用来访问指针所指向的变量 。
( 2) 通过以上的说明可以看出,*运算和
&运算互为逆运算 。
【 例 6.2】 指针的各种表示形式及其含义 。
#include<iostream.h>
void main()
{
int a=10,*p;
p=&a;
*p=15;
cout<<"a="<<a<<endl;
cout<<"p="<<p<<endl;
cout<<"&a="<<&a<<endl;
cout<<"*p="<<*p<<endl;
cout<<"&p="<<&p<<endl;
}
运行程序,输出显示结果:
a=15
p=0x0012FF7C
&a=0x0012FF7C
*p=15
&p=0x0012FF78
6.1.1.3 指针的初始化
【 例 6.3】 分析下面程序的运行结果 。
#include<iostream.h>
void main()
{
int i=1,*p1=&i;
int *p2=p1;
cout<<"p1="<<p1<<",p2="<<p2<<endl;
}
程序的输出结果为:
p1=0x0012FF7C,p2=0x0012FF7C
6.1.1.4 指针的运算
1,赋值运算指针的赋值运算可以通过指针的初始化实现,
也可以在程序中通过赋值语句来实现 。
2,算术运算指针可以和整数进行加减运算,包括增 1和减 1运算,其实质是地址的运算 。
3,关系运算指针的关系运算一般在指向相同类型变量的指针之间进行,表示它们所指向的变量在内存中的位置关系 。
所声明的两个指针作 p1==p2运算,其结果为 1( true),即指针 p1,p2指向同一个变量 。
6.1.1.5 多级指针由于指针是一个变量,在内存中占据一定的存储空间,具有一个地址,这个地址也可以利用指针来保存 。 因此,可以声明一个指针来指向它,这个指针称为指向指针的指针,即二级指针 。 二级指针是指针变量的指针,使用二级指针可以实现更为复杂的功能 。
声明二级指针的格式为:
数据类型 ** 标识符
【 例 6.4】 二级指针的使用 。
#include<iostream.h>
void main()
{
int i;
int *p1=&i,**p2=&p1;
//声明二级指针 p2
i=1;
cout<<"i="<<i<<endl;
cout<<"*p1="<<*p1<<endl;
cout<<"p1="<<p1<<endl;
cout<<"*p2="<<*p2<<endl;
cout<<"p2="<<p2<<endl;
cout<<"**p2="<<**p2<<endl;
}
运行程序,输出结果为:
i=1
*p1=1
p1=0x0012FF7C
*p2=0x0012FF7C
p2=0x0012FF78
**p2=1
6.1.2 指针与数组数组在内存中的起始地址称为数组的指针,
数组元素在内存中的起始地址称为数组元素的指针 。
用来保存数组指针的变量称为数组的指针变量,简称为数组的指针 。 用来保存数组元素指针的变量称为数组元素的指针变量,
简称为数组元素的指针 。
6.1.2.1 用指针访问数组元素
【 例 6.5】 通过数组名访问数组元素 。
#include<iostream.h>
void main()
{
int x[5]={1,2,3,4,5};
for(int i=0;i<5;i++)
{
cout<<x[i]<<'\t';
}
cout<<endl;
for(i=0;i<5;i++)
{
cout<<*(x+i)<<'\t';
}
cout<<endl;
}
运行程序,将输出结果:
1 2 3 4 5
1 2 3 4 5
( 1) 下标法
① 数组名下标法,x[i]。
② 指针变量下标法,p[i]。
( 2) 指针法
① 数组名指针法,x+i表示数组元素 x[i]的地址,*(x+i)表示数组元素 x[i]。
② 指针变量指针法,p+i表示数组元素 x[i]
的地址,*(p+i)表示数组元素 x[i]。
在使用指向一维数组的指针变量时,要注意以下几点:
( 1) p+i并不是简单地使指针变量的值加上 i,而是 p+i*n,其中 n是数组元素所占用的字节数 。
( 2) 利用指针对数组进行操作时,还必须注意越界问题 。
( 3) 可以通过改变指针变量本身值的方法
( 如 p++) 来指向不同的数组元素,但是数组名表示数组的起始地址,是一个地址常量,是不能改变的,如写成 x++就是错误的 。
( 4) 要注意指针变量的当前值 。
【 例 6.7】 分析下面的程序 。
#include<iostream.h>
void main()
{
int x[5],*p=x ;
for(int i=0;i<5;i++)
{
cin>>*p++;
}
for(i=0;i<5;i++)
{
cout<<*p++<<'\t';
}
cout<<endl;
}
运行程序后,用户输入 1,2,3,4,5后回车,屏幕上输出显示如下:
1245120 4213209 1 4460192 4460000
6.1.2.2 指针数组由指针组成的数组称为指针数组,即指针数组的每一个元素都是指针 。
声明指针数组的一般格式如下:
数据类型 *数组名 [常量表达式 1][常量表达式 2]… ;
【 例 6.8】 指针数组的使用 。
#include<iostream.h>
void main()
{
int x[2][3]={{1,2,3},{4,5,6}};
int i,j;
int *p[2]={x[0],x[1]};
//声明指针数组并初始化
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
cout<<*(p[i]+j)<<'\t';
//利用指针数组输出其指向的元素的值
}
cout<<endl;
}
}
运行程序,将输出结果:
1 2 3
4 5 6
6.1.2.3 数组指针数组指针就是一个指向数组的指针,其声明格式如下:
数据类型 ( *指针名 ) [常量表达式 1][常量表达式 2]… ;
【 例 6.9】 利用数组指针改写例 6.8。
#include<iostream.h>
void main()
{
int x[2][3]={{1,2,3},{4,5,6}};
int i,j;
int (*p)[3]=x;
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
{
cout<<p[i][j]<<'\t';
}
cout<<endl;
}
}
6.1.3 指针与字符串在 C++语言中,字符串被表示成一个字符数组 。 因此,有时也把字符串称为字符数组 。 由于字符串中的每一个字符对应字符数组中的一个数组元素,故用户可以利用指针指向字符数组中的任何一个数组元素,
即指向字符串中的任何一个字符 。 例如:
char *s1="Hello World!";
在 C++语言中,字符串既可以用字符数组表示,也可以用字符指针变量来表示 。
例如,定义一个 20个字符的字符数组 a的语句为,char a[20]。
用户可以在定义字符数组的同时进行初始化 。 例如,char a[ ]= "How are you!"。
C++规定:字符数组的最后一个元素必须是 ‘ \0’字符 。
在引用字符串时,既可以逐个字符引用,
也可以作为一个整体引用 。
【 例 6.10】 分析下面程序的运行结果 。
#include <string.h>
#include <iostream.h>
void main()
{
char a[]="01234",b[]="56789";
char *sa=a,*sb=b;
for(int i=0;i<5;i++)
*sa++=*sb++;
cout<<a<<endl;
cout<<b<<endl;
}
运行程序,输出结果为:
56789
56789
6.1.4 指针与函数
6.1.4.1 指针作为函数的参数
【 例 6.11】 利用指针作为函数参数交换实参变量的值 。
#include<iostream.h>
void swap(int *a,int *b);
void main()
{
int x=1,y=2;
cout<<"交换前 "<<endl;
cout<<"x="<<x<<",y="<<y<<endl;
swap(&x,&y);
cout<<"交换后 "<<endl;
cout<<"x="<<x<<",y="<<y<<endl;
}
void swap(int *a,int *b)
{
int temp;
temp=*a;
*a=*b;
*b=temp;
}
运行程序,输出结果:
交换前
x=1,y=2
交换后
x=2,y=1
6.1.4.2 数组作为函数的参数
【 例 6.12】 将一个数组中的元素前后逆置 。
#include<iostream.h>
void transpose(int x[],int n);
void main()
{
int a[6]={1,2,3,4,5,6};
int i;
cout<<"原数组,";
for(i=0;i<6;i++)
cout<<'\t'<<a[i];
cout<<endl;
transpose(a,6);
cout<<"逆置后,";
for(i=0;i<6;i++)
cout<<'\t'<<a[i];
cout<<endl;
}
void transpose(int x[],int n)
{
int temp,i;
for(i=0;i<n;i++,n--)
{
temp=x[i];
x[i]=x[n-1];
x[n-1]=temp;
}
}
运行程序,将输出结果:
原数组,1 2 3 4 5 6
逆置后,6 5 4 3 2 1
【 例 6.13】 用选择排序法把 n个整数按从大到小的顺序排列 。
选择排序法的思想:第 1轮从 n个元素中选择最大的一个元素,把它和位于第 1个位置的元素互换;第 2轮在剩下的 n-1个元素中选择次大的一个元素,把它和位于第 2个位置的元素互换; …… 第 n-1轮在剩下的 2个元素中选择较大的一个元素,把它和位于第 n-1个位置的元素互换,最后剩下的一个元素必然是最小的,排序完成 。
#include<iostream.h>
#define n 8
void sort(int x[],int m);
void main()
{
int a[n],i;
cout<<"请输入要排序的 8个整数 "<<endl;
for(i=0;i<n;i++)
cin>>a[i];
//从键盘输入要排序的数据
sort(a,n);
//用选择排序法实现降序排列
cout<<"排序结果为 "<<endl;
for(i=0;i<n;i++)
cout<<a[i]<< ' ';
//输出排序结果
cout<<endl;
}
void sort(int x[],int m)
{
int i,j,k,t;
for(i=0;i<m-1;i++)
//外循环,控制循环次数
{
k=i;
//预置本轮次最大元素的下标值
for(j=i+1;j<m;j++)
//内循环,筛选出本轮次最大的元素
if(x[j]>x[k]) k=j;
//存在更大元素,保存其下标
if(k!=i)
{
t=x[i];
//交换位置
x[i]=x[k];
x[k]=t;
}
}
}
运行程序,输出结果为:
请输入要排序的 8个整数
23 35 56 45 26 4 9 17
排序结果为
56 45 35 26 23 17 9 4
6.1.5 动态内存分配
6.1.5.1 运算符 new
运算符 new用于申请所需的内存单元 。 它的使用格式如下:
<数据类型 > *<指针变量 > = new <数据类型 >;
例如:
int *pi;
//整数类型指针
float *pf;
//浮点类型指针
pi= new int;
//为一个整数类型的数分配内存
pf = new float;
//为一个浮点类型的数分配内存说明:如果分配成功,则返回一个指向该分配空间的指针,如果此空间不可用或者分配空间失败或者检测到某些错误,则返回零或空指针 。 因此,在实际编程时,对于动态内存分配,应在分配操作结束后,
首先检查返回的地址值是否为零,以确认内存申请是否成功 。
在堆中也可以用运算符 new申请一块保存数组的内存单元,即创建一个数组 。 创建一数组的格式如下:
指针 = new 数据类型 [下标表达式 ]
需要注意的事,由运算符 new动态分配的存储空间的生存周期是任意的,只有在程序中使用运算符 delete释放它们时,其生存周期才结束 。
6.1.5.2 运算符 delete
当程序中不再需要使用运算符 new申请到的某个内存时单元时,就必须用运算符
delete来释放它 。 这一操作的表述形式如下:
delete 指针名 ;
//释放非数组内存单元
delete[] 指针名;
//释放数组内存单元当程序从声明指针的函数返回前必须利用该指针删除在函数中申请的内存单元 。 否则,指针将退出作用域,不能再用,那么这个内存单元在整个程序运行结束前,就无法释放,从而不能再用,这就是所谓的内存遗漏问题 。
6.2 引 用
6.2.1 引用的概念在 C++语言中,提供了一种为变量起一个别名的机制,这个别名就是引用 。
引用声明的一般格式如下:
数据类型 &引用名 =变量名;
或数据类型 &引用名 (变量名 );
比如:
int a;
int &b=a;
【 例 6.15】 引用的声明和使用 。
#include<iostream.h>
void main()
{
int a=1;
int &b=a;
//b是变量 a的引用
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
b=2;
//对 b赋值就等价于对 a赋值
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"&a="<<&a<<endl;
cout<<"&b="<<&b<<endl;
}
运行程序,将输出结果:
a=1
b=1
a=2
b=2
&a=0x0012F7C
&b=0x0012F7C
6.2.2 引用与函数
6.2.2.1 引用作为函数参数
【 例 6.16】 利用引用作为函数参数改写例
6.11的程序 。
#include<iostream.h>
void swap(int &a,int &b);
void main()
{
int x=1,y=2;
cout<<"交换前 "<<endl;
cout<<"x="<<x<<",y="<<y<<endl;
swap(x,y);
cout<<"交换后 "<<endl;
cout<<"x="<<x<<",y="<<y<<endl;
}
void swap(int &a,int &b)
{
int temp;
temp=a;
a=b;
b=temp;
}
注意,实参前没有引用运算符,&”。
6.2.2.2 返回引用的函数函数的返回值可以是引用,其一般形式为:
数据类型 &函数名 ( 形参表 )
【 例 6.17】 返回引用的函数 。
#include<iostream.h>
int temp;
int &max(int x,int y)
{
if(x>y) temp=x;else temp=y;
return temp;
}
void main()
{
int a=max(15,25);
cout<<"a="<<a<<",temp="<<temp<<endl;
max(15,25)=50;
//实现对 max()函数的返回值 temp的赋值
cout<<"temp="<<temp<<endl;
}
运行程序,将输出结果:
a=25,temp=25
temp=50