C语言程序设计课件第5章_第1页
C语言程序设计课件第5章_第2页
C语言程序设计课件第5章_第3页
C语言程序设计课件第5章_第4页
C语言程序设计课件第5章_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

级高语言设计程2009.8朱立华俞琼王立柱1序章节自测及在线编程练习:http://主讲人:朱立华2009.8朱立华俞琼王立柱2第五章一级指针与一维数组2009.8朱立华俞琼王立柱32009.8朱立华俞琼王立柱4内容提要一维数组专用于一批类型相同的变量的处理指针类型与指针的基类型直接引用与间接引用的不同方式及实质指针是地址的类型,是对地址进行的一种类型抽象,用来实现间接访问,掌握以下指针的知识内容提要指针的基本操作指针与一维数组的关系一维数组的定义及初始化,一维数组的基类型访问一维数组元素的方式——间接引用方式一维数组中的常见操作及算法:输入、输出、逆置、查找、插入、删除、排序利用指针进行间接访问指针与变量的关系指针与一维数组的关系动态演示动态演示2009.8朱立华俞琼王立柱5重点难点提示重点难点提示常用算法检索检索检索动态演示动态演示动态演示间接引用的必要性内容回顾:intm;表示定义一个变量,m是变量名,即变量空间名,直接用m访问的是变量空间中的内容,称为直接引用一个算法问题:输入10000个整数并求平均值,并且保留这些原始数据到最后统一输出解决方案:将这10000个整数看作逻辑上前后相邻的并且可以存储在内存中一片连续的区域,只要知道这些数在内存中的起始地址,就能找到对应的空间,进而访问空间中的内容,这就是间接引用方式,因此:要有变量可以存放地址--指针;要有类型可以定义一个标识符代表一组连续空间的起始地址--一维数组2009.8朱立华俞琼王立柱6用第4章的循环可以求解,定义1个变量,在循环体中读入值并求和用1个变量不能达到这一要求,根据前面的知识,似乎应该定义10000个变量,可是这样又无法用循环控制间接引用方式下,传递某一个数据地址(一般是第1个数据的地址)相当于传递了全部数据

一级指针类型(引言)

C语言优越于其他高级语言的一个特性就是:可对硬件编程;通过第一章机器语言程序设计的学习,在计算机内部访问操作数首先是读取其在内存中的存储地址值,然后通过该地址值去访问该地址所在空间中的内容C语言为了实现对硬件编程,必定要将内存地址抽象为一种类型,这就是(一级)指针类型,即指针类型是地址的类型,该类型的常量和变量值都是内存地址值。地址值是无符号整数,无论该地址的内存中数据是什么类型,地址值都占4个字节显然,知道地址值是为了操作这个地址中所存储的内容。通过地址来访问数据空间的方式称为间接引用通过该地址去间接引用的数据类型称为地址的基类型前面学习的通过存储空间的名称引用数据的方式称为直接引用2009.8朱立华俞琼王立柱7这两种类型有区别也有联系一级指针类型(概述)直接引用与间接引用:在C语言源程序中的直接引用方式经编译后在计算机内部实质上是间接引用,例如源程序中有下列代码:intn,m; //定义2个整型变量,n和m是变量名doublex; //定义1个双浮点型变量,x是变量名表n=5; //直接引用,给n赋值5m=n; //直接引用,将n中的值赋给mx=3.1415; //直接引用,给x赋值3.1415

经编译后,变量名消失,留下其地址及类型信息,如下表:变量名变量值地址类型x3.14150x12ff70doublem50x12ff78intn50x12ff7cint2009.8朱立华俞琼王立柱8源程序中目标程序中地址的基类型到哪里访问数据指示编译器怎样操作特定地址上的内存区域:该区域包含多少连续的字节,数据存储的格式,以及可以实施哪些基本操作

一级指针类型(概述)C语言中,用一级指针类型作为地址的类型。地址关联着两个存储空间,存放着两种不同的值,彼此又有紧密的联系,以上页表中地址0x12ff78为例如下:(int*)0x12ff78m0x12ff7852009.8朱立华俞琼王立柱9整型变量m的空间,此为变量空间编译后指向m的地址值,是指针常量值因此,一级指针类型是复合类型,由指针的基类型名称加”*”共同组成,例:int*----->整型指针类型,即指针所指向的空间存int值double*----->双浮点型指针类型指针字面值常量,即指针常量空间的名称一级指针类型(用*间接引用)利用指针去访问它指向的基类型的空间分两步:(1)通过指针字面值常量取出基类型空间的地址,这是直接引用

(2)加间接引用运算符“*”,得到与基类型空间名称等价的表达式,称为间接引用表达式,这是间接引用(int*)0x12ff78m0x12ff7852009.8朱立华俞琼王立柱10因此,*(int*)0x12ff78与m完全等价,m=5;也可以写成:*(int*)0x12ff78=5;上机在VC++下演示程序5.1总结:间接引用运算符*使得指针可以访问其基类型空间动态演示过程一级指针类型(基本操作)指针的基本操作——加减一个整数(1)指针加一个整数i,是将当前地址值加上i*sizeof(基类型)(2)指针减一个整数i,是将当前地址值减去i*sizeof(基类型)指针字面值常量地址值基类型值(int*)0x12ff000x12ff005(int*)0x12ff040x12ff046(int*)0x12ff080x12ff087(int*)0x12ff0c0x12ff0c8(int*)0x12ff100x12ff1092009.8朱立华俞琼王立柱11总结:理论上,指针可以加减任何整数,但必须保证结果指针指向应用程序的数据空间,否则不能间接访问(int*)0x12ff00+2=(int*)0x12ff08

(int*)0x12ff10-3=(int*)0x12ff04

sizeof(int)=4(int*)0x12ff10+2=(int*)0x12ff18,地址0x12ff18所指向的空间不是本程序的数据空间,无意义!动态演示过程一级指针类型(基本操作)指针的其它基本操作:(1)指针可以进行逻辑运算,例:!(int*)0x12ff00的结果为0(2)指针可以进行关系运算,例:(int*)0x12ff00<(int*)0x12ff08的结果为1(真)(3)两个类型相同的指针(即基类型相同的指针)可以相减,地址大的减地址小的,结果为两个地址间区域所含基类型数据个数。指针字面值常量地址值基类型值(int*)0x12ff000x12ff005(int*)0x12ff040x12ff046(int*)0x12ff080x12ff087(int*)0x12ff0c0x12ff0c8(int*)0x12ff100x12ff1092009.8朱立华俞琼王立柱12(int*)0x12ff0c-(int*)0x12ff04=2

一维数组类型(概述)将物理上前后相邻、类型相同的一组变量作为一个整体引入C语言,这个整体称为(一维)数组类型的变量,简称(一维)数组,其中每一个变量称为数组元素,变量的个数称为数组长度或数组容量。引入数组的目的:利用间接引用方式,访问一组数据。具体地说,从第一个数组元素的地址,计算出其他所有数组元素的地址,然后通过数组元素的地址,间接访问数组元素。定义一个数组:需要:(1)指定数组名,这个名称代表着第一个数组元素的指针,是一个指针常量,等价于一个指针字面值常量,称为数组指针(2)指定数组元素的类型,它是数组指针的基类型,是间接引用方式的基础(3)指定数组长度,即数组包含的元素个数,它决定了数组指针在进行加减整数的算术运算时的有效范围2009.8朱立华俞琼王立柱13一维数组类型(数组定义)数组的定义格式为:

类型标识符一维数组名[整型常量表达式];例:inta[5];元素地址元素值元素名1元素名2a?*aa[0]a+1?*(a+1)a[1]a+2?*(a+2)a[2]a+3?*(a+3)a[3]a+4?*(a+4)a[4]2009.8朱立华俞琼王立柱14类型标识符是数组元素的类型(也称为数组的基类型)的标识是1个合法的用户自定义标识符,代表整个数组空间的起始地址(第1个元素的地址),是1个指针常量表示数组的长度,即数组元素的个数,必须用一个常量表达式而不能用变量间接引用表达式下标(索引)表达式注意:数组元素的下标从0开始,可以是整型常量或变量,需要保证范围在[0~整型常量表达式-1],否则越界一维数组类型(数组定义)表5.2数组元素及元素地址的表示(数组定义:inta[5];)表示意义等价表示a数组第一个元素的地址,是指针常量&a[0]a+i(0≤i≤4)第i+1个元素的地址,是指针常量表达式,不可被修改&a[i]&a[i](0≤i≤4)a+i*(a+i)(0≤i≤4)第i+1个元素的间接引用表达,是基类型的变量a[i]a[i](0≤i≤4)第i+1个元素的下标(索引)表达,是基类型的变量*(a+i)总结:一维数组元素的地址有两种等价表示:a+i==&a[i];一维数组元素有两种等价表示:*(a+i)==a[i]2009.8朱立华俞琼王立柱15一维数组类型(初始化)一维数组的初始化:在定义数组时为其全部或部分元素指定初值。一维数组的初始化的形式:类型标识符一维数组名[整型常量表达式]={常量1[,常量2,…,常量n]};正确的初始化示例:inta[5]={1,2,3,4,5};inta[]={1,2,3,4,5}; inta[5]={1,2*4}; 错误的初始化示例:

inta[5]={,2,3};inta[5]={1,2,3,4,5,6,7};

2009.8朱立华俞琼王立柱16所有元素都有初值,a[0]至a[4]的值依次为1,2,3,4,5相当于a[5]={1,2,3,4,5};当所有元素都有初值时,数组元素个数可缺省,自动等于初值个数相当于a[5]={1,8,0,0,0};可以只对数组的前几个元素赋初值,其余元素的初值自动为0。注意!当不做初始化时所有数组元素的值均为随机数而不是0对数组元素初始化时只能从左到右依次,只能缺省最右边的元素值初值个数不能超过数组元素的个数一维数组类型(赋值)一维数组定义(包括初始化)结束之后,只能对其元素进行访问,而不能对数组整体访问错误的数组赋值示例:

(1)inta[5];a[5]={1,2,3,4,5};(2)inta[5];a={1,2,3,4,5}; (3)inta[5]={1,2,3,4,5};intb[5]=a;

b=a;

正确做法可以是:

b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];最简洁通用的方法是:(设已有变量定义:inti;)for(i=0;i<5;i++)b[i]=a[i];2009.8朱立华俞琼王立柱17错误原因:a[5]是对某个数组元素的引用,而长度为5的数组没有a[5]这个元素,不能用一组值给一个元素赋值错误原因:数组名a在赋值语句中是指向第1个数组元素的指针常量&a[0],常量不是左值,不能被赋值。错误原因:一个数组不能给另一个数组初始化

错误原因:一个数组不能给另一个数组整体赋值

一维数组类型(举例)程序5.2

编程定义并初始化一维数组,显示一维整型数组所有元素的值及对应地址,并对数组的所有元素求和思路:(1)首先需要定义并初始化数组(假设数组名为a),根据题意,还需要定义循环控制变量和累加器变量(2)然后分别用一层循环输出所有的元素值、地址值(3)再用一层循环求所有元素之和,最后输出和值程序5.3从键盘上输入n(1≤n≤10)个整数,输出这些元素,再分别统计其中正数和负数的个数,并求出最大值与最小值思路:2009.8朱立华俞琼王立柱18元素值有两种等价表示:a[i]和*(a+i)元素地址有两种等价表示:&a[i]和a+i(1)首先需要定义数组以存储n个整数,再用一层循环输出元素值(2)然后分别用一层循环作统计、找最大最小值(3)最后输出结果

动态演示过程程序5.3:源程序代码

#include<stdio.h>intmain(){inta[10],i,n;

intmax,min;

intpositive=0,negative=0;do{ printf("Pleaseinputn(1<=n<=10):\n"); scanf("%d",&n);}while(n<1||n>10);printf("Pleaseinput%delements:\n",n);for(i=0;i<n;i++)scanf("%d",&a[i]);printf("Theelementsare:\n");for(i=0;i<n;i++)

printf("%5d",a[i]);printf("\n");2009.8朱立华俞琼王立柱19保证读入的n在1至10之间读入数组的前n个元素输出数组的元素程序5.3:源程序代码

for(i=0;i<n;i++) if(a[i]>0)positive++;

elseif(a[i]<0)negative++;max=min=a[0];for(i=1;i<n;i++){

if(*(a+i)>max)max=*(a+i);

elseif(*(a+i)<min)min=*(a+i);}printf("positive=%d\n",positive);printf("negative=%d\n",negative);printf("maxelement=%d\n",max);printf("minelement=%d\n",min);return0;}2009.8朱立华俞琼王立柱20统计正数个数统计负数个数寻找最大元素寻找最小元素输出结果动态演示过程?思考:为什么这里的if不能省略??思考:为什么这里的if不能省略?(选讲)一维数组名的双重含义回顾:intm;--->m是一个整型变量的变量名,代表整型变量空间,m的类型标识为int同理:inta[5];--->a是一个数组变量的变量名,代表整个数组空间,a的类型标识为int[5]因此,数组名a有双重含义:(1)是一个数组变量的变量名,代表整个数组空间,在&a,sizeof(a)中体现该含义,但是数组变量不能通过其名称直接引用数组元素,因此数组名称“退化”了(2)是指向第一个数组元素的指针常量,所有元素的地址可根据该常量的地址值计算求得,从而方便实现间接访问数组的元素程序5.4

设计程序验证d与&d的区别,重点关注d+1与&d+1的不同2009.8朱立华俞琼王立柱21动态演示过程(选讲)一维数组名的双重含义2009.8朱立华俞琼王立柱22名称意义类型基类型运算或关系数组元素d[0]~d[4]

类型相同的变量double无实型变量运算、取址运算&

数组指针d指向第一个数组元素的指针常量double*doubled=&d[0],d+i=&d[i],*(d+i)=d[i](i=0~4)数组空间名称d数组变量(空间)的标识符double[5]无sizeof(d)=5*8=40,&d

数组空间取址&d整个数组空间的首地址double*[5]double[5]+、-、间接引用*&d与数组指针d的值虽然相等,但是d的类型是double*,基类型是数组元素类型double;而&d的类型是double(*)[5],基类型是double[5]。执行d+1,地址增加8,执行&d+1,地址增减40。数组名具体代表什么意思,要依赖上下文而定,如下表:一级指针变量与一维数组引入数组的目的:传递数组首元素地址,相当于传递整个数组空间所有元素的值。数组首元素地址可以传给?——基类型相同的指针变量指针变量的定义:回顾:整数类型int--->

整型变量intx;同理:基类型为整型的指针类型int*--->

指针变量int*x;一维数组名是指针常量,当然可以将值赋给基类型相同的指针变量,使指针变量和数组指针在间接引用方式下“共享”同一段数组空间,这相当于把数组空间的数据传给了指针变量,称为地址传递,例:inta[5];int*p=a;2009.8朱立华俞琼王立柱23a[0]a[1]a[2]a[3]a[4]13ff58app[0]p[1]p[2]p[3]p[4]13ff58一级指针变量与一维数组当p=a后,有下列等价关系存在:(1)数组元素的表示:

下标法:p[i]等价于a[i]

间接引用法:*(p+i)等价于*(a+i)(2)数组元素地址的表示:

指针表达式法:p+i等价于a+i

元素取地址法:&p[i]等价于&a[i]注意:(1)p是变量,因此它有存储空间可以取地址,即&p是指向p存储空间的指针常量,其基类型为int*

(2)p是变量,一般获得数组首元素地址值,也可以获得任意元素的地址值,例:p=&a[2]此时:p[0]等价于a[2],p[2]等价于a[4]程序5.5:用一级指针访问一维数组元素示例2009.8朱立华俞琼王立柱24a[0]a[1]a[2]a[3]a[4]13ff58app[0]p[1]p[2]p[3]p[4]13ff5813ff60p[0]p[1]p[2]动态演示过程一级指针变量与一维数组一级指针变量定义时*的位置:情况1:只定义一个指针变量,此时*位置可以近基类型名、近变量名或居中三者均可例:int*p; int*p; int*p; 情况2:一条定义语句既定义基类型的变量,又定义指针变量,此时,*近指针变量例:inta,*p,b; 情况3:一条定义语句定义多个同类型的指针变量时,每个指针变量前都跟一个*号例:int*p,*q; 简单总结:(1)“*”贴近指针变量总是正确的(2)每个指针变量前面都必须有一个“*”

2009.8朱立华俞琼王立柱25近变量名近基类型名居中必须近变量名一级指针变量与一维数组关于数组指针的基类型与指针变量基类型的一致性问题目的1:如果传递数组指针的目的是传递数组的值,那么应该将数组指针传递给同类型的指针变量,类型不同时虽然可通过强制类型转换赋值,但无意义例:inta[5]={10,15,20,25,30};double*pd;pd=(double*)a; float*pf;pf=(float*)a; 目的2:如果传递数组指针的目的在于高效使用内存资源,那么可以通过强制类型转换把该数组空间指针传递给其他类型的指针变量,把用过的数组空间用来存储其他类型的数据。程序5.6

同一段空间,可用作不同类型的数组空间示例2009.8朱立华俞琼王立柱26强制类型转换使赋值有效但无意义,因为pd+i≠a+i且pd[i]≠

a[i]

强制类型转换使赋值有效但无意义,因为虽然pf+i==a+i但pf[i]≠

a[i]

动态演示过程这样做虽然节省了空间,但是容易引起数据访问的歧义实际编程中这种方法不常用

一级指针变量与一维数组一个基类型的变量等价于长度为1的数组定义inta[4];intn=5;分析的对象a&n对象的性质长度为4的整型数组指针常量长度为1的整型数组指针常量访问第一个元素a[0]==*(a+0)&n[0]==*(&n+0)=*(&n)=n若有int*p;给p的赋值p=a;p=&n;用p间接访问元素p[0]==*(p+0)=*pp[0]==*(p+0)=*p,直接用*p结论:intn,*p;则n相当于长度为1的数组&n的唯一元素,赋值给p之后,存在两组等价关系:p==&n;*p==n;但&n为常量,p为变量2009.8朱立华俞琼王立柱27一级指针变量与一维数组取址运算&和间接引用运算*互为逆运算二者都是第2优先级的运算符,结合方向为从右向左。需要注意,运算的每一步都要保证有意义。例:intn,m=5,*p=&m;则有5组等价关系:

(1)p==&m;(2)*p=m;(3)&*p=p;(4)*&p=p(5)*&m=m但是:&*m=m;却是错误的,因为m不代表地址,所以*m无意义。回顾表5.1的内容:x=*(double*)0x12ff70

两边同时进行取址运算“&”,再消掉互逆运算符得到:

&x=&*(double*)0x12ff70=(double*)0x12ff70

最左和最右同时进行间接引用运算“*”,再消掉互逆运算符得到:*&x=*(double*)0x12ff70=x2009.8朱立华俞琼王立柱28一级指针变量与一维数组野指针:是指向”垃圾”内存的指针,有以下几种情况(1)指针变量没有被赋值,它存储的地址是不确定的例:int*p; *p=100; (2)指针操作超越了变量的作用范围例:inta[3],*p=a;p=p+3;*p=100; (3)一个字面值指针常量,例如(int*)0x12ff7c,如果其值0x12ff7c不是系统已经分配给用户程序使用的空间地址。因此一般编程时很少直接使用字面值指针常量进行间接引用(4)利用指针申请的动态空间被释放后,仍用该指针进行间接访问(具体见第6章6.5.3)2009.8朱立华俞琼王立柱29一个没有赋值的“野”指针用“野”指针间接引用很危险指针获得数组首元素地址指针增加3,出了数组范围间接引用的空间不是程序的有效数据空间一级指针变量与一维数组void型指针:称为通用指针或泛指针。可以把任何类型变量的地址赋给它,但是不能利用这样的指针做相应类型的访问或处理。例:void*p; intx=5;p=&x; //合法*p=6; //非法printf("%d",*p); //非法void指针主要用于单纯的内存数据的拷贝。在第11章的“流与文件”中,将遇到这样的指针。2009.8朱立华俞琼王立柱30一级指针变量与一维数组回顾:当一级指针变量的基类型与一维数组的基类型一致时,可以将一维数组名赋值给指针变量,这时对数组元素的访问既可以用一维数组也可以用指针变量。用一维数组访问数组的元素只能通过改变下标的方式用一级指针变量间接访问一维数组的元素可以通过移动下标(指针不动)或移动指针(指针改变)两种方式程序5.7分别用移动下标的方法和移动指针的方法实现输出数组的所有元素。在方法1中,pa[i++]实际上先访问pa[i],再执行i=i+1,当循环结束时,指针pa还是等于a即&a[0],pa[i++]也可写成a[i++]在方法2中,*pa++相当于*(pa++),这里是后++,因此相当于先访问*pa,再执行pa=pa+1,当循环结束时,pa等于a+5。由于a是常量,故*pa++不能写成*a++2009.8朱立华俞琼王立柱31动态演示过程一级指针变量与一维数组移动下标与移动指针访问数组的元素例示:程序5.8调用随机函数产生10个1至100内的整数作为数组的元素,然后再逆置存放,输出初始序列和逆置以后的序列。问题分析:本题中首先需要定义一个含10个元素的整型数组,数组的元素值需要在一个循环体中通过调用随机函数逐个赋值,然后用循环输出所有元素。接下来是数组的逆置,最简单的方法是从两边向中间对应位置的元素两两互换。方法一:采用移动下标法,用i和j分别指示待交换的两个元素的下标,i从0开始递增,j从9开始递减,循环条件为i<j。方法二:采用移动指针法,设两个指针p和q分别指向第一个元素和最后一个元素,交换对应的*p和*q,然后p++同时q--,循环条件为p<q。2009.8朱立华俞琼王立柱32动态演示过程方法一动态演示过程方法二一级指针变量与一维数组程序5.8方法一核心代码for(i=0,j=9;i<j;i++,j--){ temp=a[i]; a[i]=a[j]; a[j]=temp;}等效于:(只用i控制下标)for(i=0;i<10/2;i++){ temp=a[i]; a[i]=a[10-1-i]; a[10-1-i]=temp;}

2009.8朱立华俞琼王立柱33程序5.8方法二核心代码for(p=a,q=a+9;p<q;p++,q--){ temp=*p; *p=*q; *q=temp;} 等效于:(只用p控制指针变化)for(p=a;p<a+10/2;p++){ temp=*p; *p=*(a+10-1-(p-a)); *(a+10-1-(p-a))=temp;}

一维数组应用举例(查找)应用之一——

在数组中查找一个元素查找算法很多,这里只介绍最简单的顺序查找法.数组元素的初始序列值无任何要求,即不要求有序。查找的过程:只能是从第一个元素开始依次与待查找的元素进行比较,如果相等就查找成功,输出元素及对应下标;如果与所有元素都比较结束仍没有相等元素,则输出元素不存在的提示信息。程序5.9从键盘上输入n(1≤n≤10)个整数作为数组a的元素值,再读入一个待查找的整数x,在a数组中查找x,如果存在输出它的下标,否则提示:“Notpresent!”

提示:本程序完整的循环条件:i<n&&x!=*(p+i)

2009.8朱立华俞琼王立柱34动态演示过程一维数组应用举例(插入)应用之二

——

向数组中插入一个元素

插入算法有很多种,本程序是要求在有序数组中插入一个数据元素,并保持数组有序。具体步骤是:(1)确定待插入位置(此步骤伴随着查找过程);(2)元素后移,腾出相应位置(用递减循环实现,后移就是作形如a[j+1]=a[j];的赋值)(3)在“空”位置上插入新元素(作一次赋值)程序5.10整型数组a

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论