6.4 指针和动态内存分配所谓动态内存分配,是指程序由于某些原因而在运行过程中才可以确定内存空间的需求,从而随时向操作系统提出内存分配的请求。
动态内存分配需要用到指针和 new 运算符,其一般形式为:
pointer = new type;
若系统成功地为请求分配了内存,则指针 pointer 的值将是系统为程序所分配之内存的首地址;否则为一空指针。
当程序不再使用动态内存时(包括程序退出之前) 必须 将动态内存返还给系统。释放动态内存需要用到 delete 运算符,
其一般形式为:
delete point;
由于 C++ 语言的变量说明非常灵活,所以为单个变量申请动态内存的实用价值不大,通常都是为一个数组申请一块动态内存(称为动态数组)。申请动态数组的一般形式为:
pointer = new type[size][c2][c3]…;
其中,size 为一整型表达式,各 ci 均为整型常量。当动态数组的维数大于 1 时,接受动态数组首地址的指针也应当是一个多维数组指针。例:
int (*p)[4];
p = new int[n][4];
char (*cp)[2][3],*cq;
cp = new char[(x + y) * k][2][3];
cq = new char[x + y];
释放动态数组时,需使用如下的一般形式:
delete []p;
int *CreateTab(int n)
{
int *p;
p = new int[n];
return p;
}
int *ip = CreateTab(20);
if(ip != 0) {
// 对动态数组 ip 的一些应用
}
delete []ip;
6.5 引用
6.5.1 引用的说明与使用
<storage> type &ref_name <= var>;
其中:符号,&”是引用说明符( 注意,它与取地址运算符和按位与运算符同形 )。
一般情况下,在说明一个引用的同时需要用一个与其数据类型相同的变量对其进行初始化。这是因为 引用不是变量,它是用户为某一变量所起的别名( Alias)。 例:
int i = 5,&ri = i;
cout << ri << endl; // 输出 5
ri += 8;
cout << i << endl; // 输出 13
若在说明引用时没有将一个变量与之联系起来,则 C++ 将为该引用生成一个无名的临时变量,而引用名即为该临时变量的名字。例:
int i = 5,&ri;
ri = i; // 将变量 i 的值赋给临时变量 ri
ri += 8;
cout << i << endl; // 输入为 5
在一个程序中,除了可以说明对一般变量的引用外,还可以说明对指针的引用。但由于引用不是变量,所以不得说明引用的引用。例下面的说明就是错误的:
int i,&ri = i,&&rri = ri;
6.5.2 引用与函数
6.5.2.1 引用作为函数的参数
C++ 语言引入引用主要是为在函数间传递数据提供方便。引用参数的实参与形参的结合方式为 引用调用 。与地址调用相似,引用调用可以使函数修改实参的值。例:
void Swap(int &a,int &b)
{ // 调用 Swap( ) 函数
int t = a; int x = 3,y = 5;
a = b; Swap(x,y);
b = t; // x == 5,y == 3
}
6.5.2.2 值为引用的函数函数返回引用最能表现出引用的本质。由于引用就是与其相联系的变量,所以值为引用的函数返回的实际上就是一个变量(而不再是一个值)。这样的函数可以用在任何变量可以出现的位置(比如赋值运算符的左侧)。例如:
int &Index(int a[],int n)
{
return a[n];
}
//…
Index(iArr,5) = 8; // 等效于 iArr[5] = 8;
6.6 void 和 const 指针
6.6.1 void 指针
void 指针也叫无值型指针,它可以接受任何类型指针的值。
然而,若将无值型指针赋给其它类型指针,则必须进行强制类型转换。例:
int *ip;
void *vp;
vp = ip;
ip = vp; // 错误!
ip = (int*)vp; // 正确利用无值型指针可以编写出较为通用的程序。
void SortAny(void *vArr,unsigned n,int nSize,
int(*Comp)(void*,void*))
{
char *p,*q;
for(int i = 0; i < n - 1; i ++) {
p = (char*)vArr + i * nSize;
for(int j = i + 1; j < n; j ++) {
q = (char*)vArr + j * nSize;
if(Comp(p,q) > 0)
for(int k = 0; k < nSize; k ++) {
char temp = p[k];
p[k] = q[k];
q[k] = temp;
}
}
}
}
int Cmp_Integers(void *a,void *b)
{
return *(int*)a - *(int*)b;
}
int Cmp_Floats(void *a,void *b)
{
return (*(flaot*)a - *(float*)b < 0? -1,1);
}
int iArr[20] = {…};
float fArr[30] = {…};
SortAny(iArr,20,sizeof(int),Cmp_Integers);
SortAny(fArr,30,sizeof(float),Cmp_Flaots);
6.6.2 const 指针当用关键字 const 来修饰一个指针时,视其位置的不同则意义亦不相同。例:
const char *pc = "Points to constant char"; // 指向常量
char *const cp = "Constant pointer"; // 常指针
const char *const cpc = "Constant pointer points to
constant char"; // 指向常量的常指针
*pc = 'a'; // 错误!
cp = qp; // 错误!
第 7章 结构、联合和枚举
7.1 类型定义
typedef type identifier;
其中,typedef 是 C++ 语言的一个关键字; type 是任何一个合法的数据类型(包括用户定义的数据类型); identifier 是一个标识符,它就是用户为所定义的数据类型取的名字。
例:
typedef int Integer;
typedef unsigned long UL;
有了以上的类型定义,标识符 Integer 和 UL 就分别等同于数据类型说明关键字 int 和 unsigned long。因而在程序中可以用它们来说明变量的数据类型:
Integer i,j; // 等效于 int i,j;
UL lParam; // 等效于 unsigned long lParam;
因此,可以认为类型定义实际上就是为已有的数据类型另起了一个名字,而不是为 C++ 语言定义了新的数据类型。
C++ 语言允许在一个说明语句中为同一个数据类型取多个别名。例:
typedef int Integer,IType;
typedef float Real,*pReal,&rReal;
//
Real PI = 3.14159;
rReal rPI = PI;
pReal pPI = &PI;
7.2 结构
7.2.1 定义结构
struct struct_name {
members;
};
其中,struct 为结构说明关键字; struct_name 为结构名;而
members 则是一个个的数据项,它们表现为一个个的变量,
叫做结构成员。例:
struct Person {
char Name[20];
unsigned int Age;
unsigned char Sex;
};
结构定义描述了一个具体结构的组织形式。比如,
Person 类型的变量对内存的占用如右图所示。
由图可见,一个 Person 类的结构变量需要占用 23 个字节的内存单元( 16 位环境),且其存储顺序为:前
20 个字节存放 Name 成员的值;第 21,22 字节存放
Age 成员的值;最后一个字节存放 Sex 成员的值。
在一个程序中,一个结构一经定义,就如同用户为 C++
语言增加了一个新的数据类型。
Name(20)
Age(2)
Sex(1)
7.2.2 说明结构变量
<storage> struct_name var<,var...>;
例:
Person per1,per2,*pPer;
C++ 语言允许在定义结构的同时说明结构变量。例如:
struct Position {
int x;
int y;
} pos,*pPos;
在说明结构变量的同时还可以对变量进行初始化:
Position p = {100,200};
7.2.3 访问结构变量对结构变量的访问包括访问整个结构变量和访问结构变量中的某个(些)成员。对整个结构变量的访问通常用在两个结构就是相互赋值或作为函数调用时的实参。例:
per1 = per2;
f(pos);
访问结构成员时需要用到成员选择运算符,,” 或,->”,其一般形式为:
var.member 或 pVar->member
例:
per1.Age = 22;
pPer->Age = 22;
// BRAUN.H
#if !defined _BRAUN_H_
#define _BRAUN_H_
typedef struct {
int x;
int y;
} POSITION,*PPOSITION,&RPOSITION;
POSITION NewPosition(int,int);
#endif
// BRAUN.CPP
#include <stdlib.h>
#include "braun.h"
POSITION NewPosition(int MaxX,int MaxY)
{
POSITION pos;
pos.x = rand() % MaxX;
pos.y = rand() % MaxY;
return pos;
}
// TBRAUN.CPP
#include <iostream.h>
#include <conio.h>
#include "braun.h"
void main()
{
POSITION ps;
whild(!kbhit()) {
ps = NewPosition(640,480);
cout << ps.x << '\t' << ps.y << endl;
}
}
7.3 结构成员、结构数组和结构指针及引用
7.3.1 结构成员结构的成员可以是任何一种已存在的数据类型。一个结构一经定义,就相当于 C++ 语言的一种数据类型,因而它就可以用作另一个结构之成员的数据类型。例如,设结构 Person 已被说明,则可以进而说明如下的三口之家:
struct Family {
Person Father;
Person Mother;
Person Child;
char Address[41];
};
对于结构变量中的结构成员,访问常常需要落实到最终的成员。例如,输出父亲的姓名:
Family fam;
//...
cout << fam.Father.Name << endl;
对包含有结构成员之结构变量中普通成员的访问则与无结构成员之结构变量相同。例如,为家庭地址赋值:
strcpy(fam.Address,"西安理工大学 ");
7.3.2 结构数组由于结构就相当于 C++ 语言的一种数据类型,因此可以说明其元素为结构变量的数组。例:
Person PerArr[30];
for(int i = 0; i < 30; i ++) {
cin >> PerArr[i].Name;
cin >> PerArr[i].Age;
cin >> PerArr[i].Sex;
}
//...
7.3.3 结构指针和引用与简单变量一样,可以说明指向结构的指针、结构类型的引用。例如:
Person Per,*pPer = &Per,&rPer = Per;
//...
strcpy(pPer->Name,"张 三 ");
Per.Age = 22;
rPer.Sex = 'm';
7.4 结构与函数函数参数的数据类型可以为结构类型。当参数为结构变量时,
属于赋值调用;参数为指针或数组时,为地址调用;参数为引用时,为引用调用。例:
void SetMemberData(Person &per)
{
cout << "Input person's name,age and sex(m/f)\n";
cin >> per.Name >> per.Age >> per.Sex;
}
Person Per;
SetMemberData(Per);
7.5 位域位域实际上是一种特殊的结构,它与结构的区别在于其数据大小是以二进制位为单位而不是以字节为单位的。因此,位域通常比结构更省存储单元。说明位域的一般形式为:
struct struct_name {
unsigned member_name,constant;

};
其中,member_name 为一个成员的名字; constant 为一个非负的整型常量。
例,将 Person 定义成位域:
struct Person {
char Name[9];
unsigned Age,7; // 年龄取值范围为 0~127
unsignde Sex,1; // 分别用 0 和 1 表示男和女
};
void SetMemberData(Person &per)
{
char temp;
cout << "Input person's name,age and sex(m/f)\n";
cin >> per.Name >> per.Age >> temp;
per.Sex = (temp == 'm'? 0,1);
}
7.6 联合
7.6.1 定义联合
union union_name {
members;
};
可以看出,定义联合与定义结构的格式完全相同 。
例:
union un {
char ch;
int x;
float real;
};
然而,联合与结构的存储结构则大相径庭。比如,若上述的
un 是一个结构,则其变量所需的存储单元为 7 个字节。而
un 型联合变量仅需 4 个字节的存储单元。如下图所示。实际上,联合变量所需的存储空间取决于其中最大成员所需的存储量(本例为 real)。
联合变量通常用在那些其成员不同时使用的场合,此时它比结构变量节省存储空间。
ch x
real
7.6.2 使用联合
<storage> union_name var <,var… >;
例:
union Score {
char Sc; // 5 级计分成绩
int Pt; // 百分制成绩
};
void Convert(Scroe &s) // 将百分制转换成 5 级计分
{
switch(s.Pt / 10) {
case 10:
case 9:
s.Sc = 'A';
break;
case 8:
s.Sc = 'B';
break;
case 7:
s.Sc = 'C';
break;
case 6:
s.Sc = 'D';
break;
default:
s.Sc = 'E';
}
}
在说明联合变量的同时也可以对变量进行初始化,但给出的初值的数据类型必须与联合变量中第一个成员的数据类型相匹配。例:
Score score = {'B'};
若初始化时写成:
Score score = {88};
则变量 score 中 Sc 成员 的值将被初始化成 'X'。
另一方面,程序员可以利用联合变量的特点来实现一些特殊的操作。比如,将一个整型数据分割成高、低两个 8 位数据。
union Data {
int Integer;
char cArr[2];
};
//...
Data dt = {0x1234};
cout << (int)dt.cArr[0] << '\t' << int(dt.cArr[1]) << endl;
其输出为:
52 18
注意:数据在计算机中的存储形式为 高位存入高地址,低位存入低地址 。
7.7 枚举
7.7.1 枚举类型的定义及其变量的说明
enum enum_name{identifiers};
其中:花括号中是一个个用逗号分隔的标识符,叫做枚举常量。例:
enum BOOL{FALSE,TRUE};
就定义了一个名为 BOOL 的枚举类型,该类型变量的可取值分别为 FALSE 和 TRUE。
BOOL b,c = TRUE;
应当说明的是,枚举常量表现为一串字符,但实质上是一个整数值。比如,设已有了上述枚举常量 c 的说明,则:
cout << c << endl;
的输出却是 1。
这是由于编译器在处理枚举定义时,将其第一个常量的值赋为 0,第二个常量的值赋为 1,,余此类推。
然而,C++ 语言允许用户自行设定枚举常量的值。例:
enum Boolean{TRUE = 1,FALSE = 0};
enum Sign{NEGTIVE = -1,ZERO,POSITIVE};
由于枚举常量实际上是一个整型常,所以也可以为一个具体的枚举变量赋上一个整数。例:
c = 0; // 等效于 c = FALSE
c = 100; // 合法但不合理在实用中并不提倡这样使用枚举 。
习题:
17,18,19