十一、程序动态存储结构 十二、指针的类型转换和匹配关系...

43
1 十十十十十十十十 一、 十十 十十十十十十十十十十十十 十十 十十十十十十十十十十十十十十

description

第 6 章 指针与数组. 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算. 十一、程序动态存储结构 系统将内存数据分为三个段即数据段、代码段和堆栈 段。 编译程序对源程序进行编译时,将函数中的执行语句部 分编译成指令代码放置在代码区,将静态变量、全局变量与 字符串常数存放在全局数据区,函数的形式参数放置在临时 开辟的称之为先进后出的堆栈区,堆栈区内尚包含函数定义 部分中内部引入的局部变量或局部数组。 另外系统提供一些函数进行内存的动态控制,这一片可 - PowerPoint PPT Presentation

Transcript of 十一、程序动态存储结构 十二、指针的类型转换和匹配关系...

Page 1: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

1

十一、程序动态存储结构

十二、指针的类型转换和匹配关系

十三、下标表达式与访问指针寻址计算

Page 2: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

2

十一、程序动态存储结构 系统将内存数据分为三个段即数据段、代码段和堆栈段。 编译程序对源程序进行编译时,将函数中的执行语句部分编译成指令代码放置在代码区,将静态变量、全局变量与字符串常数存放在全局数据区,函数的形式参数放置在临时开辟的称之为先进后出的堆栈区,堆栈区内尚包含函数定义部分中内部引入的局部变量或局部数组。

另外系统提供一些函数进行内存的动态控制,这一片可以由用户控制的动态区域称为堆结构或堆区,这一可由用户动态控制的区域实际上是隶属于数据段的。 剩下的存储单元为自由内存空间。

Page 3: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

3

1. C++ 中 new 运算符和 delete 运算符 C++ 提供的关键字 new 是专门用于建立动态内存分配的。 new 运算符试图动态地根据其后的类型名分配堆内存

空间,也即在堆空间定义变量或数组。 new 运算符不能用于分配一个函数 , 但可以用于分配一个函数指针。 new 运算符定义堆中变量或对象的格式为: new type(initialList) new 类名 ( 初始化表 )

表达式的结果是 type* 类型的地址。下面的几个语句: int x; int *p=&x; *p = 5 ;

Page 4: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

4

对变量 x 进行了间接赋值,上面的指针变量 p 也可以指向堆空间: p=new int(5); p=new int; *p = 5 ;

值得注意变量 x 的地址在变量 x 的生存期是不变的,new 运算符分配的地址在其生存期也具有类似的性质,不同的是该存储空间可以由用户通过 delete 运算符释放。 new 运算符构成的表达式建立或定义动态一维数组的

语法格式为: new type [dynSize] new 类名 [ 动态数组大小 ]

表达式的结果是 type* 类型的地址。

Page 5: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

5

系统在堆区中开辟了一块大小为 dynSize *sizeof (type)

的空间,表达式的结果就指向这块存储空间的开始位置,因此这一地址具有数组名一样的性质。 当 new 运算符建立动态数组时,不同时指定数组元素

的初值。 这个语法格式适用于一维数组的动态确定,动态数组大小 dynSize 初始化的位置是灵活的 , 可以在运行时实时地输入,但必须在 new 函数的调用点之前确定。

Page 6: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

6

当不能成功地分配所需要的内存时, new 返回 0 。判断new 的返回值是否为 0 ,可以得知系统中有无足够的空闲内存供程序使用。例如: int *p = new int[10000] ; if (p==0) cout<<”run out of memory” << endl ; 值为零的指针称为空指针,它表示指针的一种状态。 如下语句定义了一个固定指针 hArray , hArray 像一维数组名一样不作为左值, hArray 指向堆空间中的某个存储位置:

long* const hArray = new long[100] ;

Page 7: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

7

new 运算符表达式分配或定义动态二维数组的格式为: new double [ dynSize ][ maxn ]

// new 类名 [ 动态数组大小 ][ 固定数组大小 ]

表达式的结果为 double (*)[maxn] 型的地址,因此可以用如下语句进行初始化: double (*p)[ maxn ]= new double[ dynSize ][ maxn ] ;也可以先定义数组指针然后再调用 new 运算符: double (*p)[ maxn ]; ;... ;

p = new double[ dynSize ][ maxn ] ;

Page 8: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

8

类似地 new 运算符分配或定义动态三维维数组的格式为: new int [ dynSize ] [ maxm ][ maxn ] // new 类名 [ 动态维数 ][ 固定维数 m][ 固定维

数 n]表达式的结果为 int (*)[maxm][maxn] 型的地址,因此可以用如下语句进行初始化: int (*p) [ maxm ][ maxn ]= new int[ dynSize ] [ maxm ][ maxn ] ;上面的数组维数只有 dynSize 是可以动态输入的正数表达式。 其余的必须是编译期间就确定的正整型常数表达式。

Page 9: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

9

delete 运算符释放由 new 运算符创建的对象内存。 delete 运算符表达式的结果为 void 类型的函数返回 ,

因此仅单独调用。 delete 的操作数必须是由 new 运算符返回的地址。 delete 运算符表达式的语法格式为:

delete 指针表达式 ; delete pointer;

或: delete [ ] 指针表达式 ; delete [ ] pointer;

Page 10: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

10

delete 运算符删除后的堆内存空间是未定义的,在删除堆空间后间接访问这个指针则会产生意料不到的结果。 其中 delete pointer 形式的运算符与 new type 匹配,即用于释放单个对象指针。 而 delete [ ] pointer 形式的运算符与 new type[ size ]

匹配,即用于释放动态数组。 对不是用 new 分配的对象指针使用 delete 会产生意料

不到的结果。

Page 11: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

11

十一、程序动态存储结构 也可以当作多维数组使用。 这个 void* 型的首地址须强制转换为确定类型的指针,也就是明确内存空间的具体访问规则。 如果系统没有足够的多余内存, malloc 函数

返回 NULL 即 0 。

Page 12: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

12

例如: double* p = (double*) malloc (24);

// 相当于 p= (double*)malloc(sizeof(double[3]));

long ( *q )[ 3 ] = (long (*)[ 3 ])malloc (24); // 或 q= (long (*)[3])malloc(sizeof(long[2][3])); 表示两个 24 个字节的堆空间分别委托一级指针 p 和数组指针 q管理,相当于 p管理 double 型的一维数组 p[3],q管理long 型的二维数组 q[2][3] 。 系统并不自动释放用户申请 malloc 函数分配的堆空间,良好的编程习惯是及时地归还系统资源, malloc 函数分配的空间应由 free 函数来清除。

Page 13: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

13

free 函数的原型为: void free ( void* ptr );ptr 匹配malloc 函数分配的内存块地址,例如 :free(p), free(q) ;

不要将其它的指针值作为 free 的实参,以免引起不可预料的错误。

new 和 delete 运算符同 malloc 函数和 free 函数的工作原理是一致的,本质上 new 和 delete 运算符是 malloc 和 free

等内存控制函数的某种映射。 在新开发软件的时候优先采用 new 与 delete 运算符。new 运算函数的返回类型已经参照malloc 内在机制进行了严格的界定,因此无需提供显示类型转换,但此时接受指针应与返回类型严格匹配才行。 malloc 函数则要求提供类型转换,以便将泛泛的一块内存空间用于具体类型的数据运算。

Page 14: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

14

[ 例 ] 动态申请一个一维数组 #include<stdio.h>

#include<malloc.h> void main(void) { int m; scanf ("%d", &m); // 数组的维数 m 动态实时输入

int * a,k; if (m%2) a= new int [m]; // 相当于在堆空间定义 int a[m];

else a= (int*)malloc (m*sizeof (int)); // 与上面分支 new等价的 malloc版本。 for( k=0; k<m; k++) a [ k ]=k; // 在 a 指向堆空间时不要改动 a 的值 int *p= a; //设置另一个指针 p遍历访问堆空间

Page 15: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

15

for (k=0; k<m; k++, p++) printf ("a[%d]=%d ", k,*p); if (m%2) delete [ ] a; // delete [ ] a 匹配 a= new int[m]

else free (a); // free(a) 匹配 a=(int*)malloc (m*sizeof(int)) } // 动态运行结果为 :4 a[0]=0 a[1]=1 a[2]=2 a[3]=3

定义语句 {int a[4];} 定义一个 4 个元素的数组 , 这个数组名 a 具有 int*const 类型属性。不妨认为语句 {int* a=new int [m];}定义一个 m 个元素的数组 , 但 m 是可以动态改变的整型变量。该数组通过 int* 型的指针 a管理 , 同时指望 delete 运算符收回相应的内存空间。

Page 16: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

16

[ 例 ] 申请一个二维数组,第一个下标是可变的

#include<stdio.h>

#include<malloc.h>

void main(void)

{ const int N=4;

int m, k, j;

scanf("%d",&m);

int (* d) [N];

if (m%2) d= new int [m][N];

else d= (int (*)[N]) malloc (m*N*sizeof (int));

Page 17: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

17

for( k=0;k<m;k++) // 在 d 指向堆空间时不要改动 d

的值for( j=0; j<N; j++) d [k] [j]=k*N+j;int (*q)[N]= d;for (k=0; k<m; k++, q++){ int *p =*q; for( j=0; j<N; j++) printf ("d [%d][%d]=%d ", k, j, p[j]); printf ("\n");} if (m%2) delete [ ] d; else free (d);}

Page 18: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

18

定义语句 {int d[2][4];} 定义一个 2 行 4列的二维数组 , 这

个数组名 d 具有 int (*)[4] 类型属性。不妨认为语句 {(int (*d)[N] = (int (*)[N]) malloc (m*N*sizeof (int));}

定义一个 m 行 N列的二维数组 d[m][N], 但 m 是可以动态改变

的整型变量。 该数组通过 int (*)[N] 类型属性的指针 d管理,同时指望

free 函数收回相应的内存空间二维动态数组由一维动态指针数组构成,该指针数组的每一个元素分别指向一维动态数组。 二维动态可调数组是高频采用的编程技术。

Page 19: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

19

下面的程序建立二级指针,该二级指针与二维动态数组相联系,指向堆空间。[ 例 ] 动态的二维数组 pp[M][N] 分配 , 维数 M,N都可实时输

入 typedef long type; #include<iostream.h> #include<malloc.h> #include<process.h> void main(void) { type** pp; cout<<"input number M:"; int M=2,N=6; cin>>M; pp=(type**) malloc (M*sizeof (type*)); if (pp==NULL) exit (1); cout<<"input number N:"; cin>>N;

Page 20: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

20

int j ; for (j=0; j<M; j++) { pp[ j ]=(type*) malloc (N*sizeof (type)); if (pp[ j ]==NULL) exit(1); } int k ; for (k=0; k<M; k++) for ( j=0; j<N; j++) pp[ k ][ j ]=k*N+j+1; for (k=0; k<M; k++) { cout<<endl; for (j=0; j<N; j++) cout<<" pp["<<k<<"]["<<j<<"]="<<pp [ k ][ j ]}

Page 21: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

21

for (j=0; j<M; j++)

if (pp[ j ]!=NULL) free (pp[ j ]);

if (pp!=NULL) free (pp);

}

// 运行程序输出结果为input number M:2input number N:6pp[0][0]=1 pp[0][1]=2 pp[0][2]=3 pp[0][3]=4 pp[0][4]=5 pp[0][5]=6

pp[1][0]=7 pp[1][1]=8 pp[1][2]=9 pp[1][3]=10 pp[1][4]=11 pp[1][5]=12

Page 22: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

22

十二、指针的类型转换和匹配关系

1. 指针类型转换 转换为一级指针和指向二维数组指针的强制类型转换的语法格式为:

(type*)(p) ( 类型名 *) 指针表达式 (T(*)[c])p ( 类型 (*)[c]) ( 指针表达式 )

其中 type,T 可以是内置数据类型 , 可以是结构名 , 类类型

名 ,联合名, c 是静定的整数。 指针强制类型转换的作用是将指针表达式的类型转换为左边圆括号界定的指针类型。

Page 23: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

23

指针的类型转换是复杂的。概括地说遵循下面的规则:

a. 指针的类型转换就是把源指针的值复制给目标指针,即表达式 (type*)(p) 或 (T(*)[c])p 与 p 具有相同的地址值 ,

不同的类型属性。 b.目标指针维持自身的性质。例如:如果 p 是 T* 型的一级指针,则 *p 是 T 型的间接变量 , 则 *((type*)(p)) 是 type 型的左值。 p 的步长增量为 sizeof(T) , (type*)p 的步长增量为sizeof (type) ;而 *(T(*)[c])p 是 T* 型的右值, (T(*)[c])p 的步长增量为 c*sizeof(T) 。 c. 整型数据和浮点数据内存位的解释不同,两种指针之间不宜类型转换。

Page 24: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

24

d. 指针的类型转换时注意内存的空间映像,保证目标指针在合适的存储空间移动。转换的结果一般作为右值,除非将 T* 型的左值指针 p 转换为自身类型,如 [(T*)p+=n;] 。 [ 例 ] char* p=(char*)a 表达式将 long* 型地址映射给

char* 型指针 #include <stdio.h> void main() { long a[2]= {0x61626364,0x65666768}; char* p=(char*)a;

for ( int k=0; k<8; k++,p++) printf ("%c-%x ",*p,*p);

} // 输出: d-64 c-63 b-62 a-61 h-68 g-67 f-66 e-65

Page 25: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

25

根据多维数组在内存空间连续存放的性质 , 可将多维数组名映射到一级指针。[ 例 ] 一级指针遍历三维数组 #include <stdio.h> void main() { const int L=2,M=3,N=2; int s [L][M][N]={1,2,3,4,5,6,7,8,9,10,11,12}; int b [L*M*N]; int *p=(int*)s; int *q=b; int k ; for (k=0; k<L*M*N;k++) *q++=*p++; q-=L*M*N; for (k=0;k<L*M*N; k++,q++) printf ("%d,%d*",*q, b [k]); } // 输出结果:1,1*2,2*3,3*4,4*5,5*6,6*7,7*8,8*9,9*10,10*11,11*12,12*

Page 26: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

26

[ 例 ] 一维内存空间张成多维数组#include <stdio.h>void main(){ const int L=2,M=3,N=2; int i=0,j=0,k=0; int b [L*M*N]= {1,2,3,4,5,6,7,8,9,10,11,12}; int (*s) [M][N]= (int (*) [M][N])b; for (i=0; i<L; i++) for(j=0; j<M; j++) for (k=0; k<N; k++) printf ("%d ",s [i][j][k]); int (*d)[L*N] = (int (*)[4])b; for (j=0; j<M; j++) for (k=0; k<L*N; k++) printf ("%d ",d[j][k]);} // 输出结果: 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12

Page 27: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

27

一维数组与多维数组共同点是相邻元素之间的地址根据线性规律增长。 因此可以将一维数组空间委托数组指针接管,这一性质在 new int[N][M] 运算符或 malloc(N)等的函数调用中隐含

地得到利用。 对于一片连续的内存区域,可以通过一级指针索引,也可以通过指向多维数组的指针访问。 即可以将一维数组强行张成多维数组,也可以将多维数组映射为一维数组。

Page 28: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

28

2. 指针的类型匹配关系 对于数组定义 {int d[r][c]; } ,二维数组名 d 代表二维数组首元素的地址,其类型属性为 int (*) [c ] ,该地址可以初始化一个二级指针,但须进行强制类型转换。如:

int **pp=(int**) d;

强制映射之后 pp[i] 的值是不确定的。原因在于:d[i] 是 int* 型的右值地址, d+i非常特殊地等于 d[i] 的值,为(char*)d+i*c*sizeof(int) ; pp[i] 是 int* 型的左值指针, pp+I

不等于 pp[i] 的值。 pp+i 的值自动计算为 (char*)pp+i*sizeof(int*) ,而指针pp[i] 的值通过赋值得到或关联一个指针数组名间接获得。因此不将其它类型指针转换为二级指针。

Page 29: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

29

正确的操作次序二级指针指向一级指针,一级指针指向变量。 int* 型的一级指针 p 访问 int 的数组, int** 型的二级指

针pp 访问 int* 的指针数组等。例如 : 一维数组 a: int a[n]; 指针数组 pa: int * pa[r]; 二维数组 d: int d[r][c]; 一级指针 p: int *p= a; 二级指针 pp: int **pp= pa; 数组指针 q: int (* q)[c]=d;

只要被关联数组元素事先适当赋值,在 k 不越界的前提下 pp[k] 或 p[k] 的操作是有根据的。指针数组 pa 和数组指

针 q的初始化是不同的,指针数组 pa 须要对每个元素赋值,而数组指针 q 只需赋值一次。 pa[k] 是左值,而 q[k] 是右值。

Page 30: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

30

[ 例 ] 指针的匹配和转换 #include<stdio.h>

void main(void)

{ int i, b[3][2]= {1,2,3,4,5,6};

int (*q)[3]= (int(*)[3])b;

for( i=0; i<2; i++)

printf ("[%d,%d,%d]\t", q[i][0] , q[i][1], q[i][2]);

int* pa[ ]= {b[2],b[1],b[0]};

int** pp=pa;

for (i=0; i<3; i++)

printf ("[%d,%d]\t",pp[i][0] ,pp[i][1]);

} // 输出 [1,2,3] [4,5,6] [5,6] [3,4] [1,2]

Page 31: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

31

十三、下标表达式与访问指针寻址计算下标表达式的一般格式为: ep[en] 指针表达式 [ 整型表达式 ]

下标表达式 ep[en] 等价于 (*(ep+en)) ,反过来 (*(ep+en))

等价于 ep[en] 。 一般地 ep 为指针表达式, en 为整型表达式, ep 可以是另一个下标表达式。在多级下标表达式嵌套中必须有且仅允许其中一个表达式是用于定位地址的指针表达式。 &ep[en]等价于 &(*(ep+en))等价于 ep+en ,而 ep+en

的内存地址值由关系式给定: _ep+en*step

Page 32: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

32

其中 _ep 是 ep 的地址值, step 是指针 ep 的步长增量,如果 ep 是 double* 型的指针 , 则 step=sizeof(double)=8

如果 ep 是 char** 型的指针表达式,则 step=sizeof(char*)

如果 ep 是 long(*)[5] 型的指针,则 step=sizeof(long[5])=4*5=20

如果 ep 是 double(*)[5][2] 型的指针,则 step=sizeof(double[5][2])=8*5*2=80

依此类推。

Page 33: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

33

下标表达式 ep[en] 具有优美的可读性,运行效率当 en不

等于 0 时等价于访问指针形式 (*(ep+en)) ,优先采用下标表达

式代替访问指针形式。 实际上在专业的程序设计中几乎看不到访问指针形式(*(ep+en)) 的踪影,原因一是访问指针形式可读性欠佳类型属性模糊,二是字符键入较难。 ep[0] 等价于 (*ep) 但表达式 ep 不等价于 *ep 。

Page 34: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

34

对于数组定义 { type a[max1], d[max1][ max2],

s[max1][max2][ max3];}等,其中 type 表示各种数据类型。令 n=sizeof(type) ,一般地,下标表达式的地址映射按照下面的规律进行: 对 1 维数组 a[max1], 数组元素 a[k] 的地址值是: _a+k* n

对 2 维数组 d[max1][ max2], 数组元素 d[j][k] 的地址是: _d+j* max2*n +k*n

Page 35: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

35

对 3 维数组 s[max1][max2][ max3], s[i][j][k] 的地址是: _s+(i* max2* max3+j* max3 +k)* n 对 m 维数组 w[max1][max2]...[maxm], 数组元素 w[s1][s2]...[sm] 的地址索引值为: _w+[(s1*max2*max3...maxm )+ (s2*max3...maxm) ... +sm]*n 其中 _a,_d,_s,_w 分别是 a,d,s,w 的地址值,具体地以字节为单位, d[j] 和 d+j 的地址值都是 (char*)d+j*max2*n , s

[i]和 s+i 的地址值都是 (char*)s+i* max2*max3*n , s[i][j] 的地址值是: (char*)s+(i* max2 +j)*max3*n依此类推,它们都与 max1 无关。

Page 36: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

36

d[ j ],d+j,&d[ j ][ k ],s[ i ][ j ],s[ I ] 和 s+i等是根据上面的规则生成的右值地址,编译器用于寻址计算,它们不是左值,因而未分配内存空间。 同样的一个地址值可以具有不同的类型属性,可将右值地址赋给同类型的左值指针,以优化相同的地址表达式的重复的寻址计算。 设m,r,c 是预先静定的正数,对于下面的二维数组 d 或

指向二维数组的指针 d 的定义: int d[r][c]; int (*d)[c];

Page 37: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

37

则 d+i 是一个 int(*)[c] 类型的地址, d[i]+j 具有类型属性

int*, 它们构成右值表达式。 d[ i ][ j ] 是 int 型的变量。 d+i和

d[ i ] 具有相同的地址值不同的类型属性。 二维数组名 d 具有 int(*)[c] 的地址属性,同时拥有数组的大小信息,其类型属性抽象为 int[r][c] ,这一性质主要用在 sizeof (d) ,可以确定数组占有的内存大小为sizeof (int[r][c]) ,而左值指针名仅占有 sizeof (int*) 字节

的内存。这是数组名和同类型指针名的差异所在。

Page 38: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

38

对于三维数组 s 或指向三维数组的指针 s 的定义: int s[m][r][c]; int (*s)[r][c];

s+i 具有类型属性 int (*)[r][c], s[i]+j 具有类型属性int (*)[c], s[i][j]+k 具有类型属性 int* ; 它们构成右值表达式。 s[i][j][k] 为 int 型的变量。其中i,j,k 是整型表达式。 s[i] 地位相当于二维数组名 d , d[i] 和 s[i][j] 的地位相

当于一维数组名 a 。 右值数组名 s 可以确定数组占有的内存大小为sizeof (int[m][r][c]) ,而左值指针 s占有 sizeof (int*) 字节

的内存。

Page 39: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

39

具有 n 个下标的表达式指的是多维数组,一个多维数组是一个其元素是数组的数组。访问指针运算符作用于一个 n维数组类型上产生一个 n-1 维数组。 如果 n 大于 1 , n-1 维数组存放的是右值地址,如果 n 是 1则产生一个变量或数组元素。

访问指针运算和下表运算作用于地址表达式,进行的是降维处理。 p 为 int** 型的地址则 (*(p+i)) 或 p[i] 为 int* 型的左值指针,p 为 int* 型的地址则 (*(p+i)) 或 p[i] 为 int 型的变量。 p 为 int(*)[c] 型的地址则 (*(p+i)) 或 p[i] 为 int* 型的右值地址。 p 为 int(*)[r][c] 型的地址,则 (*(p+i)) 或 p[i] 为 int(*)[c] 型的右值地址。依此类推。

Page 40: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

40

取地址运算符 & 的操作数 v 一般为左值,记为&v 。 表达式 &v 的意义为取左值变量对应的存储地址,其结果为一右值。 左值 v 为 int 型的变量, &v 的结果为 int* 型的

地址。 左值 v 为 int* 类型的指针, &v 的结果为 int**

类型的地址。

Page 41: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

41

二维数组 d[i][j] 的行地址 d[i] 是右值, d[i] 之前可放置取

地址运算符构成 &d [ i ] , d[ i ] 等价于 *(d+i), &d [ i ] 运算为

& *(d+i),最终为 d+i 。 对于数组 int a[c];&a 的结果是一个 int(*)[c] 地址。 对于指针 int *a;&a 的结果是一个 int** 的地址。 对于数组 int d[r][c],&d 的结果是一个 int(*)[r][c] 的地址,对于数组指针 int (*d)[c],&d 的结果是一个 int(**)[c] 的

地址。 类似地可以推广到多维数组的情形。取地址运算进行升维处理,在运算中把源操作数变为右值。

Page 42: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

42

函数名可以跟在取地址运算符 & 之后表示取函数名代表的代码段入口地址,函数名本身是右值表达式,无需取地址运算符 & 可以直接得到函数的入口地址。

数组占有一片内存,一维数组和多维数组的元素是递增有序的。指针仅在指向数组空间时才进行寻址遍历 ( 即加减或自增自减等 ) 访问计算。寻址遍历时注意遵循下面几点: 1. double* 型的一级指针在 double 型的数组空间上寻址遍历 ; 2. char** 型的二级指针在 char* 型的数组空间上寻址遍

历 ; 3. long (*)[c] 型的指针匹配相应二维数组 long d[ ][c]的首行地址 d+k 4. int (*)[r][c] 型的指针匹配相应三维数组 int s[][r][c] 的首页地址 s+k

Page 43: 十一、程序动态存储结构 十二、指针的类型转换和匹配关系 十三、下标表达式与访问指针寻址计算

43