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

Slides:



Advertisements
Similar presentations
2.8 函数的微分 1 微分的定义 2 微分的几何意义 3 微分公式与微分运算法则 4 微分在近似计算中的应用.
Advertisements

阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
第九章 指针 西安工程大学.
第七章 指针 计算机公共教学部.
第六章 指针 指针的概念 指针变量 指针与数组 指针与函数 返回指针值的函数.
§1 二阶与三阶行列式 ★二元线性方程组与二阶行列式 ★三阶行列式
二级指针与二维数组.
C语言程序设计基础 第10章 指针进阶 刘新国.
10.1 二级指针 10.2 指针与二维数组 10.3 指针的动态存储分配 10.4 函数指针 10.5 main函数的参数
第6章 指针 6.1 指针的概念 6.2 变量与指针 6.3 数组与指针 6.4 字符串与指针 6.5 函数与指针 6.6 返回指针值的函数
6.4 字符串与指针 1. 用字符数组存放一个字符串.
第六节 二维数组和指针 二维数组的地址 对于一维数组: (1)数组名array表示数组的首地址, 即array[0]的地址;
8.1 指针的概念 8.2 指针变量 8.3 指针变量的基础类型 8.4 指针的运算 8.5 指针与一维数组 8.6 指针应用实例
C++中的声音处理 在传统Turbo C环境中,如果想用C语言控制电脑发声,可以用Sound函数。在VC6.6环境中如果想控制电脑发声则采用Beep函数。原型为: Beep(频率,持续时间) , 单位毫秒 暂停程序执行使用Sleep函数 Sleep(持续时间), 单位毫秒 引用这两个函数时,必须包含头文件
第6章 指针 学习目的与要求: 了解指针的概念和相关术语 熟练掌握指向变量、数组和字符串的指针变量的使用方法 了解指向函数的指针变量
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第 十 章 指 针.
走进编程 程序的顺序结构(二).
元素替换法 ——行列式按行(列)展开(推论)
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
Zhao4zhong1 (赵中) C语言指针与汇编语言地址.
二维数组的指针表示 与复杂的指针例子 专题研讨课之三.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第二章 Java语言基础.
第六章 指针 指针的概念 指针的运算 指向变量的指针 指向数组的指针 指向函数的指针 二级指针 主讲:李祥 时间:2015年10月.
欲穷千里,更上层楼 第十章 指 针 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环,
第五章 习题课 电子信息与计算机科学系 曾庆尚.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
第七章 操作符重载 胡昊 南京大学计算机系软件所.
C++大学基础教程 第6章 指针和引用 北京科技大学 信息基础科学系.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C语言程序设计 主讲教师:陆幼利.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
$9 泛型基础.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
线 性 代 数 厦门大学线性代数教学组 2019年4月24日6时8分 / 45.
C语言大学实用教程 第7章 指针 西南财经大学经济信息工程学院 刘家芬
第二章 Java基本语法 讲师:复凡.
指针 几个概念:  指针也是一种数据类型,具有指针类型的变量,称为指针变量。
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第6讲 指针与引用 6.1 指针 6.2 引用.
<编程达人入门课程> 本节内容 内存的使用 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群: ,
C语言程序设计 第一章 数据类型, 运算符与表达式 第二章 顺序程序设计 第三章 选择结构程序设计 第四章 循环控制 第五章 数组.
第4章 Excel电子表格制作软件 4.4 函数(一).
第九节 赋值运算符和赋值表达式.
§6.7 子空间的直和 一、直和的定义 二、直和的判定 三、多个子空间的直和.
第7章 程序的结构 四、生存期与存储属性 五、extern关键字与外部连接属性 六、static关键字与内部连接属性.
3.16 枚举算法及其程序实现 ——数组的作用.
本节内容 结构体 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第6章 指针与数组 七、指向数组的指针 八、二级指针 九、指针数组 十、void 关键字与void *型的指针.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
C程序设计 实验二 数据类型、运算符和表达式 第6讲
第15讲 特征值与特征向量的性质 主要内容:特征值与特征向量的性质.
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
本节内容 结构体.
程序设计基础A(C语言) 第一章 C语言概述 主讲教师: 许 康
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C语言程序设计 第8章 指针.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
基本知识 数据类型、变量、常量、运算符.
第八章 指 针 北京邮电大学出版社.
本节内容 进程 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
§2 自由代数 定义19.7:设X是集合,G是一个T-代数,为X到G的函数,若对每个T-代数A和X到A的函数,都存在唯一的G到A的同态映射,使得=,则称G(更严格的说是(G,))是生成集X上的自由T-代数。X中的元素称为生成元。 A变, 变 变, 也变 对给定的 和A,是唯一的.
第7章 地址和指针 7.1 地址和指针的概念 7.2 指针变量的定义和指针变量的基类型 7.3 给指针变量赋值 7.4 对指针变量的操作
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

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

十一、程序动态存储结构 系统将内存数据分为三个段即数据段、代码段和堆栈 段。 编译程序对源程序进行编译时,将函数中的执行语句部 分编译成指令代码放置在代码区,将静态变量、全局变量与 字符串常数存放在全局数据区,函数的形式参数放置在临时 开辟的称之为先进后出的堆栈区,堆栈区内尚包含函数定义 部分中内部引入的局部变量或局部数组。 另外系统提供一些函数进行内存的动态控制,这一片可 以由用户控制的动态区域称为堆结构或堆区,这一可由用户 动态控制的区域实际上是隶属于数据段的。 剩下的存储单元为自由内存空间。

1. C++中new运算符和delete运算符 的。 new运算符试图动态地根据其后的类型名分配堆内存空 间,也即在堆空间定义变量或数组。 new运算符不能用于分配一个函数,但可以用于分配一 个函数指针。 new运算符定义堆中变量或对象的格式为: new type(initialList) new 类名(初始化表) 表达式的结果是type*类型的地址。 下面的几个语句: int x; int *p=&x; *p=5;

对变量x进行了间接赋值,上面的指针变量p也可以指 向堆空间: p=new int(5); p=new int; *p=5; 值得注意变量x的地址在变量x的生存期是不变的, new运算符分配的地址在其生存期也具有类似的性质,不同 的是该存储空间可以由用户通过delete运算符释放。 new运算符构成的表达式建立或定义动态一维数组的语 法格式为: new type [dynSize] new 类名[动态数组大小] 表达式的结果是type*类型的地址。

系统在堆区中开辟了一块大小为dynSize *sizeof (type) 的空间,表达式的结果就指向这块存储空间的开始位置,因 此这一地址具有数组名一样的性质。 当new运算符建立动态数组时,不同时指定数组元素的 初值。 这个语法格式适用于一维数组的动态确定,动态数组大 小dynSize初始化的位置是灵活的,可以在运行时实时地输 入,但必须在new函数的调用点之前确定。

当不能成功地分配所需要的内存时,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];

new运算符表达式分配或定义动态二维数组的格式为: new double [ dynSize ][ maxn ] // new 类名[动态数组大小][ 固定数组大小] 表达式的结果为double (*)[maxn]型的地址,因此可以用如 下语句进行初始化: double (*p)[ maxn ]= new double[ dynSize ][ maxn ]; 也可以先定义数组指针然后再调用new运算符: double (*p)[ maxn ]; ;... ; p = new double[ dynSize ][ maxn ];

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

delete运算符释放由new运算符创建的对象内存。 delete运算符表达式的结果为void类型的函数返回,因 此仅单独调用。 delete的操作数必须是由new运算符返回的地址。 delete运算符表达式的语法格式为: delete 指针表达式; delete pointer; 或: delete [ ]指针表达式; delete [ ] pointer;

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

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

例如: 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函数来清除。

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函数则要求提供类型转 换,以便将泛泛的一块内存空间用于具体类型的数据运算。

[例] 动态申请一个一维数组 #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遍历访问堆空间

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运算符 收回相应的内存空间。

[例] 申请一个二维数组,第一个下标是可变的 #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));

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);

定义语句{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函数收回相应的内存空间二维动态数组由一维动态指针 数组构成,该指针数组的每一个元素分别指向一维动态数 组。 二维动态可调数组是高频采用的编程技术。

下面的程序建立二级指针,该二级指针与二维动态数组 相联系,指向堆空间。 [例] 动态的二维数组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;

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; { cout<<endl; for (j=0; j<N; j++) cout<<" pp["<<k<<"]["<<j<<"]="<<pp [ k ][ j ]

if (pp[ j ]!=NULL) free (pp[ j ]); if (pp!=NULL) free (pp); } for (j=0; j<M; j++) if (pp[ j ]!=NULL) free (pp[ j ]); if (pp!=NULL) free (pp); } //运行程序输出结果为 input number M:2 input number N:6 pp[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

十二、指针的类型转换和匹配关系 1.指针类型转换 转换为一级指针和指向二维数组指针的强制类型转换的 语法格式为: (type*)(p) (类型名*)指针表达式 (T(*)[c])p (类型(*)[c]) (指针表达式) 其中type,T可以是内置数据类型,可以是结构名,类类型 名,联合名,c是静定的整数。 指针强制类型转换的作用是将指针表达式的类型转换 为左边圆括号界定的指针类型。

指针的类型转换是复杂的。概括地说遵循下面的规则: 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.整型数据和浮点数据内存位的解释不同,两种指针之 间不宜类型转换。

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

根据多维数组在内存空间连续存放的性质,可将多维数 组名映射到一级指针。 [例]一级指针遍历三维数组 #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*

[例]一维内存空间张成多维数组 #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

一维数组与多维数组共同点是相邻元素之间的地址根据 线性规律增长。 因此可以将一维数组空间委托数组指针接管,这一性质 在new int[N][M]运算符或malloc(N)等的函数调用中隐含地 得到利用。 对于一片连续的内存区域,可以通过一级指针索引,也 可以通过指向多维数组的指针访问。 即可以将一维数组强行张成多维数组,也可以将多维数 组映射为一维数组。

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]的值通过赋值得到或关联一个指针数组名间接获得。因 此不将其它类型指针转换为二级指针。

正确的操作次序二级指针指向一级指针,一级指针指向 变量。 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]是右值。

[例]指针的匹配和转换 #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]

十三、下标表达式与访问指针寻址计算 下标表达式的一般格式为: ep[en] 指针表达式[整型表达式] 下标表达式ep[en] 等价于(*(ep+en)),反过来(*(ep+en)) 等价于ep[en]。 一般地ep为指针表达式,en为整型表达式,ep可以是 另一个下标表达式。在多级下标表达式嵌套中必须有且仅允 许其中一个表达式是用于定位地址的指针表达式。 &ep[en]等价于&(*(ep+en))等价于ep+en,而ep+en 的内存地址值由关系式给定: _ep+en*step

其中_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 依此类推。

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

对于数组定义{ 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

对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无关。

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];

则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*)字节的 内存。这是数组名和同类型指针名的差异所在。

对于三维数组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*)字节的 内存。

具有n个下标的表达式指的是多维数组,一个多维数组 如果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]型 的右值地址。依此类推。

取地址运算符& 的操作数v一般为左值,记为 址,其结果为一右值。 左值v为int型的变量,&v的结果为int*型的地 址。 左值v为int*类型的指针,&v的结果为int**类型 的地址。

二维数组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]的地 址。 类似地可以推广到多维数组的情形。取地址运算进行升 维处理,在运算中把源操作数变为右值。

函数名可以跟在取地址运算符&之后表示取函数名代表 的代码段入口地址,函数名本身是右值表达式,无需取地址 运算符&可以直接得到函数的入口地址。 数组占有一片内存,一维数组和多维数组的元素是递增 有序的。指针仅在指向数组空间时才进行寻址遍历(即加减 或自增自减等)访问计算。寻址遍历时注意遵循下面几点: 1. double*型的一级指针在double型的数组空间上寻 址遍历; 2. char**型的二级指针在char*型的数组空间上寻址遍 历; 3. long (*)[c]型的指针匹配相应二维数组long d[ ][c] 的首行地址d+k 4. int (*)[r][c]型的指针匹配相应三维数组int s[][r][c] 的首页地址s+k

请打开“第7章.ppt”