学习-c语言教学相关第08讲_第1页
学习-c语言教学相关第08讲_第2页
学习-c语言教学相关第08讲_第3页
学习-c语言教学相关第08讲_第4页
学习-c语言教学相关第08讲_第5页
免费预览已结束,剩余63页可下载查看

下载本文档

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

文档简介

第8讲指针2第8讲指针指针的概念指针变量的定义和初始化指针运算符指针与数组指针数组程序设计举例C语言程序设计指针的故事是“稀饭”(C_fans)最挚爱的武器很多“MissionImpossible”由指针完成大多数语言都有无数的“不可能”而C语言是“一切皆有可能”——李宁“ImpossibleisNothing”——adidas3指针指针的故事“该程序执行了非法操作,即将关闭”这种错误几乎全是由指针和数组导致的黑客攻击服务器利用的bug绝大部分都是指针和数组造成的某些用C的人,尽量避免使用指针4指针指针的故事5指针

某国情报机关最近一段时期接连损失了好几名情报人员,经过调查,发现是在自己内部存在一名“鼹鼠”,但不知是谁。不得已,买通了一名隐藏极深的双重间谍进行调查。很快,调查取得了重大突破,这名间谍成功地获取了该“鼹鼠”的代号。因此情报局长派遣一名经验丰富的特工前去取回这条极其重要的情报。双方约定的接头地点在大厦的1101房间。1101大厦110211031104110521012102210321042105310131023103310431052103210331023102K指针指针的故事地址也是一种数据:街道上的门牌号码;同学录上的通讯地址:××大学×号楼××房间。指针'K'…3102…2103110121033102字符数据chpqch的地址q的地址指针的故事寻址方式如何读写内存中的数据?通过变量的地址访问变量所在的存储单元访问一个变量需要知道:(1)它的起始地址(2)它的数据类型对于内存:物理上,以字节为单元逻辑上,以不同数据类型为单元。两种寻址方式直接(寻址)访问直接按变量地址来存取变量内容的访问方式间接(寻址)访问通过指针变量来间接存取它所指向的变量的访问方式指针9指针的概念指针就是内存对象的地址。内存对象包括:变量,数组,函数等。C语言允许直接通过地址来处理数据。指针intx;x=23;内存单元的地址一个无符号的整数,就是指向变量的指针。变量名其实就是内存单元的符号化名称。内存单元的内容就是变量的值。FF00x23直接引用Directreference通过变量名直接引用变量占用的内存单元。10指针变量指针变量就是保存指针(内存地址)的变量。TheCProgrammingLanguage(K&R):Apointerisavariablethatcontainsthememoryaddress

of

anothervariable.指针intx=23;int*x_pointer;x_pointer=&x;FF00x23FFF0x_pointerFF00指针变量保存变量的地址。变量x_pointer的值是变量x的地址(指针)。目前,指针x_pointer指向变量x。间接引用Indirectreference通过指针变量间接引用变量占用的内存单元。11指针变量指针变量的声明类型说明符*指针变量名;定义了一个指向整型变量的指针变量x_pointer*表示x_pointer是一个指针变量x_pointer的值是int*类型的指针,读作:指向int型数据的指针指向整型对象的指针指针变量可以声明为指向任何类型的数据(或对象)指针int*x_pointer;int*x_pointer,*y_pointer;char*charPtr;double*dPtr;注意所有指针变量保存的数据的类型是相同的,即一个内存单元的地址,但是,它们指向的数据的类型可以不同。声明多个指针时,每个变量前都必须有*这三种数据类型的相同点:它们都是指针类型,它们的变量存放的都是地址,长度都是4个字节;这三种数据类型的不同点:它们是不同的数据类型,相互之间不能替代使用。例如,当需要一个“pointertoint”类型的指针时,不能用一个“pointertochar”类型的指针来代替。现在我们有了三种新的数据类型(指针类型):int* “pointertoint”double* “pointertodouble”char* “pointertochar”

基类型+指针类型符=新的数据类型(指针类型)(why?)指针变量13指针变量指针是一种变量,因而也具有变量的三个要素,但指针是一种特殊的变量,其特殊性表现在它的类型和取值上。具体而言:变量名:与一般的变量命名规则相同;

变量的值:是另一个变量的内存地址;变量的类型:包括两种类型,指针变量本身的类型,即指针类型,它的长度为4个字节;指针指向的变量的类型,指明了该变量的长度。指针指针变量能否存放自己的地址?14指针变量的初始化一些问题:

如何把变量x的地址存放在指针x_pointer

能否:原因:类型不匹配,2000为常量,x_pointer为

int*型修改:

指针变量指针Warning:Nonportable pointerconversion2000200420082012intx=20;int*x_pointer;x20?x_pointerx_pointer=2000;?2000从何而来?编程时并不知道变量的实际地址;x_pointer=(int*)2000;强制类型转换200015我们想要做的事情:x_pointer

=x的内存地址;变量的地址用取地址运算符(&)获得变量在内存中的地址。我们写的语句:内存对象的地址指针intx;scanf(“%d”,&x);x_pointer=&x

;取地址运算符:&返回变量在内存中的地址。&x的类型是

int*,即指向整型变量的指针类型。一般的,如果x的类型是T,则&x的类型为指向T型变量的指针类型。16取地址运算符指针intx,*x_pointer;x=20;x_pointer=&x;x_pointer“指向”x;x的地址是x_pointer的值x_pointerx200020002000&x20fff017如何使用指针x_pointer

来访问或修改变量x的值我们想要做的事情:y=x_pointer所指向的变量的值;

或x_pointer所指向的变量内存单元=值;指针运算符:**:指针运算符(或称“间接访问”运算符)我们写的语句:指针运算符指针inty;y=*x_pointer;*x_pointer=5;185指针运算符:*返回指针变量所指向的对象的别名。

*

x_pointer的类型是

int。一般的,如果x_pointer的类型是指向T型变量的指针类型,则*

x_pointer的类型为T。指针运算符指针intx,*x_pointer;inty;x=20;x_pointer=&x;y=*x_pointer

;*x_pointer=5;x_pointerx200020002000&x20fff0X的别名:*x_pointer520y19指针变量指针变量的初始化在声明语句中为指针变量指定初值。指针变量可以被初始化为0,NULL或一个地址量。0和NULL是等价的(用NULL更好)NULL是在<stdio.h>等几个头文件中定义的符号常量,表示0指针intx,*p=&x;intx,*p=NULL;空指针:不指向任何对象一个其值为NULL的指针不同于一个未初始化的指针。一个指针在定义后,是未被初始化的,其值是随机的,即可能指向某个无效的地址,此时若对它进行访问,将会出错。而NULL常量用来明确地说明一个指针不指向任何有效的数据。20案例分析:指针运算符指针运算符指针#include<stdio.h>voidmain(){inta,*aPtr;a=7;

aPtr=&a;printf("Theaddressofais%p"

"\nThevalueofaPtris%p",&a,aPtr);printf("\n\nThevalueofais%d"

"\nThevalueof*aPtris%d",a,*aPtr);printf("\n\nShowingthat*and&areinversesofeachother."

"\n&*aPtr=%p"

"\n*&aPtr=%p",&*aPtr,*&aPtr);}21案例分析:指针运算符指针运算符运行结果指针Theaddressofais1A58ThevalueofaPtris1A58Thevalueofais7Thevalueof*aPtris7Showingthat*and&areinversesofeachother.&*aPtr=1A58*&aPtr=1A58*和&是互反的指针变量的定义和初始化【例】使用指针变量在屏幕上显示变量的地址值定义了指针变量pa,但pa并未指向a?如果指针指向一个非你控制的内存空间并对该空间进行访问,将可能造成危险指针变量的定义和初始化【例】使用指针变量在屏幕上显示变量的地址值指针变量使用之前必须初始化Neveruseuninitializedpointers

指针变量的定义和初始化【例】使用指针变量在屏幕上显示变量的地址值

指针变量的定义和初始化【例】使用指针变量在屏幕上显示变量的地址值Pointershavenames,typesandvalues

指针变量的定义和初始化【例】使用指针变量在屏幕上显示变量的地址值指针变量指向的数据类型称为基类型指针变量只能指向同一基类型的变量指针变量的定义和初始化【例】使用指针变量在屏幕上显示变量的地址值不能写成:int*pa,pb;

间接寻址运算符【例】使用指针变量,通过间接寻址输出变量的值pa0*pa&aa

间接寻址运算符【例】使用指针变量,通过间接寻址输出变量的值pa0*pa&aa9引用指针所指向的变量的值称为指针的解引用(PointerDereference)

例输入a和b两个整数,按先大后小的顺序输出a和b。解题思路:用指针方法来处理这个问题。不交换整型变量的值,而是交换两个指针变量的值。

间接寻址运算符#include<stdio.h>intmain(){int*p1,*p2,*p,a,b;

printf(“integernumbers:");

scanf(“%d,%d”,&a,&b);

p1=&a;p2=&b;

if(a<b){p=p1;p1=p2;p2=p;}

printf(“a=%d,b=%d\n”,a,b);

printf(“%d,%d\n”,*p1,*p2);

return0;}abp1p2p59&a&b成立#include<stdio.h>intmain(){int*p1,*p2,*p,a,b;

printf(“integernumbers:");

scanf(“%d,%d”,&a,&b);

p1=&a;p2=&b;

if(a<b){p=p1;p1=p2;p2=p;}printf(“a=%d,b=%d\n”,a,b);

printf(“%d,%d\n”,*p1,*p2);return0;}abp1p2p59&a&b&b&a#include<stdio.h>intmain(){int*p1,*p2,*p,a,b;printf(“integernumbers:");scanf(“%d,%d”,&a,&b);

p1=&a;p2=&b;

if(a<b){p=p1;p1=p2;p2=p;}printf(“a=%d,b=%d\n”,a,b);printf(“%d,%d\n”,*p1,*p2);return0;}abp1p2p59&a&b&b&a#include<stdio.h>intmain(){int*p1,*p2,*p,a,b;printf(“integernumbers:");scanf(“%d,%d”,&a,&b);p1=&a;p2=&b;if(a<b){p=p1;p1=p2;p2=p;}printf(“a=%d,b=%d\n”,a,b);printf(“%d,%d\n”,*p1,*p2);return0;}abp1p2p59&a&b&b&a可否改为p1=&b;p2=&a;?35指针运算指针可以参与以下运算:赋值运算给指针变量赋值关系运算两个指针之间的比较算术运算加(减)一个整数两个指针相减指针36指针运算指针的赋值运算可以把指针赋给同类型的指针变量。指针intx=10,*p,*q;p=&x;q=p;printf(“*q=%d",*q);*q=1010x2000p2000q200037指针运算指针的赋值运算把指针赋值给类型不同的指针变量时要进行类型转换。但void*类型的指针是一个例外。指针inta,*intPtr=&a;char*charPtr;charPtr=(char*)intPtr;void*voidPtr;voidPtr=intPtr;charPtr=voidPtr;void*通用指针代表任何指针类型不能间接引用*voidPtr=10;voidf(void*p);…f(ptr);常见的应用形式38指针运算指针的关系运算比较两个指针的值。指针p1<p2p1<=p2p1>p2p1>=p2p1==p2p1!=p22000p12002p2char*charPtr,*ptr;…charPtr>=(char*)2000…if(ptr!=0)…与同类型的指针常量比较常常与0比较39指针运算指针的算术运算自增自减(++,--)加上一个整数(+,+=,-,-=)两个指针相减指针40指针运算指针的算术运算举例指针int*vP1,*vP2;vP1=(int*)2000;vP2=vP1+2;数组vvP12000+2=20022000+2*4=2008vP2所指向的内存对象的长度sizeof(int)?vP1+2200020042008201241指针运算指针的算术运算举例指针int*vP1,*vP2;vP1=(int*)2000;vP2=(int*)2008;数组vvP1vP2vP2和vP1之间内存对象的个数?vP2-vP1

=(2008-2000)/sizeof(int)

=2指针的算术运算在数组上使用才有意义200020042008201242指针与数组数组和指针关系密切。数组名是一个指针常量。数组元素指针:指向数组元素的指针。数组元素指针可以用来完成任何涉及数组下标的操作。将bPtr的值置为数组b中的第一个元素的地址

bPtr=b;等价于

bPtr=&b[0];指针intb[5];int*bPtr;43指针与数组数组和指针关系密切。引用数组元素的表达式数组元素b[3]

可以用*(bPtr+3)

来引用3是偏移量这种表示法称为指针偏移量表示法还可以用bPtr[3]

来引用称为指针下标表示法与b[3]

相同还可以用*(b+3)

来引用指针使用指针变量p指向数组a:图5-4一维数组的指针法访问

inta[10],*p;p=a;inta[10],*p=a;或者写成:一维数组与指针

p+i指向元素a[i]使用指针法引用一维数组的第i个元素的方法:1)*(p+i)访问元素a[i]。2)*(a+i)访问元素a[i]。

3)指向数组的指针变量也可以带下标,即:p[i]与*(p+i)等价,表示元素a[i]45指针与数组数组和指针关系密切。引用数组元素的表达式指针main(){inti,a[5]={1,2,3,4,5};for(i=0;i<5;i++)printf("%2d",a[i]);}下标法main(){inti,a[5]={1,2,3,4,5};for(i=0;i<5;i++)printf("%2d",*(a+i));}地址法main(){inta[5]={1,2,3,4,5},*p;for(p=a;p<(a+5);p++)printf("%2d",*p);}指针法46指针与数组数组和指针互换使用时的注意事项数组名是一个指针常量。指针因为a是数组名,即数组的首地址,它的值在程序运行期间是固定不变的!是一个常量。错main(){inta[5]={1,2,3,4,5},*p;for(p=a;a<(p+5);a++)printf("%2d",*a);}47指针与数组数组和指针互换使用时的注意事项注意指针变量的值。指针要注意指针变量的当前值。ppa[0]a[1]a[2]a[3]a[4]数组amain(){inti,a[5],*p;p=a;for(i=0;i<5;i++)scanf("%d",p++);for(i=0;i<5;i++,p++)printf("%d",*p);}错p=a;

例5-10

使用指针法改写例5-2。即输入N个数据存入数组中,输出其中的最大元素。方法一:通过数组名计算数组元素的地址,引用数组元素。#defineN10voidmain(){inti,imax,max,a[N];/*imax代表最大值元素 所在的位置*/printf("Enter%dNumbers\n",N);/*提示输入 数据*/for(i=0;i<N;i++)scanf("%d",a+i);

一维数组与指针

max=a[0]; imax=0;/*假设第0元素就是最大元素*

for(i=1;i<N;i++){if(*(a+i)>max){

max=*(a+i);/*或者写为max=a[i]*/

imax=i;}printf("TheMaxNumbwera[%d]=%d\n",imax,max);}

一维数组与指针

方法二:使用指针变量作循环控件变量#defineN10main(){inti,imax,max,a[N],*p;

/*imax代表最大值 无素所在的位置*/printf("Enter%dNumbers\n",N);/*提示 输入数据*/

for(p=a;p<a+N;p++)scanf("%d",p);

一维数组与指针

max=a[0];imax=0; /*假设第0元素就是最大元素*/p=a;

/*此语句不能少,因为在输入数据后,指针已 指向数 组外了*/

for(i=0;i<N;i++,p++)/*此语句可以写成

{ for(i=0;p<a+N;i++,p++)*/if(*p>max){

max=*p;

imax=i;}printf("TheMaxNumbwea[%d]=%d\n",imax,max);}

一维数组与指针

两种方法的比较:

1)方法一与例5-2的下标法执行效率是相同的,C编译系统是将a[i]转换为*(a+i)处理,即先计算元素的地址。

2)方法二比方法一和下标法执行效率高,用指针变量直接指向元素,不必每次都重新计算地址,使用指针运算(p++)指向下一个元素,这种有规律地改变地址值(p++)能大大提高程序执行效率。

3)用下标法比较直观,能直接知道是第几个元素。使用指针法,一定要知道当前指针指向哪个元素,否则可能得到意想不到的结果。

一维数组与指针

使用指针引用数组元素,应注意以下2个问题:

1)若指针p指向数组a,虽然p+i与a+i、*(p+i)与*(a+i)意义相同,但仍应注意p与a的区别(a代表数组的首地址,是不变的;p是一个指针变量,可以指向数组中的任何元素),例如:for(p=a;a<(p+10);a++)/*错误,因为a代表数组 的首地址,是不变的,a++不合法*/printf("%d",*a)2)指针变量可以指向数组中的任何元素,注意指针变量的当前值。

一维数组与指针

指向数组元素的指针的一些运算设有定义“inta[10],*p=a;”,则对指向数组的指针变量的一些操作运算如表5-1所示

一维数组与指针

例5-12

指向数组的指针变量的运算示例。#include<stdio.h>voidmain(){inta[6]={2,4,6,8,10,12},*p;p=a+2;

/*输出p所指向的元素,即a[2],让p指向下一个元素,即a[3]*/printf("%d",*p++);

/*输出p所指向元素的值,即a[3],让p指向下一个元素,即a[4]*/printf("%d",*(p++));

/*让p指向下一个元素,即a[5],输出p所指向元素的值,即a[5]*/printf("%d",*++p);

/*输出p所指向元素的值,即a[5],让p所指向元素的值加1*/printf("%d",(*p)++); printf("%d",*p); /*输出p所指向元素的值,即a[5]*/}

一维数组与指针

二维数组与指针

1.二维数组的地址例如有如下二维数组的定义:inta[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}}按5.3.1节所述,二维数组a可理解为有三个元素(a[0]、a[1]、a[2])的一维数组,每一个元素又是一个有4个元素的一维数组。

例如:一维数组a[0]的元素为a[0][0],a[0][1],a[0][2],a[0][3]。即:

a[0][0],a[0][1],a[0][2],a[0][3]

a[1][0],a[1][1],a[1][2],a[1][3]

a[2][0],a[2][1],a[2][2],a[2][3]

在C语言中,二维数组在内存中占一片连续存储空间,元素的存放是按行排列的。即存放完一行之后顺次放入第二行。二维数组的指针

1)二级指针常量。因为数组名可以看成是由3个元素a[0]、a[1]、a[2]构成的一维数组,每个元素指向该行的首地址。因此,二维数组名是一个二级指针常量。

2)一级指针常量。a[0]可以看成是由a[0][0]、a[0][1]、a[0][2]、a[0][3]构成的一维数组,可以将a[0]这个特殊数组名理解为指向int类型的一级指针常量。a[1]与a[0]具有同样性质,a[1]与a[0]的偏移量是一行元素的长度。

二维数组a的指针如图5-6所示(假设第一个元素的地址是0x2000)

二维数组与指针

二维数组a的指针。假设第一个元素的地址是0x2000)

二维数组与指针

说明:

a+0表示第0行的首地址,与a[0]、或&a[0][0]的值相同,但意义及参与的运算不同。a+1表示第1行元素的首地址,与a[1]、或&a[1][0]的值相同。a+2表示第2行元素的首地址,与a[2]、或&a[2][0]的值相同。

由于把a[0]、a[1]、a[2]看成一维数组,它们代表各自数组的首地址,即:a[0]相当于&a[0][0],a[1]相当于&a[1][0],a[2]相当于&a[2][0]根据一维数组的表示方法,则有:a[0]+1:表示一维数组中第二个元素的地址,相当于&a[0][1]。a[0]+2:表示一维数组中第三个元素的地址,相当于&a[0][2]。a[1]+1:表示二维数组中第二个元素的地址,相当于&a[1][1]。

二维数组与指针

注意:

a+0和a[0]的区别:虽然都表示第一行元素的首地址,但它们的含义是不同的。a+0是行指针,它的每一个增量单位是一行,a[0]后者是一个元素指针,它的增量单位是元素。*(a+0)相当于a[0],实际上*(a+0)相当于将行指针转换成列指针(指向元素的指针)。已知某元素的指针后,可以用*运算符访问该元素。例如:*(a[1]+2)=*(*(a+1)+2)=a[1][2]=13二维数组与指针

例5-14

输出二维数组的相关地址数据和元素值#include<stdio.h>#definePF"%u,%u,%u,%u,%u\n"voidmain(){inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};printf(PF,a,*a,a[0],&a[0],&a[0][0]);printf(PF,a+1,*a+1,a[0]+1,&a[0]+1,&a[0][0]+1);printf("\n");printf(PF,a+1,*(a+1),a[1],&a[1],&a[1][0]);printf(PF,a+1+1,*(a+1)+1,a[1]+1,&a[1]+1,&a[1][0]+1);printf("\n");printf(PF,a+2,*(a+2),a[2],&a[2],&a[2][0]);printf("\n");printf("%d,%d\n",*(a[1]+1),*(*(a+1)+1));}65458,65458,65458,65458,6545865466,65460,65460,65466,65460程序的运行结果如下:65466,65466,65466,65466,6546665474,65468,65468,65474,6546865474,65474,65474,65474,654746,

温馨提示

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

评论

0/150

提交评论