C++程序设计 湖南大学 杜四春、银红霞
第 6章 指针
6.1 指针的概念
6.2 指针的定义和使用
6.3 指针运算
6.4 void指针和 const指针
6.5 指针与数组
6.6 动态内存分配
6.7 指针与字符串
6.8 指针与函数
C++程序设计 湖南大学 杜四春、银红霞
6.1 指针的概念
指针变量(简称指针)是一种特殊性质的
变量。
每个变量有一个地址,必要时可以使用取
地址运算符,&”来获取变量的地址。
? 通过变量名或地址访问一个变量的方式,
称为“直接访问” 。
? 把地址存放在一个变量中,然后通过先找
出地址变量中的值(一个地址),再由此地址
找到最终要访问的变量的方法,称为“间接访
问”。
这里的地址变量其实就是指针。
C++程序设计 湖南大学 杜四春、银红霞
由上图可以看到,指针变量 p指向变量 a。
在理解“指向”的时候,应该了解它指的是:
p中存有 a的地址,通过该地址就能找到 a。由
此,在 C++语言中用“指针”来表示指向关系。
即“指针”就是地址。一个变量的指针就是该
变量的地址。存放地址的变量,就是指针变量。
p a
5
图 6-3 指针 p示意图
C++程序设计 湖南大学 杜四春、银红霞
指针除了可以指向变量之外,还可以指向
内存中其他任何数据结构,如数组,结构体和
联合体等,它还可以指向函数,后面将陆续介
绍。应该注意,在程序中参加数据处理的量不
是指针本身的量,因为指针本身是个地址量。
而指针所指向的变量,即指针所指向的内存区
域中的数据(称为指针的目标)才是需要处理
的数据。这就是 C++语言中利用指针处理数据
的特点。
C++程序设计 湖南大学 杜四春、银红霞
6.2 指针的定义和使用
指针是一个变量,在程序中使用时,必须
先声明,后使用。在指针声明的同时也可以进
行初始化。
6.2.1 指针的定义
指针的定义指出了指针的存储类型和数据类型,
定义的语法形式如下:
存储类型 数据类型 *指针变量名
例如:
int *p1;
static int *p2;
char *da;
上面定义了名为 p1,p2和 da的三个不同类型指针。
C++程序设计 湖南大学 杜四春、银红霞
通常把指针指向的变量的数据类型称为指针的数
据类型;而任何一个指针变量本身数据值的类型都是
unsigned long int。
在指针变量名前的符号, *” 表示指向运算 。 指针
变量的类型确定后只能指向这种既定的数据类型 。 不
可指向其他类型的数据 。
注意:定义一个指针变量必须用符号,*”,它表
明其后的变量是指针变量,但千万不要认为,*p”是指
针变量,指针变量是 p而不是 *p。
C++程序设计 湖南大学 杜四春、银红霞
具有相同存储类型和数据类型的指针可以在一行
中说明, 它们也可以和同类型的普通变量在一起说明 。
例如:
int *p1,*p2,*p3;
char m,*da;
在上例中, 第一行声明了三个 int型指针 p1,p2,
p3;第二行声明了一个 char型变量 m和一个指针 da。
注意:当在一行中定义多个同一类型的指针时,
用逗号隔开各指针变量标识符,并且每个变量前都要
加上星号,*”。
C++程序设计 湖南大学 杜四春、银红霞
6.2.2 指针的使用
定义了一个指针后,在使用此指针前,必须首先
给它赋一个合法的值。否则,程序中对指针的使用就
有可能导致系统崩溃。可以在定义指针的同时,通过
初始化来给指针赋值,也可以在使用之前给指针赋值。
下面首先来了解指针的初始化。
由于指针是保持地址的变量, 所以初始化时赋予
它的初值必须是地址量 。 指针初始化的一般形式是,
存储类型 数据类型 *指针名 =初始地址值;
例如:
int a,*pa=&a;
将变量 a 的内存地址作为初始值赋予 int型指针 pa。
C++程序设计 湖南大学 杜四春、银红霞
上面这种写法与下面的写法是等价的:
int a;
int *pa=&a;
当把一个变量的内存地址作为初始值赋给指针时,
该变量必须在指针初始化之前已经说明过。其道理很
简单,变量只有在说明之后才被分配一定的内存地址。
此外,该变量的数据类型必须与指针的数据类型一致。
C++程序设计 湖南大学 杜四春、银红霞
下面的例子是把一个指针初始化为空指针 。
int *px=0;
这个语句将指针 px的值初始化为 0。 值为 0的指针
叫作空指针 。
为了使用安全起见,一般来说,在定义指针时,
最好初始化,哪怕是初始化为空指针。
C++程序设计 湖南大学 杜四春、银红霞
如果在定义指针时, 指针初始化为 0或者根本没有
初始化 。 那么在使用此指针前, 就必须给它赋有意义
的值 。
例如:
int n,*p1; //定义指针 p1时没有初始化
p1=&n; //给指针 p1赋值为 int型变量 n的地址
或者:
int n,*p1=0; //定义指针 p1时初始化为 0
p1=&n; //给指针 p1赋值为 int型变量 n的地址
都是正确的。
C++程序设计 湖南大学 杜四春、银红霞
也可以向一个指针赋初值为另一个指针变量, 即
把另一个已经初始化的指针赋予一个指针 。 这时, 这
两个指针指向同一变量的内存地址 。 如下例所示:例
如:
int n;
int *p1=&n; //指针 p1的值初始化为变量 n的地址
int *p2=p1; //指针 p2的值初始化为指针变量 p1
上面这种写法与下面的写法是等价的:
int n;
int *p1=&n; //指针 p1的值初始化为变量 n的地址
int *p2=&n; //指针 p2的值初始化为变量 n的地址
C++程序设计 湖南大学 杜四春、银红霞
也可以定义一个指向数组的指针 。 由于数组名表
示的是该数组的首地址, 所以如果定义一个指针指向
数组, 则可如下例声明:
例如:
int a[10],*pa=a;
这个语句定义了一个指针 pa,并把 pa初始化为指
向数组 int a[10]的指针, 即指针 pa指向数组的第一个
元素 。 这时, 不需要使用取地址运算符, &”。
上述声明方式与下面的语句等价:
int a[10],*pa=&a[0];
“&a[0]”表示数组 a中第一个元素所在的内存地址
值。
C++程序设计 湖南大学 杜四春、银红霞
如果说明了一个指针, 并使其值为某个变量的地
址, 则可以通过这个指针间接地访问在这个地址中存
储的值 。
利用指针来访问变量值需要使用间接访问运算符
,*” 。
例如:
int a=1,*pa=&a;
cout<<*pa;
用 cout语句输出的是变量 a的值 1,即,*pa”就是
代表变量 a。上面的,cout<<*pa;”语句与
,cout<<a;”语句的作用相同。
C++程序设计 湖南大学 杜四春、银红霞
同样, 在利用指针访问变量的值时, 也可以通过
指针给变量间接赋值 。
例如:
int a=1,*pa=&a;
*pa=2;
cout<<a;
用 cout语句输出的是变量 a的值 2,而不是 1。
C++程序设计 湖南大学 杜四春、银红霞
经过上面部分的学习已知, 在 C++语言中有两个
有关指针的特别运算符:
? & 运算符:为取地址运算符, &x的值为 x的地址 。
? * 运算符:指针运算符, 或指向运算符, 也称间接
运算符, *p代表 p所指向的变量 。
在指针变量的定义和指针变量的引用中都有 *p。
但引用指针时的 *p与定义指针变量时用的 *p是有区别
的, 它们形式上有些相似, 而含义是不同的 。
C++程序设计 湖南大学 杜四春、银红霞
由于引进了指针的概念, 读者在程序中要注意区
分下面三种表示方法所具有的不同意义 。 例如, 有一
个指针 px,
? px ----- 指针变量, 它的内容是地址量 。
? *px --- 指针的目标变量, 它的内容是数据 。
? &px -- 指针变量占用的存储区域的地址 。
px
&px
*px
图 6-4 *px和 &px的意义
C++程序设计 湖南大学 杜四春、银红霞
6.2.3 指针的灵活性
上面讲述过, 多个指针可以指向一个变量 。 由这
种思想可以得知:利用指针的灵活性, 既可交换指向,
也可以交换指针所指向的变量的值 。 为了更好地了解
指针的概念, 下面通过一些例子来使读者消化掌握有
关指针的使用 。
例 6-2:多个指针指向同一个变量 。
例 6-3:交换指针所指向的变量 。
例 6-4:交换指针所指向的变量的值 。
交换地址 ( 即交换指向 ) 和交换所指向的变量的
值有本质的区别 。
C++程序设计 湖南大学 杜四春、银红霞
6.2.4 二级指针
由于指针是一个变量, 在内存中也占据一定的空
间, 具有一个地址, 这个地址也可以利用指针来保存 。
因此, 可以声明一个指针来指向它, 这个指针称为指
向指针的指针, 也即二级指针 。 声明二级指针的形式
如下:
存储类型 数据类型 **指针变量名
其中的两个星号, **” 表示二级指针;数据类型
是指通过两次间接寻址后所访问的变量的类型 。
例如:
int i,*p=&i;
int **pp=&p;
上述语句声明了一个二级指针 pp,它指向指针 p。
C++程序设计 湖南大学 杜四春、银红霞
#include<iostream.h>
void main()
{
int a;
int *p=&a,**pp=&p;
a=1;
cout<<”a=”<<a<<endl;
cout<<”*p=”<<*p<<endl;
cout<<”p=”<<p<<endl;
cout<<”*pp=”<<*pp<<endl;
cout<<”**pp=”<<**pp<<endl;
}
此程序的运行结果为:
a=1
*p=1
p=0x0065FDFD
*pp=0x0065FDFD
**pp=1
C++程序设计 湖南大学 杜四春、银红霞
6.3 指针运算
指针运算是以指针变量所持有的地址值为
运算量进行的运算。因此,指针运算的实质是
地址的计算。
由于指针是持有地址量的变量这一特性,
指针的运算与普通变量的运算在种类上和意义
上都是不同的。指针运算的种类是有限的,它
只能进行算术运算、关系运算和赋值运算。
C++程序设计 湖南大学 杜四春、银红霞
6.3.1 指针的算术运算
指针的算术运算是按 C++语言地址计算规则进行
的, 这种运算与指针指向的数据类型有密切关系, 也
就是 C++语言的地址计算与地址中存放的数据长度有
关 。
设 px和 py是指向具有相同数据类型的一组若干数
据的指针, n是整数, 则指针可以进行的算术运算有如
下几种:
px+n,px-n,px++,++px,
px--,--px,px-py
C++程序设计 湖南大学 杜四春、银红霞
1,指针与整数的加减运算,( px+n,px-n)
指针作为地址量加上或减去一个整数 n,其意义是
指针当前指向位置的前方或后方第 n个数据的位置 。 由
于指针可以指向不同数据类型, 即数据长度不同的数
据, 所以这种运算的结果值取决于指针指向的数据类
型 。
例如, 假设有一个指向单字节字符类型变量的指
针和另一个指向双字节整数类型的指针 。 当字符指针
加 1时, 实际结果是指针中的地址值加 1;而整数指针
加 1时, 实际结果是指针中的地址值加 2。
C++程序设计 湖南大学 杜四春、银红霞
由此, 对于某种数据类型的指针 p来说:
p+n的实际操作是,( p) +n*sizeof( 数据类型 ) ;
p-n的实际操作是,( p) -n*sizeof( 数据类型 ) ;
其中, ( p) 表示指针 p中的地址值, 而不是 &p,
sizeof( 数据类型 ) 的长度单位为字节 。
在 C++语言中, 指针加减运算一般用在对数组元
素进行操作的场合 。 通过对指向数组的指针进行加减
运算, 可以使指针指向数组中不同的元素 。 此时也必
须注意越界问题 。
C++程序设计 湖南大学 杜四春、银红霞
2,指针加 1,减 1运算,( px++,++px,px--,--
px)
指针加 1,减 1单项运算也是地址计算, 它具有上
述的计算特点, 指针的加 1,减 1单项运算是指针中的
地址值的变化 。 指针 ++运算后就指向了下一个数据的
位置, --运算后就指向了上一个数据的位置 。 运算后指
针地址值的变化量取决于它指向的数据类型 。
例如,一个 int型指针 p存放的地址为 1010,当执行
p++后,p存放的地址为 1012,即指针 p指向了下一个
数据的地址。
C++程序设计 湖南大学 杜四春、银红霞
指针加 1,减 1单项运算也分为前置运算和后置运
算, 当它们和其他运算出现在一个表达式中时, 要注
意它们之间的结合规则和运算顺序 。 例如:
y=*px++
该表达式中有三种运算,=,*和 ++。 查表 2-4可
知, *和 ++优先于 =。 *和 ++属于同级运算, 其结合规
则是从右至左 。 所以 ++运算是对 px进行的 。 它相当于:
y=*(px++)
这里 px++是后置运算。因此该表达式的运算顺序
是,访问 px当前值指向的目标,把目标变量的值赋予 y,
然后 px加 1指向下一个目标。
C++程序设计 湖南大学 杜四春、银红霞
例 6-7,字符串复制函数 。
strcpy(s,t)
char *s,*t;
{
while(*s++=*t++)!='\0'};
}
这是标准函数库中的一个函数, 函数体中使用了
指针后置运算,*s++=*t++)!='\0'
它的运算过程是把 t的目标变量的值赋予 s的目标变
量,然后判断赋值表达式的结果值,即赋的值是否不
等于‘ \0’。 s和 t的值使用后执行加 1运算,分别指向下
一个目标。函数中循环体是空语句。
C++程序设计 湖南大学 杜四春、银红霞
3,指针的相减运算,( px-py)
如果两个指针 px和 py所指向的变量类型相同, 则
可以对它们进行相减运算 。 px-py运算的结果值是两指
针指向的地址位置之间的数据个数 。 由此看出, 两指
针相减实质上也是地址计算 。 它执行的运算不是两指
针持有的地址值相减, 而是按下列公式得出结果 。
(( px) -( py)) / 数据长度
上式中( px)和( py)分别表示指针 px和 py的地
址值,所以,两指针相减的结果值不是地址量,而是
一个整数。
C++程序设计 湖南大学 杜四春、银红霞
指针的相减运算一般也用在对数组进行的操作中 。
比如:
int x[5],a;
int *px=&x[1],*py=&x[4];
a=py-px;
这里的变量 a就表示数组元素 x[1]和 x[4]之间相隔的
元素的个数。
C++程序设计 湖南大学 杜四春、银红霞
6.3.2 指针的关系运算
在两个指向相同类型变量的指针之间可以进行各
种关系运算 。 两指针之间的关系运算表示它们指向的
地址位置之间的关系 。 比如:
int a;
int *p=&a,*q=p;
若上面声明的两个指针作 p==q运算, 其结果为 1
( true), 也即指针 p,q指向同一个变量 。 两指针相
等的概念是两指针指向同一位置 。
C++程序设计 湖南大学 杜四春、银红霞
假设数据在内存中的存储逻辑是由前向后, 那么
指向后方的指针大于指向前方的指针 。 对于两指针 p和
q之间的关系表达式:
p<q
若 p指向位置在 q指向位置的前方,则该表达式的
结果值为 1,反之为 0。
指向不同数据类型的指针之间的关系运算是没有
意义的, 指针与非 0整数之间的关系运算也是没有意义
的 。 但是指针可以和零之间进行等于或不等于的关系
运算, 即:
p==0 或 p!=0
它们用于判断指针 p是否为一空指针。
C++程序设计 湖南大学 杜四春、银红霞
6.3.3 指针的赋值运算
当向指针变量赋值时, 赋的值必须是地址常量或
变量, 不能是普通整数 。 指针赋值运算常见的有以下
几种形式 。
? 把一个变量的地址赋予一个指向相同数据类型的
指针, 例如:
char a,*p;
p=&a;
? 把一个指针的值赋予相同数据类型的另外一个指
针, 例如:
int *p,*q;
p=q;
C++程序设计 湖南大学 杜四春、银红霞
? 把数组的地址赋予指向相同数据类型的指针 。 例
如:
char a[10],*pa;
pa=a;
? 此外, 还经常使用下列赋值运算:
int *p,*q,n;
p=q+n;
p=q-n;
p+=n;
p-=n;
C++程序设计 湖南大学 杜四春、银红霞
6.4 void指针和 const指针
在 C++语言中,可以声明指向 void类型的
指针。指向 void类型的指针称为 void指针。此
外,在声明指针时,还可以用关键字 const进行
修饰,用关键字 const修饰的指针称为 const指
针。
C++程序设计 湖南大学 杜四春、银红霞
6.4.1 void指针
一般来说, 只能用指向相同类型的指针给另一个
指针赋值, 而在不同类型的指针之间进行赋值是错误
的 。 比如:
int a,b;
int *p1=&a,*p2=p1; //正确
而:
int a;
int *p1=&a;
double *p2=p1; //错误
上述语句中的两个指针 p1,p2指向的类型不同,
因此, 除非进行强制类型转换, 否则它们之间不能相
互赋值 。 但是 void指针是一个特例 。
C++程序设计 湖南大学 杜四春、银红霞
C++语言允许使用空类型 ( void) 指针, 即不指定
指针指向一个固定的类型, 它的定义格式为:
void *p;
表示指针变量 p不指向一个确定的类型数据, 它的
作用仅仅是用来存放一个地址 。
void指针它可以指向任何类型的 C++数据 。 也就是
说, 可以用任何类型的指针直接给 void指针赋值 。 不
过, 如果需要将 void指针的值赋给其他类型的指针,
则需要进行强制类型转换 。 比如:
int a;
int *p1=&a;
void *p2=p1;
int *p4=(int *)p2;
C++程序设计 湖南大学 杜四春、银红霞
6.4.2 const指针
关键字 const放在不同的位置表示的意义也不相同:
? ( 1) 关键字 const放在指针类型前, 就是声明一个
指向常量的指针 。 此时, 在程序中不能通过指针来改
变它所指向的值, 但是指针本身的值可以改变, 即指
针可以指向其他数据 。
? ( 2) 关键字 const放在, *” 号和指针名之间, 就
是声明一个指针常量 ( 也称常指针 ) 。 因此, 指针本
身的值不可改变, 也即它不能再指向其他数据, 但它
所指向的数据的值可以改变 。
? ( 3) 关键字 const在上述两个地方都加, 则是声明
一个指向常量的指针常量, 指针本身的值不可改变,
它所指向的数据的值也不能通过指针改变 。
C++程序设计 湖南大学 杜四春、银红霞
6.5 指针与数组
要访问或使用一个数组元素,可以用三种
不同的方法。一种是下标法,另一种是地址法,
还有一种是指针法。
1,下标法
这种方法在前面已学习过,即指出数组名和下标
值,系统就会找到该元素。数组用其下标变化实行对
内存中的数组元素进行处理 。
C++程序设计 湖南大学 杜四春、银红霞
例如, 程序中说明了一个数组:
int a[5];
则编译系统在一定的内存区域为该数组分配了存
放 int型数据的 5个连续存储空间,它们分别是 a[0],
a[1],……, a[5](见图 6-8)。则 a[i]表示从数组存储
首地址开始的第 i个元素变量。在程序中通过 i的变化就
可以处理数组中的任何元素。 a[i]就是用下标法表示的
数组元素。
C++程序设计 湖南大学 杜四春、银红霞
2,地址法
前面已经介绍, 一个数组名代表它的起始地址 。
地址法即通过地址访问某一数组元素 。
例如, 程序中说明了一个数组:
int a[5];
则 a的值就是数组的起始地址, 即 a指向 a[0],
a+1指向 a[1],…… 。 同样, a+i是 a[i]的地址, 通
过 a+i的地址可以找到 a[i]元素, 即 *( a+i) 就是
a[i]。
C++程序设计 湖南大学 杜四春、银红霞
例如:要访问数组元素 a[3],下面两种方式是等价
的:
a[3] ——下标法
*(a+3)——地址法
从另一个角度来看,a+i和 &a[i]是相等的,都是 a[i]
的地址。注意要区分 a[i]和 &a[i]二者的不同含义,a[i]
是 a数组第 i个元素的值,而 &a[i]是 a[i]元素的地址。
C++程序设计 湖南大学 杜四春、银红霞
3,指针法
除上述两法之外, 还可以定义一个指针变量, 指
向一数组元素 。
例如:若程序中同时说明了一个 int型指针;
int *pa;
并且通过指针赋值运算;
pa=a;

pa=&a[0];
则指针 pa就指向了数组 a的首地址。这里指针的目
标变量 *pa就是 a[0]。根据上节介绍的指针运算的原理,
*( pa+1)就是 a[1],*( pa+2)就是 a[2]…… 。即 *
( pa+i)就是 a[i] 。
C++程序设计 湖南大学 杜四春、银红霞
对于二维数组, 又是如何用指针访问其中的每个
数组元素的呢?
例如, 定义了一个二维数组 a:
int a[3][5];
a是以一个 3*5的二维数组, 它有三行, 每一行都
有其起始地址 。
C++语言规定以 a[0],a[1],a[2]分别表示第 0行,
第 1行,第 2行的起始地址,即该行第 0列元素的地址。
注意 a[0],a[1],a[2]并不是一个元素,而是一行首地
址,正如同一维数组名是数组起始地址一样,a[0]的
值等于 &a[0][0],a[1]的值等于 &a[1][0],a[2]的值等
于 &a[2][0]。在同一行中是同样的类推方式,比如:
a[0]+1的值等于 &a[0][1],a[1]+2的值等于
&a[1][2],…… 。
C++程序设计 湖南大学 杜四春、银红霞
因此, 对于二维数组中的元素 a[i][j]有多种访问方
法 。 以下是其中的一部分:
假设定义了一个二维数组:
int a[2][3];
则对它的访问方法有:
*(*(a+i)+j)
*(a[i]+j)
*(a+i)[j]
*(a+3*i+j)
C++程序设计 湖南大学 杜四春、银红霞
如果用指针法访问二维数组中的元素, 处理的方
法也有许多种 。
假设定义了一个指向上述二维数组 a首元素的指针
p,以下列出一部分通过这个指针访问数组元素 a[i][j]
的方法:
*(*(p+i)+j)
*(p[i]+j)
*(p+i)[j]
*(p+3*i+j)
p[i][j]
C++程序设计 湖南大学 杜四春、银红霞
对于三维以上的多维数组,访问数组元素的方法
在原理上是一样的,只是在使用时一定要注意 C++中
多维数组中各元素在内存单元中的存储顺序。并且,
在利用指针访问数组元素时,同样要注意越界问题。
C++程序设计 湖南大学 杜四春、银红霞
6.5.2 数组指针与指针数组
在 C++语言中,数组指针就是一个指向数组的指
针;指针数组就是其元素为指针的数组。在学习 C++
语言时,要注意对它们进行区分,不能等同起来。
1,数组指针
数组指针是一个指向一维数组的指针变量, 定义
数组指针的格式为:
数据类型 ( *指针名) [常量表达式 ];
C++程序设计 湖南大学 杜四春、银红霞
例如:
int (*p)[5];
上面这个语句定义了一个数组指针 p,它指向一个
包含 5个元素的一维数组,数组元素为整型。注意,*p
两侧的圆括号不能省略,它表示 p先与星号,*”结合,
是指针变量。如果省略了圆括号,即写成 *p[5]的形式,
由于方括号的优先级比星号高,则 p先与方括号 [ ]结合,
是数组类型,那么语句 int *p[5];是定义了一个指针
数组。
C++程序设计 湖南大学 杜四春、银红霞
2,指针数组
指针数组就是其元素为指针的数组 。 它是指针的
集合, 它的每一个元素都是指针变量, 并且它们具有
相同的存储类型和指向相同的数据类型 。
说明指针数组的语法格式为:
数据类型 *指针数组名 [常量表达式 ];
其中, 数据类型式指数组中各元素指针所指向的
类型, 同一指针数组中各指针元素指向的类型相同;
指针数组名也即数组的首地址, 是一个标识符;常量
表达式指出这个数组中的元素个数 。
例如:下面定义了几个指针数组 。
int *p1[6];
float *p2[3][4];
C++程序设计 湖南大学 杜四春、银红霞
具有相同类型的指针数组可以在一起说明, 它们
也可以与变量, 指针等一起说明 。 如:
int a,*p[2];
指针数组在使用前也必须首先赋值,也可以利用
初始化赋值。
指针数组主要用于字符串的操作,例如:
static char *name[5]= {“Tom”,”John”,”Mary”,”Smith
Black”,”Rose”};
其中 name是一维数组,每一个元素都是指向字符
数据的指针类型数据,其中 name[0]指向第一个字符串
,Tom”,name[1]指向第二个字符串,John”,…… 。
用指针数组处理字符串不仅可以节省内存,而且
提高运算效率。例如,想对 5个姓名排序,若将字符串
交换位置速度慢,而交换地址则速度快得多。
C++程序设计 湖南大学 杜四春、银红霞
6.6 动态内存分配
所谓动态内存分配是指在程序运行期间根
据实际需要随时申请内存,并在不需要时释放。
应用程序数据所占得内存可以分为 3类:静
态存储区、栈、堆。在程序运行开始前就分配
的存储空间都在静态存储区中;局部变量分配
的存储空间在栈中;动态内存分配的存储空间
在堆中,堆也称为自由存储单元。 new运算符
与 delete运算符一起使用,就可以直接进行动
态内存的申请和释放(也称为创建和删除)。
C++程序设计 湖南大学 杜四春、银红霞
6.6.1 new运算符
new运算符用于申请所需的内存单元, 返回指定类
型的一个指针 。 它的语法格式为:
指针 =new 数据类型;
其中, 指针应预先声明, 指针指向的数据类型与 new
后的数据类型相同 。 若申请成功, 则返回分配单元的
首地址给指针;否则 ( 比如没有足够的内存空间 ),
则返回 0( 一个空指针 ) 。 例如:
int *p;
p=new int;
系统自动根据 int类型的空间大小开辟一个内存单
元,用来保存 int型数据,并将地址保存在指针 p中。
C++程序设计 湖南大学 杜四春、银红霞
对于动态内存分配, 应在分配操作结束以后, 首
先检查返回的地址值是否为 0,以确认内存申请是否成
功 。 在分配成功以后, 就可以使用这个指针 。 例如:
*p=1;
即把 int型数值 1赋给指针 p所指向的 int型内存单元 。
在申请分配内存单元时, 也可以指定分配的内存
大小 。 例如:
int *p;
p=new int(30);
则系统为指针 p分配了 30个字节的内存单元存放 int
型数据。
C++程序设计 湖南大学 杜四春、银红霞
也可以用 new运算符申请一块保存数组的内存单
元, 即创建一个数组 。 创建一维数组的语法格式为:
指针 =new 数据类型 [常量表达式 ];
其中, 常量表达式给出数组元素的个数, 指针指
向分配的内存首地址, 指针的类型与 new后的数据类
型相同 。
例如:
int *p;
p=new int[10];
则系统为指针 p分配了整型数组的内存,数组中有
10个元素。
C++程序设计 湖南大学 杜四春、银红霞
对于动态创建多维数组, 情况要复杂一些 。 以二
维数组为例, 语法格式为:
指针 =new 数据类型 [常量表达式 1][常量表达式 2];
若申请成功, 指针指向分配的内存首地址, 但此
时指针的类型不是 new后的数据类型, 而是一个该类
型的数组, 即指针是一个数组指针 。
例如:
int (*p)[3];
p=new int[2][3];
则系统为指针 p分配了一个二维数组 。
C++程序设计 湖南大学 杜四春、银红霞
6.6.2 delete运算符
delete运算符是释放 new申请到的内存 。 也即, 当
程序中不再需要使用运算符 new创建的某个内存单元
时, 就必须用运算符 delete来删除它 。 它的语法格式为:
delete 指针; //释放非数组内存单元
delete[常量 ] 指针; //释放数组内存单元
其中,指针是指向需要释放的内存单元的指针的
名字。并且 delete只是删除动态内存单元,并不会将指
针本身删除。
C++程序设计 湖南大学 杜四春、银红霞
对于释放对象的不同,delete的语法格式也不同。
对于数组内存单元的释放,一定要带,[常量 ]”部分,
常量告诉 delete数组有多少个元素。如果没有带,[常
量 ]”部分,则只释放数组的第一个元素占据的内存单
元。
例如:对 int型内存单元的申请和释放 。
int *p;
p=new int; //申请内存单元
*p=1;
delete p; //释放内存单元
例如:对数组内存单元的申请和释放 。
int *p;
p=new int[10];
delete[10] p;
C++程序设计 湖南大学 杜四春、银红霞
需要注意的是:
( 1) 在程序中对应于每次使用运算符 new,都应
该相应地使用运算符 delete来释放申请的内存 。 并且对
应于每个运算符 new,只能调用一次 delete来释放内存,
否则有可能导致系统崩溃 。
( 2) 运算符 delete必须用于先前 new分配的有效
指针, 而不能用于未定义的其他任何类型的指针 。
( 3) 对空指针调用 delete是安全的 。
( 4) C++语言保留了 C语言中的两个库函数:
malloc( ) 与 free( ) 。 这两个函数也是实现动态内
存分配作用的, 其功能分别与运算符 new和 delete相似 。
但是最好不要将库函数和运算符混合使用, 否则可能
导致系统崩溃 。
C++程序设计 湖南大学 杜四春、银红霞
6.7 指针与字符串
在 C++语言中, 可以定义一个字符数组,
将字符串存放在该数组中, 通过数组下标来访
问所需的字符;也可以定义一个字符指针, 通
过指针的指向来访问所需的字符 。
6.7.1 通过指针访问字符
如果要通过指针访问一个字符串, 可以将这个指
针指向此字符串, 并利用指针的加 1,减 1操作实现对
各个字符的访问 。
C++程序设计 湖南大学 杜四春、银红霞
例 6-17:利用字符指针访问字符串 。
程序一:
#include<iostream.h>
void main()
{
char *str="hello";
cout<<str<<endl;
}
C++程序设计 湖南大学 杜四春、银红霞
6.7.2 几个常用的字符串处理函数
C++提供了许多字符串处理的库函数 。 常用的有:
1,strcat( )
char *strcat(char *s1,char *s2)
字符串连接函数, 将字符串 s2连接到字符串 s1的
后面, 并返回 s1的地址值 。
2,strcmp( )
int strcmp(const char *s1,const char
*s2,[int n])
字符串比较函数, 比较两个字符串 s1和 s2的大小
( 如果有参数 n,比较前 n个字符的大小 ) 。 当字符串
s1大于, 等于或小于字符串 s2时, 函数返回值分别是
正数, 零和负数 。
C++程序设计 湖南大学 杜四春、银红霞
3,strcpy( )
char *strcpy(char *s1,const char *s2)
将 s2所指向的字符串复制到 s1所指向的字符数组
中, 然后返回 s1的地址值 。
4,strlen( )
int strlen(const char *s)
返回字符串 s的长度 。
当程序中使用这些字符串处理函数时,需要在程
序的开始加上头文件 string.h。
C++程序设计 湖南大学 杜四春、银红霞
6.8 指针与函数
在第四章中,介绍了在调用函数的实参与
形参之间具有三种传递方式:传值方式、地址
传递和引用传递。利用指针作函数参数,可以
方便地实现地址传递。函数可以返回指针,指
针也可以指向函数。
C++程序设计 湖南大学 杜四春、银红霞
6.8.1 指针作为函数参数
如果函数的某个参数是指针, 对这个函数的调用
就是传址调用, 也就是使实参指针和形参指针变量指
向同一内存地址 。 在被调用函数的运行过程中, 对形
参指针所指向的地址中内容的改变都会影响到实参 。
虽然用指针作函数的参数可以使得形参的改变对
相应的实参有效 。 但是, 如果在函数中反复使用指针
进行间接访问, 会影响程序的可读性且容易产生错误,
因此在 C++语言中扩充了引用的概念, 这样既可以实
现指针所带来的功能, 又使程序清晰易读 。 关于引用
的概念将在本书的第八章介绍 。
C++程序设计 湖南大学 杜四春、银红霞
当以数据的地址作为实参调用一个函数时,被调
用函数的形参也必须是可接收地址的变量,并且数据
类型必须与被传送的数据类型相同。
由于数组名是一个指针,因此,数组名也可以用
作函数的参数,也属于传址调用。
注意:在函数的传址调用中,传递的参数的值并
不改变,也即指针本身的值并不改变,改变的是它指
向的值。
C++程序设计 湖南大学 杜四春、银红霞
6.8.2 指针型函数
除了 void类型的函数之外,函数在调用结束后都会
有返回值,指针同样也可以作为函数的返回值。当一
个函数的返回值是指针类型时,这个函数就是指针型
函数。
通常非指针型函数调用结束后,可以返回一个变
量,但是这样每次调用只能返回一个数据。有时需要
从被调函数返回一批数据到主调函数中,这时可以通
过指针型函数来解决。指针型函数在调用后返回一个
指针,通过指针中存储的地址值,主调函数就能访问
该地址中存放的数据,并通过指针算术运算访问这个
地址的前、后内存中的值。因此,通过对空间的有效
组织(如数组、字符串等能前后顺序存放多个变量的
数据类型),就可以返回大量的数据。
C++程序设计 湖南大学 杜四春、银红霞
定义指针型函数的函数头的一般语法格式为:
数据类型 *函数名 ( 参数表 )
其中, 数据类型是函数返回的指针所指向数据的
类型; *函数名声明了一个指针型的函数;参数表是函
数的形参列表 。
例如:
int *fun(a,b)
它表示函数 fun返回一个指针值,这个指针指向一
个整型数据,a,b是形参。
C++程序设计 湖南大学 杜四春、银红霞
6.8.3 函数指针
函数指针就是指向函数的指针 。 定义函数指针的
语法格式为:
数据类型 ( *函数指针名 ) ( 参数表 ) ;
其中, 数据类型是指函数指针所指向函数的返回
值的类型, 参数表中指明该函数指针所指向函数的形
参类型和个数 。
例如:
int (*p)(int,int);
就定义了一个函数指针 p,它指向一个返回整型值,
有两个整型参数的函数。
C++程序设计 湖南大学 杜四春、银红霞
在定义了指向函数的指针变量后, 在使用此函数
指针之前, 必须先给它赋值, 使它指向一个函数的入
口地址 。 由于函数名是函数在内存中的首地址, 因此
可以赋给函数指针变量 。 赋值的一般语法格式为:
函数指针名 =函数名;
例如, 对上面刚定义的函数指针 p,可以给它赋值
如下:
p=func1;
其中, 函数名所代表的函数必须使一个已经定义
过的, 和函数指针具有相同返回类型的函数 。 并且等
号后面只需写函数名而不要写参数, 例如不要写成下
列形式:
p=func1(a,b);
C++程序设计 湖南大学 杜四春、银红霞
当函数指针指向某函数以后, 可以用下列形式调
用函数:
( *指针变量 ) ( 实参表列 )
例如,( *p) ( a,b), 它相当于 funl( a,b) 。
必须指出的是:指针的运算在这里是无意义的。
因为指针指向函数的首地址。当用指针调用函数时,
程序是从指针所指向的位置开始按程序执行,若进行
指针运算,程序的执行就不是从函数的开始位置执行,
这就会造成错误。
C++程序设计 湖南大学 杜四春、银红霞
与定义一般变量指针数组一样, C++语言中也可
以定义具有特定返回类型和特定参数类型的函数指针
数组 。 函数指针数组也可以是多维的, 不过在实际编
程中多只用到一维函数指针数组 。 定义它的语法格式
如下:
数据类型 ( *函数指针名 [常量表达式 ]) ( 参数表 ) ;
例如:
int (*p[5])(int,int);
就定义了一个含有 5个元素的函数指针数组,其中
的每个元素都是一个指向函数的指针,且指向的函数
都是返回值类型为整型,带两个整型参数的函数。