C++语言程序设计 第六章 指针
第 6章 指针
※ 6.1 指针的基本概念
※ 6.2指针与数组
※ 6.3 指针与函数
※ 6.4 引用
C++语言程序设计 第六章 指针
6.1指针的基本概念 C++有在程序运行时获得变量的地址
和操纵地址的能力,这种用来操纵地
址的特殊类型变量就是指针。
6.1.1指针的概念
在计算机中, 内存是按字节编址的, 一个字节就是一个基
本的存储单位, 称为存储单元 。 每个存储单元都有一个编号,
这个编号就称为该存储单元的, 地址, 。 按地址可以找到相
应的存储单元进而对该存储单元的数据进行存取操作 。 因此
系统对数据的存取最终是通过内存单元的地址进行的 。
C++语言程序设计 第六章 指针
图 6.1 变量的存储
3
4
00654000H
00654001H
00654002H
00654003H
00654004H
00654005H
00654006H
00654007H
00654008H
变量 a
变量 b
如下语句,
int a = 3;
int b = 4;
则系统给整型变量 a,b分别分配 4个字节作为它们的存储空间, 如图 6.1 所示, 系统分配
00654000H~ 00654003H四个字节给变量 a,00654004H~ 00654007H字节给 b。 我们说变量 a
的地址是 00654000H,地址 00654000H指向的存储空间中存放的是数据 3,也就是存储空间的
内容为 3,即 a的值 。 同样 b的地址为 00654004H。
注意在内存中并不存在变量名, 对变量值的存取都是通过地址进行的 。
所谓指针, 就是一个变量的地址, 是一个常量 。 如变量 a的地址是 00654000H,则 00654000H
就是变量 a的指针 。
C++语言程序设计 第六章 指针
6.1.2指针变量的定义
由于指针变量是一个变量,所以它具有和普通变量一样的属性,即指针变量
也有一定的存储类型、数据类型和使用范围。因此,在使用指针变量之前,
必须对指针变量进行定义,以说明该指针变量的性质。
指针变量定义的一般形式为,
存储类型 数据类型 * 标识符;
,标识符, 就是指针变量名 。 例如下面语句定义了名字为 p和 q的两个不同类
型的指针变量,
int *p;
float *q;
标识符前的, *”, 表示其后的名字是一个指针变量名 。 在本例中, 指针变
量名是 p和 q,而不是 *p和 *q。 C++中, 在定义指针变量时, 以下形式均为合
法的,
int* p; // *靠左
float * q; // *两边都不靠
C++语言程序设计 第六章 指针
6.1.3指针变量运算符
有两个与指针变量有关的运算符 &和 *,下面对它们进行介绍
并说明它们之间的关系
1,取地址运算符,&
该运算符表示的是对, &”后面的变量进行取地址运算
2,指针运算符,*
该运算符也叫, 取内容运算符,, 它后面必须是一个指针变
量 。 表示的是访问该指针变量所指向的变量, 即访问指针所
指向的存储空间中的数据 。
C++语言程序设计 第六章 指针
一个指针变量 p在程序中通常有如下表示形式,
p — 指针变量,它的内容是地址量;
*p一 指针所指向的变量, 是指针所指向的内存空间中的数据;
&p一 指针变量所占存储空间的地址。
#include <iostream.h>
void main()
{
int a=10;
int *p;
p=&a;
cout<<"a="<<a<<endl;
cout<<"p="<<p<<endl;
cout<<"&a="<<&a<<endl;
cout<<"*p="<<*p<<endl;
cout<<"&p="<<&p<<endl;
cout<<endl;
*p=15;
cout<<"a="<<a<<endl;
cout<<"p="<<p<<endl;
cout<<"&a="<<&a<<endl;
cout<<"*p="<<*p<<endl;
cout<<"&p="<<&p<<endl;
}
运行结果为,
a=10
p=0x0065FDF4
&a=0x0065FDF4
*p=10
&p=0x0065FDF0
a=15 p=0x0065FDF4
C++语言程序设计 第六章 指针
6.1.4指针的初始化与赋值
指针初始化的一般形式为,
存储类型 数据类型 * 指针变量名=初始地址量;
例如,
int a;
int *p=&a;
上述语句在定义 p指针的同时, 把整型变量 a的地址作为初值
赋给了整型指针 p,从而使 p指向了变量 a的存储空间 。, *”
号仅是一个标记, 说明 p是一个指针变量, 在定义的同时将 a
的地址赋予了指针变量 p。 还可以写成以下语句形式,
int a,*p=&a;
C++语言程序设计 第六章 指针
为明确表示指针变量不指向任何变量, 在C ++语言中用 0值给指针变量赋值表示
这种情况, 记为 NULL。 例如
int *p = NULL; 或 int * p = 0;
指针值为 0的指针变量为空指针 。 对于静态的指针变量, 如在定义时未给它指定
初值, 系统自动给它指定初值为 0。 空指针并不是指针存储空间为空的意思, 这
里的 0也不是数值的 0,而是 NULL字符的 ASCII码值 。 空指针不表示任何指向,
而是表示指针的一种状态, 它在程序中经常作为一种状态标志使用 。
另外, 指针变量对所指对象也有类型限制, 不能将一个其他对象的地址赋给指针
变量 。 如有以下定义,
int i = 100,j,*ip,*intpt;
float f,*fp;
ip = &i;
以下都是不正确的赋值,
ip = 100; // 指针变量不能赋整数值
intpt = j; // 指针变量不能赋整型变量的值
fp = &i; // 能指向 float型变量的指针变量, 不能指向 int型变量
fp = ip; // 两种指向不同类型变量的指针变量不能相互赋值
C++语言程序设计 第六章 指针
6.1.5指针的运算
1.指针的算术运算
指针的算术运算只有两种:加和减 。 设 p和 q是指向具有相同类型的一组数据的指针, 数据的
排列是由前向后的, n是整数, 则指针可以进行的算术运算有如下几种,
p+n p-n p++ ++p p-- --p p-q
(1)指针与整数的加减运算
(2)指针加 1,减 1运算
(3) 指针的相减
2,指针的关系运算
两个指针进行关系运算时, 它们必须指向同一数据类型, 指针的关系运算表示它们所指
向的变量在内存中的位置关系 。 由于数据在内存中的存储顺序是由前向后, 若指针 p和 q
指向同一类型的数据, 则指针的关系运算如表 6.2所示 。
应注意, 在指向不同数据类型的指针之间进行关系运算是没有意义的, 指针与一般整数
常量或变量之间进行关系运算也是没有意义的 。 但指针与整数 0之间可进行等或不等的关
系运算, 即
p==0;

p! =0
用于判断指针是否为空指针 。
C++语言程序设计 第六章 指针
【 例 6.4】 指针的关系运算
#include <iostream.h>
void main( )
{
int a=10,b=10,*ptr1,*ptr2;
ptr1=&a;
ptr2=&b;
cout<<(*ptr1==*ptr2)<<endl;
cout<<(ptr1==ptr2)<<endl;
}
运行结果为
1
0
C++语言程序设计 第六章 指针
6.2指针与数组
C++语言程序设计 第六章 指针
6.2.1指向数组的指针
一个数组包含若干元素, 每个数组元素都在内存中占用存储单元, 它们都有相应的
地址 。 指针变量既然可以指向变量, 当然也可以指向数组和数组元素 (把数组起始地
址或某一数组元素的地址放到一个指针变量中 )。
引用数组元素可以用下标法 (如 a[1]),也可以用指针法, 即通过指向数组元素的指针
找到所需的元素 。
利用数组下标的变化可以实现对数组中各元素的处理, 如有如下语句,
int a[10];
定义了包含 10个数组元素的整型数组 a,则系统在内存中为数组 a分配存放 int数据
的 10个连续的存储空间,分别为 a[0],a[1],a[2]… a[9]
由于数组的存储位置是由系统分配的,并且不允许用户在程序中任意设定或改变数组的存储位
置,所以,表示数组首地址的数组名是一个地址常量,不能向它赋值。
引用一个数组元素, 可以用下标法和指针法,
下标法, 如 a[i]或 p[i]。
指针法, 如 *( a+i) 或 *( p+i) 。 其中 a是数组名, p是指向数组的指针变量, 其初值为 a。
对一个数组元素 a[i]的地址可以表示为,&a[i],p+i,a+i。
C++语言程序设计 第六章 指针
6.2.2指针与字符数组
在 C++程序中,实现一个字符串有两种方法。
一是 用字符数组 实现,
例如下面语句,static char s1[]="Hello world";
其中 s1是数组名, 代表数组存储的首地址;
二是 用字符指针 实现,
即不定义字符数组, 而定义一个字符指针 。 用字符指针指向字符串中的字符, 如,
char *s1= "Hello world";
【 例 6.6】 使用字符指针变量实现将字符串
a赋值到字符串 b中 。
#include <iostream.h>
void main()
{
char a[]="Hello world",b[20],*p,*q;
p=a,q=b;
for(;*p!= '\0';p++,q++)
*q=*p;
*q='\0';
cout<< "string1 is,";
cout<<a<<endl;
cout<< "string2 is,";
cout<<b<<endl;
}
运行结果为,
string1 is,Hello world
string2 is,Hello world
C++语言程序设计 第六章 指针
6.2.3多级指针与指针数组
1,多级指针
在前面的叙述中, 一个指针变量可以指向一个相应数据类型的数据, 例如,
int a,*p;
p=&a;
使指针 p指向 a,则指针 p所指向的变量 *p就是要处理的数据变量 a。 如果同时存在另一个指针
pp,并且把指针 p的地址赋予指针变量 pp,即
pp=&p;
则 pp就指向了指针 p,这时指针 pp所指向的变量 *pp 就是指针 p。
在 C++中,把 pp这样的指向指针的指针称为多级指针,
在程序中定义二级指针的一般形式如下,
存储类型 数据类型 ** 指针名
C++语言程序设计 第六章 指针
6.2.4指针与多维数组
1,指针数组处理多维数组
#include <iostream.h>
void main()
{
int a[2][3],* p[2];
int i,j;
p[0]=a[0];
p[1]=a[1];
for( i=0;i<2;i++)
for(j=0;j<3;j++)
a[i][j]=j+i;
for(i=0;i<2;i++)
for( j=0;j<3;j++)
{
cout<<"a["<<i<<"][" <<j<<"],";
cout<< *(p[i]+j)<<endl;
}
}
图 6.18指针数组
b[0] b[0][0]
b[0][1]
b[1][0]
b[1][1]
b[2][0]
b[2][1]
p[0]
p[1]
int b[3][2];
p[2]
int *p[3];
*p[0]
*(p[0] +1)
*p[1]
*(p[1] +1)
*p[2]
*(p[2]+1 )
b[1]
b[2]
运行结果为,
a[0][0],0
a[0][1],1
a[0][2],2
a[1][0],1
a[1][1],2
a[1][2],3
C++语言程序设计 第六章 指针
C++语言程序设计 第六章 指针
2.二级指针处理多维数组和多个字符串
#include <iostream.h>
void main()
{
int a[2][3],*p[2],** pp;
int i,j;
p[0]=&a[0][0];
p[1]=&a[1][0];
pp=p;
for( i=0;i<2;i++)
for(j=0;j<3;j++)
pp[i][j]=j+i; //给数组元素赋值
for(i=0;i<2;i++)
for( j=0;j<3;j++)
{
cout<< "a["<<i<<"][" <<j<<"],";
cout<< *(*(pp+i)+j)<<endl; //输出数组元
}
}
int a[3][2];
图 6.19 二级指针与二维数组
*pp a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0]
a[1]
a[2]
int **pp;
**pp
*(*pp +1)
**(pp +1)
*(*(pp +1)+1)
**(pp+2)
*(*(pp+2 )+1)
*(pp+1)
*(pp+2)
pp
C++语言程序设计 第六章 指针
6.2.5数组指针
数组指针的说明形式如下,
存储类型 数据类型 (*指针名 )[元素个数 ]
在使用数组指针时, 有两点希望引起同学的注意,
① *p两侧的括号一定不要漏掉, 如果写成 *p[3]的形式, 由于方括号 [ ]运
算级别高, 因此 p先与 [3]结合, 是数组, 然后再与前面的 *结合, *p[3]是
指针数组 。
② p是一个行指针,它只能指向一个包含有若干元素的一维数组,p不能
指向一维数组中的元素。如果要访问一维数组中的某个元素如第 j个元素,
可用 (*p)[j]的形式。
C++语言程序设计 第六章 指针
6.3 指针与函数
6.3.1 指针作为函数参数
指针作为函数的参数时, 是以数据的 地址 作为实参调用一个函数, 即 作为参数传递的不是数据
本身, 而是数据的地址 。 因此, 与之相应的被调用函数中的形参应为指针变量, 并且其数据类
型必须与被传递参数的数据类型保持一致 。 请看下面的程序,
【 例 6.20】 将键盘输入的一个大写字母转换为小写字母并显示出来 。
#include<iostream.h>
void lower(char *c);
void main ( )
{
char str;
cout << "Enter character:" << endl;
cin >> str;
lower(&str);
cout << "converted character:" << endl;
cout << str << endl;
}
C++语言程序设计 第六章 指针
void lower(char *c)
{
if (*c >= ' A' && *c <= ' Z' )
*c += ' a' -' A' ;
}
运行结果为,
Enter a character,
R
r
在 lower( )函数中,形参指针 c接收从主调函数传送来的实参(大写字符的地址),将该地址
中的大写字符转化为小写字符,转换结果仍保存在该地址空间中。
使用指针作为参数在函数间传递数据时要注意两点,
⑴ 在主调函数中, 要以指向变量的指针作为实参来调用另一个函数 。
⑵ 被调用函数的形参必须是可以接受地址值的指针变量, 而它的数据类型应与被传送的数
据类型保持一致 。
C++语言程序设计 第六章 指针
6.3.2 函数调用中数组的传递
基本思路为:使用数组名作为实参调用函数, 在
被调用函数中, 以指针变量作为形参接受数组的地址,
该指针被赋予数组的地址之后, 就指向了数组的存储
空间 。 根据指针处理数组数据的原理, 在被调用函数
中, 使用这个指针就可以对主调函数中的所有数据进
行处理 。 因此, 采用数组名的传递方式可以较圆满地
解决数组中大量数据在函数间传递的问题 。
C++语言程序设计 第六章 指针
6.3.3 函数指针
C++语言程序设计 第六章 指针
一个函数在编译时被分配一个入口地址 ( 第一条指
令的地址 ), 可以将该地址赋给一个指针, 这样,
指针变量持有函数入口地址, 它就指向了该函数,
所以称这种指针为指向函数的指针, 简称函数指针 。
函数指针定义的一般形式,
数据类型 ( *指针变量名 ) ( 形参类型 )
在 C++程序设计中, 函数指针的作用主要体现在函
数间传递函数 。 函数可以在函数间进行传递, 也就
是传递函数的执行地址, 这也就意味着对函数的一
种调用控制 。 当函数在两个函数间传递时, 主调函
数中的实参为传递函数的函数名, 而被调用函数中
的形参是能够接收传递函数地址的函数指针 。
C++语言程序设计 第六章 指针
6.4 引用
C++语言程序设计 第六章 指针
说明
(1)一个变量被定义为引用时必须进行初始化,除非这个引用是用作函
数的参数或返回值,为引用提供的初始值应为变量。
(2)由于引用不是变量,所以,不能说明引用的引用,也不能说明数组
元素的类型为引用数组,或指向引用的指针。
(3)引用与指针不同。指针的内容或值是某一变量的内存单元地址,而
引用则与初始化它的变量具有相同的内存单元地址。
(4)可以用一个引用初始化另一个引用。
C++语言程序设计 第六章 指针
6.4.2函数参数的引用传递方式
函数的值调用并非适用于所有函数参数,原因有二,其一, 当需要将一个
较大的对象作为参数传给函数时, 用于存放该对象备份的空间和存放过
程所需的时间都比较大, 这对于实际应用来说是不能容忍的;其二, 当
主调函数希望得到修改后的参数时, 值调用也不再适用 。 请看下例,
【 例 6.29】 用引用调用的方法实现两个数据互换 。
#include<iostream.h>
void swap(int &u,int &v);
void main ( )
{
int a = 3;
int b = 4;
cout << "a=" << a << "b=" << b << endl;
swap (a,b);
cout << "a=" << a << "b=" << b << endl ;
}
C++语言程序设计 第六章 指针
void swap(int &u,int &v)
{
int temp = v;
v = u;
u = temp;
}
运行结果为,
a=3 b=4
a=4 b=3
利用引用型参数进行函数调用, 有两点好处,
(1)在函数内对参数值的修改不再作用于局部复制, 而是针对实际参数进行的 。
这就是 为什么 swap( )可以正常工作的原因 。
(2)在传递大型的对象时, 也不再有高额的空间与时间开销 。 它的传递方式类似
于指针, 不像传值那样复制整个对象 。