计算机程序设计:从理论到实践(微课版)课件第8章 对数据的间接访问_第1页
计算机程序设计:从理论到实践(微课版)课件第8章 对数据的间接访问_第2页
计算机程序设计:从理论到实践(微课版)课件第8章 对数据的间接访问_第3页
计算机程序设计:从理论到实践(微课版)课件第8章 对数据的间接访问_第4页
计算机程序设计:从理论到实践(微课版)课件第8章 对数据的间接访问_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

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

文档简介

8

对数据的间接访问1主要内容8.1指针的概念8.2指针变量的定义、初始化及引用8.3使用指针访问数组8.4使用指针访问混合类型数据8.5使用指针访问函数和文件28.1指针的概念直接访问与间接访问内存中每个字节都有一个编号,称为该字节的地址程序运行时,每个变量都会被分配一块内存空间,其起始字节的编号,称为该变量的地址直接访问:通过变量名(对应一个地址)访问内存空间中的数据(用钥匙打开快递柜直接取物品)间接访问:通过第二个变量访问第一个变量所对应的内存空间中的数据(快递柜B中存放着快递柜A的钥匙,通过快递柜B去取A中的物品)3bdca内存地址…

内存用户数据区0x61fe08…0x61fe140x61fe180x61fe1c…inta,c,d;直接访问:a=8;c=a+5;printf("c=%d",c);间接访问:int*b;b=&a;d=*b+5;printf("d=%d",d);0x61fe1c138134通过内存单元的地址能够在内存中找到该内存单元,其作用就像公园里指针形状的路标。因此,将地址形象化地称为“指针”(pointer),它指向地址所标识的内存单元(变量的指针就是变量的地址)必须定义一个变量专门存放指针,该变量称为指针变量

58.2指针变量的定义、初始化及引用指针变量的定义格式:

基类型名*指针变量名

/*基类型——该指针变量所指向的地址中存储的数据的类型*/6例如:int*pi1,*pi2,*pi3;float*pf1,*pf2,*pf3;char*pc1,*pc2,*pc3;指针变量的初始化格式:基类型名*指针变量名=&指向的变量名;例如:inti;int*pi=&i;floatf;float*pf=&f;charc;char*pc=&c;7指针变量的引用格式:*指针变量名例8-1:

#include<stdio.h>intmain(){inti,*pi;pi=&i;scanf("%d",pi);printf("i=%d,*pi=%d\n",i,*pi);*pi=314;printf("i=%d,*pi=%d\n",i,*pi);return0;}8注:

(1)定义指针变量后,如果没有被初始化过或不赋初值,该指针变量无具体的指向,即指向不确定(野指针)。野指针不会直接引发错误,但操作野指针指向的内存区域将会出问题。导致一个指针变量成为野指针的原因:1)指针变量未初始化,它所指向的内存地址是随机的;2)指针越界访问,指向的范围超出了合理范围,如利用指针方式越界访问数组。(2)如果一个指针变量最初所指向的对象已被删除或用free()函数释放掉了,那么该指针变量就成了悬空指针(也叫空挂指针)。这有点像超市的储物柜,我一小时前使用了储物柜的某个格子并且在半小时前退掉了,尽管我知道这个储物格的号码,但现在该储物格已经被别人使用了,至于格子里的具体内容当然无法知晓了,更别提去操作了9悬空指针的情况在函数调用时容易出现,主调函数中的指针变量指向了被调函数中的局部变量,但函数调用结束时,局部变量的内存空间被释放了,主调函数中的指针变量即变成了悬空指针无论是野指针还是悬空指针,都是指向无效的内存区域(不安全不可控)的指针,访问“不安全可控”的内存区域将导致难以预料的后果:1)它指向的内存空间可能已重新分配给新的变量,如果这个新变量的数据类型与原变量不同,指针的类型检查就是无效的。即使新的变量仍然具有相同的类型,它的值也与原变量的值毫无关系。另外,可能还会破坏这个新变量的值;2)指针指向的位置可能已经被内存管理系统临时使用,此时修改它的值将导致内存管理出错。10(3)避免使用野指针:最好的办法就是养成在定义指针后、在使用之前完成初始化或赋值的良好习惯,以及注意不要越界使用指针。(4)解决悬空指针的办法:所指向的对象删除或开辟的内存空间释放后,将该指针变量的值置为NULL(0,它可以赋给任何类型的指针变量),以及避免在调用函数时返回局部变量的地址。(5)可以定义void类型指针变量。void类型指针是一种特殊的指针,它无须类型转换即可指向任意类型指针;任意类型的指针也可指向void类型11#include<stdio.h>//例8-2voidmain(){doublef=1.23,*pf;void*p;p=&f;pf=(double*)p;printf("&f=%x,p=%x,pf=%p\n",&f,p,pf);printf("*pf=%f\n",*pf);}12指针的基本操作取址运算(&):求取拟操作对象的地址(数组的起始地址用数组名获取)取值运算(*):取得指针所指向对象的内容。当运算符“*”与“&”连用时,两者将会抵消。13例如,inta=123,*p;p=&a;

思考:*(&a)?&(*p)赋值(=):将一个指针值赋给某个指针变量charc;char*pc=&c,*pc1=pc;注:赋值运算要求赋值号右侧的指针值与赋值号左侧的指针变量之间类型相容,不同类型的指针变量之间不能赋值算术运算:可以用某些受限制的形式进行指针算术运算指针与整数进行加减运算:不是简单的加减法++/--运算:不是简单的自加/自减指针变量减法运算:通常用于指向同一数组元素关系运算:指向同一数组时才有意义类型转换:

格式(类型名*)指针变量名14多级指针指针变量的值也需要存放在内存的某个区域,它也有类型、起始地址。可以另外定义一个指针变量来存放其起始地址,这个指针称为指向指针的指针(多级指针)定义格式:基类型名**指针变量名[=&指针变量名];15例如:inti=314,*pi,**ppi,

***pppi;pi=&i;ppi=π

pppi=&ppi;要访问变量i的内容,有四种方式:(1)直接访问i(2)通过指针变量pi间接访问i:*pi

i(3)通过指针变量ppi间接访问i:**ppi

i(4)通过指针变量ppi间接访问i:***pppi

i16指针变量作为函数的参数指针变量作为函数的形参将一个实参变量的地址传递到一个函数中形参定义为与实参同类型的指针变量实现对普通变量的地址传递

指针传递可以降低参数传递的代价,使主调函数与被调函数共享同一块内存空间17#include<stdio.h>//例8-3voidfun(inta,intb,int*p,int*q){ *p=a+b; *q=a-b;}voidmain(){inta=111,b=333,sum,sub;fun(a,b,&sum,&sub);printf("sum=%d,sub=%d\n",sum,sub);}18指向指针的指针变量作为函数的形参实现对指针类型变量的地址传递函数调用时,将实参指针变量本身的地址传递给形参形参定义为二级指针变量19#include<stdio.h>voidexch(int**p,int**q){ intt; t=**p; **p=**q; **q=t;}voidmain(){inta=28,b=75;printf("a=%d,b=%d\n",a,b);int*pi1=&a,*pi2=&b;exch(&pi1,&pi2);printf("a=%d,b=%d\n",a,b);}//例8-4程序功能:交换两个变量的值20使用指针访问一维数组如:int*p,a[10];p=a;//使指针变量p指向数组下标法地址法指针法第k个元素的地址&a[k]a+kp+k第k个元素a[k]*(a+k)*(p+k)8.3使用指针访问数组21注:(1)要通过指针处理数组,首先应该使指针指向该数组,具体方式为:p=a;或p=&a[0];(2)指针变量是变量,值可以改变,数组名是常量,值不能改变。如:p++;正确

a++;错误

(3)指向同一数组的两个指针变量相减,为这两个指针变量所指向的数组元素之间跳过的元素个数。两个指针之间不能进行加、乘及除法运算

(4)指针变量也可以带下标,如:*(p+k)等价于p[k]22例8-5:#include<stdio.h>voidmain(){inti,*p,a[10]={1,2,3,4,5,6,7,8,9,10};printf("下标法输出各元素:\n");for(i=0;i<10;i++)printf("%4d",a[i]);printf("\n指针法输出各元素:\n");for(p=a;p<a+10;p++)printf("%4d",*p);printf("\n");}23

也可以通过指针数组或指向指针的指针变量处理数组指针数组:数组元素全部为指针类型,每个元素都相当于一个指针变量格式:

基类型名*数组名[长度]={地址表列};24例8-6:#include<stdio.h>voidmain(){inti,a[5];int*p[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};for(i=0;i<5;i++)scanf("%d",p[i]);for(i=0;i<5;i++)printf("%3d",*(p[i]));printf("\n");}25例8-7:#include<stdio.h>voidmain(){inti,a[5],*p[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};int**pt=p;for(i=0;i<5;i++)scanf("%d",*pt++);

pt=p;for(i=0;i<5;i++)printf("%3d",*(*pt++));printf("\n");}26使用指针访问二维数组a[0][0]a[0][1]a[0][2]a[0][3]a[0][4]a[2]a[1]a[0]a[3]a[4]例:inta[5][5];aa+1a+2a+3a+4a[0]a[0]+1a[0]+2a[0]+3a[0]+4列指针行指针27如:inta[3][4],*p;

p=a[i];//p每次跳动一个元素,为列指针变量

下标法地址法列指针法第i行、j列元素的地址&a[i][j]a[i]+j或*(a+i)+jp+j第i行、j列元素a[i][j]*(a[i]+j)或*(*(a+i)+j)*(p+j)28行指针变量(指向一维数组整体的指针变量)定义格式:

基类型名(*指针变量名)[长度];

如:int(*p)[4];//p的基类型为int[4],即具有4个//int型元素的一维数组

inta[3][4];p=a;下标法地址法行指针法第i行、j列元素的地址&a[i][j]a[i]+j或*(a+i)+j*(p+i)+j第i行、j列元素a[i][j]*(a[i]+j)或*(*(a+i)+j)*(*(p+i)+j)29例8-8:#include<stdio.h>intmain(){inti,j,a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12},*pt,(*p)[4];printf("使用列指针输出数组a的元素:");for(i=0;i<3;i++){

pt=a[i];for(j=0;j<4;j++)printf("%4d",*(pt+j));printf("\n");}30printf("使用行指针输出数组a的元素:");

p=a;for(i=0;i<3;i++){for(j=0;j<4;j++)printf("%4d",*(*(p+i)+j));printf("\n");}return0;}31使用指针处理字符串常量例8-9:#include<stdio.h>voidmain(){char*p;

p="ChinaEcust";puts(p);for(;*p!='\0';p++)printf("%c",*p);}32使用指针访问字符数组例8-10:#include<stdio.h>voidmain(){charstr1[50],str2[20],*p,*q;printf("输入两个字符串:\n");gets(str1);gets(str2);p=str1;q=str2;while(*p)p++;while(*p++=*q++);printf("结果字符串是:%s\n",str1);}33字符数组字符指针变量存储方式不同分配一段连续的空间分配一个用于存放地址的存储区域运算方式不同数组名是指针常量,不能运算字符指针变量是指针变量,可以运算赋值方式不同可以初始化,不能用赋值语句整体赋值可以初始化,可以用赋值使用字符指针变量和字符数组的比较34结构体指针变量的定义与初始化定义格式:结构体类型*指针变量名;如:structStudent{intnum;charname[20];floatscore;};structStudentst1={1001,“张三”,85};structStudent*p=&st1;或structStudent*p;p=&st1;8.4使用指针访问混合类型数据35结构体指针变量的引用

与基本数据类型指针变量的引用方式类似

引用形式一:(*结构体指针变量).成员名引用形式二:结构体指针变量->成员名

36注:(1)由于成员运算符“.”的优先级比指针运算符“*”高,*p两侧应加“()”,即(*p).num(2)“->”称为指向运算符,该运算符只能用于结构体指针变量,直接获得成员变量。即p->num与(*p).num等价

(3)C允许一个结构体类型的成员中含有指向同一结构体类型的指针,这种结构称为自引用结构

如:structabc{inta;floatb;structabc*p;}37如:(*p).num++;(*p++).num;(*++p).num;p->num++;

(p++)->num;++p->num;

//引用p所指向的结构体变量中成员num的值,用完使该值增1//引用p所指向的结构体变量中成员num的值,然后再使p自增//++运算符和*运算符优先级相同,结合性为从右到左//引用p所指向的结构体变量中成员num的值,用完使num增1//引用p所指向的结构体变量中成员num的值,然后使p自增//使p所指向的结构体变量中成员num的值先增1,再引用num值38使用指针访问结构体数组如:structStudent{intnum;charname[20];floatscore;};structStudentst[10];structStudent*p=st;引用时:可以st[0].name、(*p).name、p->name

p++;表示p指向st[1]元素39返回指针值的函数

定义格式:类型名*函数名(参数表列);如:int*fun_p(inta,floatb,charc);或int*fun_p(int,float,char);

这里,定义了一个函数,该函数具有三个不同类型的参数,函数的返回值是一个指针值8.5使用指针访问函数及文件4041例8-11:输出两个数据中的较大值及其地址。#include<stdio.h>int*fun_p(intx,inty){printf("Theaddressofxandyare%p,%p.\n",&x,&y);returnx>y?&x:&y;}voidmain(){inta,b;int*pt;printf("Inputaandb:");scanf("%d%d",&a,&b);pt=fun_p(a,b);printf("Maxvalueis%d.Its'addressis%p.\n",*pt,pt);}Code::BlocksVC++6.042改进:#include<stdio.h>int*fun_p(int*x,int*y){printf("Thevalueofxandyare%p,%p.\n",x,y);return*x>*y?x:y;}voidmain(){inta,b;int*pt;printf("Inputaandb:");scanf("%d%d",&a,&b);printf("Theaddressofaandbare%p,%p.\n",&a,&b);pt=fun_p(&a,&b);printf("Maxvalueis%d.Its'addressis%p.\n",*pt,pt);}函数指针变量的定义与初始化

定义格式:类型名(*指针变量名)(参数表列);如:int(*p)(inta,floatb,charc);或int(*p)(int,float,char);

这里,定义p为一个指向具有三个不同类型参数、返回值为int的函数的指针变量43例8-12:#include<stdio.h>floatfun_add(intx,inty){returnx+y;}

floatfun_sub(intx,inty){returnx-y;}

floatfun_mul(intx,inty){returnx*y;}

floatfun_div(intx,inty){return1.0*x/y;}44voidmain(){inta,b;float(*fp)(int,int);printf("请输入两个整数a和b:");scanf("%d%d",&a,&b);

fp=fun_add;printf("%d+%d=%.0f.\n",a,b,(*fp)(a,b));

fp=fun_sub;printf("%d-%d=%.0f.\n",a,b,(*fp)(a,b));

fp=fun_mul;printf("%d*%d=%.0f.\n",a,b,(*fp)(a,b));

fp=fun_div;printf("%d/%d=%f.\n",a,b,(*fp)(a

温馨提示

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

评论

0/150

提交评论