6.4 指针和动态内存分配
所谓动态内存分配,是指程序由于某些原因而在运行过程中才可以确定内存空间的需求,从而随时向操作系统提出内存分配的请求。
动态内存分配需要用到指针和new 运算符,其一般形式为:
pointer = new type;
若系统成功地为请求分配了内存,则指针pointer 的值将是系统为程序所分配之内存的首地址;否则为一空指针。
当程序不再使用动态内存时(包括程序退出之前)必须将动态内存返还给系统。释放动态内存需要用到delete 运算符,其一般形式为:
delete point;
由于C++ 语言的变量说明非常灵活,所以为单个变量申请动态内存的实用价值不大,通常都是为一个数组申请一块动态内存(称为动态数组)。申请动态数组的一般形式为:pointer = new type[size][c2][c3]…;
其中:size 为一整型表达式,各c i均为整型常量。当动态数组的维数大于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 引用的说明与使用
其中:符号“&”是引用说明符(注意,它与取地址运算符和按位与运算符同形)。
一般情况下,在说明一个引用的同时需要用一个与其数据类型相同的变量对其进行初始化。这是因为引用不是变量,它是用户为某一变量所起的别名(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说明结构变量
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
#include"braun.h"
POSITION NewPosition(int MaxX, int MaxY) {
POSITION pos;
pos.x = rand() % MaxX;
pos.y = rand() % MaxY;
return pos;
}
//TBRAUN.CPP
#include
#include
#include"braun.h"
void main()
{
POSITION ps;
whild(!kbhit()){
ps = NewPosition(640, 480);
cout << ps.x << '\t' << ps.y << endl;
}
}
数组元素存储地址的计算 一维数组 设一维数组A[n]存放在n 个连续的存储单元中,每个数组元素占一个存储单元(不妨设为C 个连续字节). 如果数组元素A[0]的首地址是L ,则A[1]的首地址是L+C ,A[2]的首地址是L+2C ,… …,依次类推,对于01≤≤-i n 有: C i A Loc i A Loc *])0[(])[(+= 二维数组 二维数组的每个元素含两个下标,如果将二维数组的第一个下标理解为行号,第二个下标理解为列号,然后按行列次序排列个元素,则二维数组呈阵列形状。例如 mn m m n n a a a a a a a a a A ΛΛΛ Λ21 222 2111211= 它是一个行号为1~m ,列号为1~n 的二维数组元素阵列。 如何保存二维数组? 首先要确定一个顺序 222120 121110 020100a a a a a a a a a B = 222120 121110020100a a a a a a a a a B = 22 2120 121110 020100a a a a a a a a a B = 2221 20 12 11 10 02 01 00 a a a a a a a a a 第0行 第1行 第2行
222120 121110 020100a a a a a a a a a B = 2221 20 121110020100a a a a a a a a a B = 22 2120 121110 020100a a a a a a a a a B = 2212 02 21 11 01 20 10 00 a a a a a a a a a 第0列 第1列 第2列 222120 121110 020100a a a a a a a a a B = 221202a a a a a B = B = B B 021******* 00 21 10 20 a a a a a a a a a 设count 为数组B 中元素的个数,则count=9 按行优先存储
C语言数组元素的查询 在实际开发中,经常需要查询数组中的元素。例如,学校为每位同学分配了一个唯一的编号,现在有一个数组,保存了实验班所有同学的编号信息,如果有家?想知道他的孩子是否进入了实验班,只要提供孩子子的编号就可以,如果编号和数组中的某个元素相等,就进入了实验班,否则就没进入.不幸的是,C语言标准库没有提供与数组查询相关的函数,所以我们只能自己编写代码。 对无序数组的查询所谓无序数组,就是数组元素的排列没有规律。无序数组元素查询的思路也很简单,就是用循环遍历数组中的每个元素,把要查询的值挨个比比较一遍。请看下面的代码: #include
} if(subscript<0){ printf("%d isn't in the array.\n", num); }else{ printf("%d is in the array, and it's subscript is %d.\n ", num, subscript); } system("pause"); return 0; } 运行结果: Please input an integer: 100 100 is in the array, and it's subscript is 7. 或者 Please input an integer: 28 28 isn't in the array. 这段代码的作用是让用户输入一个数字,判断该数字是否在数组中,如果在,就打印出下标。 第10~15行代码是关键,它会遍历数组中的每个元素,和用户输入的数字进行比较,如果相等就获取它的下标并跳出循环。
精心整理一填空题 1)数组的元素通过下标来访问,数组Array的长度为Array.length。 2)数组复制时,"="将一个数组的引用传递给另一个数组。 3)JVM将数组存储在栈(堆或栈)中。 4)数组的二分查找法运用的前提条件是数组已经排序。 5)Java中数组的下标的数据类型是整型。 6)数组最小的下标是0。 7) 8) 9) 10) 11) 12) 1. 2. 3. 4. 5. 6. 7. 8.A. 9. 10. 11.A 12.C.charstr[5]={"hi"}; D.charstr[100]=""; 13.数组在Java中储存在C中 14.A.栈 B.队列 C.堆 D.链表 15.下面程序的运行结果是____ main(){ inta[][]={{1,2,3},{4,5,6}}; }
A.3 B.4 C.5 D.6 16.下面程序的运行结果是_C___ 17.m ain(){ intx=30; int[]numbers=newint[x]; x=60; (numbers.length); } A.60 B.20 C.30 D.50 18. 19.m 20.c 21.i 22.w 23. 24.A 25.C 26. A. C.用 27. 28. A. 29. 30. A.char--'"u0000' B.Boolean--true C.float--0.0f D.int--0 31.关于数组作为方法的参数时,向方法传递的是A A.数组的引用 B.数组的栈地址 C.数组自身 D.数组的元素 32.关于数组复制,下列说法错误的是AC A."="可以实现数组复制 B.运用循环语句进行数组复制必须两个数组长度相同 C.arraycopy()方法没有给目标数组分配内存空间 D.数组复制是数组引用的传递
第 5 章数组 一、选择题 1.设有一个10阶的对称矩阵A,采用压缩存储方式,以行序为主存储,a11为第一元素,其存储地址为1,每个元素占一个地址空间,则a85的地址为( B )。 A. 13 B. 33 C. 18 D. 40 2.有一个二维数组A[1:6,0:7]每个数组元素用相邻的6个字节存储,存储器按字节编址,那么这个数组的体积是(①L)个字节。假设存储数组元素A[1,0]的第一个字节的地址是0,则存储数组A的最后一个元素的第一个字节的地址是(②J)。若按行存储,则A[2,4]的第一个字节的地址是(③C)。若按列存储,则A[5,7]的第一个字节的地址是(④I)。就一般情况而言,当(⑤C)时,按行存储的A[I,J]地址与按列存储的A[J,I]地址相等。供选择的答案: ①-④: A.12 B. 66 C. 72 D. 96 E. 114 F. 120 G. 156 H. 234 I. 276 J. 282 K. 283 L. 288 ⑤: A.行与列的上界相同 B.行与列的下界相同 C.行与列的上、下界都相同 D.行的元素个数与列的元素个数相同 3.设有数组A[i,j],数组的每个元素长度为3字节,i的值为1到8,j的值为1到10,数组从内存首地址BA开始顺序存放,当用以列为主存放时,元素A[5,8]的存储首地址为( B )。 A. BA+141 B. BA+180 C. BA+222 D. BA+225 4.假设以行序为主序存储二维数组A=array[1..100,1..100],设每个数据元素占2个存储单元,基地址为10,则LOC[5,5]=( B )。 A. 808 B. 818 C. 1010 D. 1020 5.数组A[0..5,0..6]的每个元素占五个字节,将其按列优先次序存储在起始地址为1000的内存单元中,则元素A[5,5]的地址是( A )。 A. 1175 B. 1180 C. 1205 D. 1210 6.有一个二维数组A[0:8,1:5],每个数组元素用相邻的4个字节存储,存储器按字节编址,假设存储数组元素A[0,1]的第一个字节的地址是0,存储数组A的最后一个元素的第一个字节的地址是(①H )。若按行存储,则A[3,5]和 A[5,3]的第一个字节的地址是(②C )和(③E )。若按列存储,则A[7,1]和A[2,4]的第一个字节的地址是(④A )和(⑤F )。 ①-⑤:A.28 B.44 C.76 D.92 E.108 F.116 G.132 H.176 I.184 J.188 7.将一个A[1..100,1..100]的三对角矩阵,按行优先存入一维数组B[1‥298]中,A中元素A6665(即该元素下标i=66,j=65),在B数组中的位置K为( B )。供选择的答案: A. 198 B. 195 C. 197 8.二维数组A的元素都是6个字符组成的串,行下标i的范围从0到8,列下标j的范圈从1到10。从供选择的答案中选出应填入下列关于数组存储叙述中()内的正确答案。 (1)存放A至少需要( E )个字节; (2)A的第8列和第5行共占( A )个字节; (3)若A按行存放,元素A[8,5]的起始地址与A按列存放时的元素( B )的起始地址一致。 供选择的答案: (1)A. 90 B. 180 C. 240 D. 270 E. 540 (2)A. 108 B. 114 C. 54 D. 60 E. 150 (3)A. A[8,5] B. A[3,10] C. A[5,8] D. A[0,9] 9.二维数组A的每个元素是由6个字符组成的串,其行下标i=0,1,…,8,列下标j=1,2,…,10。若A按行先存储,元素A[8,5]的起始地址与当A按列先存储时的元素( B )的起始地址相同。设每个字符占一个字节。 A. A[8,5] B. A[3,10] C. A[5,8] D. A[0,9] 10.若对n阶对称矩阵A以行序为主序方式将其下三角形的元素(包括主对角线上所有元素)依次存放于一维数组B[1..(n(n+1))/2]中,则在B中确定a ij(i 一、选择题 1. 将一个A[1..100,1..100]的三对角矩阵,按行优先存入一维数组B[1‥298]中,A中元素A6665(即该元素下标i=66,j=65),在B数组中的位置K为()。供选择的答案: A. 198 B. 195 C. 197 2. 二维数组A的元素都是6个字符组成的串,行下标i的范围从0到8,列下标j的范圈从1到10。从供选择的答案中选出应填入下列关于数组存储叙述中()内的正确答案。(1)存放A至少需要(E)个字节; (2)A的第8列和第5行共占(B)个字节; (3)若A按行存放,元素A[8,5]的起始地址与A按列存放时的元素( B )的起始地址一致。 供选择的答案: (1)A. 90 B. 180 C. 240 D. 270 E. 540 (2)A. 108 B. 114 C. 54 D. 60 E. 150 (3)A. A[8,5] B. A[3,10] C. A[5,8] D. A[0,9] 3. 设A是n*n的对称矩阵,将A的对角线及对角线上方的元素以列为主的次序存放在一维数组B[1..n(n+1)/2]中,对上述任一元素aij(1≤i,j≤n,且i≤j)在B中的位置为( A )。 A. i(i-l)/2+j B. j(j-l)/2+i C. j(j-l)/2+i-1 D. i(i-l)/2+j-1 4. A[N,N]是对称矩阵,将下面三角(包括对角线)以行序存储到一维数组T[N(N+1)/2]中,则对任一上三角元素a[i][j]对应T[k]的下标k是(B)。 A. i(i-1)/2+j B. j(j-1)/2+i C. i(j-i)/2+1 D. j(i-1)/2+1 5. 设二维数组A[1.. m,1..n](即m行n列)按行存储在数组B[1.. m*n]中,则二维数组元素A[i,j]在一维数组B中的下标为( A)。 A.(i-1)*n+j B.(i-1)*n+j-1 C. i*(j-1) D. j*m+i-1 6.有一个二维数组A,行下标的范围是0到8,列下标的范围是1到5,每个数组元素用相邻的4个字节存储。存储器按字节编址。假设存储数组元素A[0,1]的第一个字节的地址是0。 存储数组A的最后一个元素的第一个字节的地址是(⑧)。若按行存储,则A[3,5]和A[5,3]的第一个字节的地址分别是(③)和(⑤)。若按列存储,则A[7,1]和A[2,4]的第一个字节的地址分别是(①)和(⑥)。供选择的答案:A~E:①28 ②44 ③76 ④92 ⑤108 ⑥116 ⑦132 ⑧176 ⑨184 ⑩188 7.有一个二维数组A,行下标的范围是1到6,列下标的范围是0到7,每个数组元素用相邻的6个字节存储,存储器按字节编址。那么,这个数组的体积是((12))个字节。假设存储数组元素A[1,0]的第一个字节的地址是0,则存储数组A的最后一个元素的第一个字节的地址是((11))。若按行存储,则A[2,4]的第一个字节的地址是(③)。若按列存储,则A[5,7]的第一个字节的地址是(⑨)。供选择的答案A~D:①12 ②66 ③72 ④96 ⑤114 ⑥120 ⑦156 ⑧234 ⑨276 ⑩282 (11)283 (12)288 8. 设矩阵A是一个对称矩阵,为了节省存储,将其下三角部分(如下图所示)按行序存放在一维数组B[ 1, n(n-1)/2 ]中,对下三角部分中任一元素ai,j(i≤j), 在一维数组B中下标k 的值是:(B) A.i(i-1)/2+j-1 B.i(i-1)/2+j C.i(i+1)/2+j-1 D.i(i+1)/2+j c++中关于数组作为函数参数并传递数组元素个数的几种有效方法的讨论 //由于数组的元素个数默认情况下是不作为实参内容传入调用函数的,本程序用来讨论有此带来的 //相关问题,以及解决问题方法,即给调用函数传递数组的元素个数的几种有效方法并实现它 #include } void PutArray5(vector数据结构习题3(数组)
c++中关于数组作为函数参数并传递数组元素个数的几种有效方法的讨论