计算机C语言程序设计ch07指针上.ppt
第七章 指针,指针与变量指针运算指针与数组指针与字符串指向指针的指针命令行参数本章小结,作业:7.6 7.8 7.9 7.10 7.13 7.16 7.23 7.27练习:7.1-7.5 7.7 7.11 7.12 7.14 7.15,指针是高级程序设计语言中一个重要的概念正确灵活运用指针可有效地表示和使用复杂的数据结构可动态分配内存空间,节省程序运行空间,提高运行效率不正确理解和使用指针,指针将是程序中最危险的成分,由此带来的后果可能是无法估量的。,指针,必须清楚:数据在内存中的存储和访问方式已知:每个变量在计算机内存占用一块存储区该存储区的地址就是相应变量的地址变量可能有值该存储区保存的内容就是相应变量的值,变量地址,变量名,变量值,变量的存储区,7.1 指针与变量,例如有变量声明 char c=S;int v=27,u=32;int*p=则编译程序分别给变量 c、v、u、p 分配存储空间如图所示其中:B900是变量v的地址 也是变量v的指针,指针即是地址 变量的指针就是变量的地址 存放指针(变量地址)的变量是指针变量 指针变量简称指针必须理解清楚:给变量分配的内存区域该内存区域的地址该内存区域保存的内容以及它们之间的关系,指针类型与指针变量,指针类型:格式指向T类型变量的指针类型用:T*其中:T称为该指针类型的基类型在C中,任何一个类型都伴随着一个指向本类型变量的指针类型基类型(指针所指向的类型)可以是基本数据类型构造型数据类型指针类型函数,指针变量:,声明指针变量T*p1,*p2,*pn;其中,T*指针类型,T称为该指针类型的基类型 pi是标识符,是指针类型的变量,指向T类型变量的指针变量意义指针变量简称指针是一种特殊的变量它里面存储的“值”被解释成为一个变量的地址,确切的说是计算机内存的一个地址,T*v;称“指向T类型变量的指针变量v”“v指向T类型”“T类型的指针v”例如int x,y;char ch=a;int*ip1=/*pa是指向float类型变量的指针变 量*/,cp=,指针变量的值是内存地址(即变量的地址)取地址的方法:基本类型变量、数组成员、结构体变量、公用体变量等,用求地址运算符“函数的地址为函数的入口地址,用函数名字表示,设有声明:int*ip1,*ip2;char*cp;int x,y;char ch=a;在此基础上,有操作:,ip1=,cp=,x=5;或*ip1=5;,ip2=,y=8;,内存单元,地址,变量,E990,指针ip1,E994,指针ip2,E998,指针cp,E99C,int型变量 x,E99E,int型变量 y,E9A0,char型变量 ch,E99C,E99E,E9A0,a,5,8,如果再执行 ip1=ip2;,E99E,指针变量ip1指针所指变量x,访问变量(指针所指变量)的方法直接访问:直接使用变量的名字int v=10;v=v*10;间接访问:用指向该变量的指针*p 比如访问变量 v 可以用下面方式来实现:int*p=它通过指向 v 的指针变量 p,采用间接访问的方 式实现对变量 v的访问,取出变量 v 的值参与运算区分指针变量和指针所指的变量区分开指针值和指针所指变量的值,程序片段 int*ip;int i=3,j;ip=,j=*ip;,3,注意:指针变量在使用前一定有值,void main()/*2*/int i,j;/*3*/char ch;/*4*/int*pi,*pj;/*5*/char*pch;/*6*/printf(Input an integer:);/*7*/scanf(%d,/*17*/*18*/,例7-1 指针变量与指针所指变量,该程序运行过程为:程序第7行输出提示信息在键盘输入23程序第9行输出提示信息在键盘输入45程序第11行输出提示信息在键盘输入r程序第16行输出程序第17行输出,Input an integer:23Input another integer:45Input a char:ri=23 j=45 ch=r*pi=23*pj=45*pch=r,变量i,j,ch在内存中存储的内容,分别通过对指针pi,pj,pch的间接引用获得。,void main()/*2*/int i,j;/*3*/int*pmax,*pmin,*p;/*4*/printf(Input an integer:);/*5*/scanf(%d,/*16*/*17*/,例7-2 用指针变量实现:输入两个 整数,按从大到小顺序输出,假设输入25、38,该程序运行到第10行结束时,&i,&i,&j,&j,&i,当程序执行完第12行 p=pmax;,当程序执行完第13行pmax=pmin;,当程序执行完第14行pmin=p;,“*”的用法:“*”放在指针定义中时,表示指针类型 int*p指针定义符,用来说明指针变量“*”放在表达式中的指针变量之前 j=*ip 间接访问操作符,“对指针所指变量的访问”“*”运算符 3*5例7-1第5行的“int*pi,*pj;”,说明指向int类型的指针变量pi、pj;第6行的“char*pch;”,说明指向char类型的指针变量pch 例7-1第17行打印语句中的“*pi”,“*pj”,“*pch”,空指针与无效指针,T*p=NULL;NULL是C指针类型的一个特殊值,称为“空”表示指针变量的值为空,不指向任何变量或函数。NULL值属于所有指针类型。判断指针变量iptr的值是否为空可以使用 iptr!=NULL 或 iptr=NULL保证指针在没有指向有效对象时取值为NULL是一种良好的编程风格。,有时可能不小心生成无效指针 invalid pointer无效指针是指一个指针变量无值,它既没有指向确定的变量或函数,也不是NULL产生无效指针的原因很多,例如说明指针变量后还没有给它赋值;把整型变量转换成指针;回收为指针所指对象分配存储空间;指针运算超出数组范围。访问空指针(指针变量的值为NULL)或无效指针(指针变量无值)所指向的内容是错误的程序中存在无效指针,不是好的程序设计风格,例7-3 分析下述程序片段的执行结果,int*p,*q,u,v;q=,&u,3,&v,3,5,&u,7,NULL,*p=3发生错误,因为 p 不指向任何变量,7.2 指针运算,求地址,赋值=把一个指针值赋值给某个指针变量例子:int*px,*py,x;px=/*将地址4800赋给px,而不是整数类型4800*/,加+(+)和减-(-)操作常用于数组加+(+)当指针指向数组的一个元素(成分),指针值加上一个整数表达式,结果值仍然是个指针值 如:int*p,a10=1,2,3,4,5 p=,例如:int*p,*q,*r,a100;p=则 p+3 是 a13的地址 计算过程如下:&(a10)+3*sizeof(int)即a13的地址,若有q=p+3;则 q指向a13“p+”表示“p=p+1”,减-(-)指针的减法运算包括:指针值减去一个整数表达式,和两个相容的指针值相减 指针值减去一个整数表达式 当指针指向数组的一个成分,指针值减去一个整数表达式,得到的结果值仍然是一个指针值如:int*p,a10=1,2,3,4,5 p=,例如:int*p,*q,*r,a100;p=则 p-3 是a7的地址计算过程是&(a10)-3*sizeof(int)即a7的地址若有 q=p-3;则 q指向a7“p-”表示“p=p-1”,两个相容的指针值相减(同一个数组)指针值相容,即所指的对象是同一个类型 两个相容的指针相减,结果是整型值,即两指针值间的距离 例如int*p,*q,*r,a100;p=则p-q得-5 而 q-p得5,指针加减运算限制:若指针p指向的不是数组成分,或p+k、p-k后超出数组定义范围,则其行为是未定义的,将产生不可预料的结果不能对函数指针、void*类型指针进行加减运算不允许两个指针间进行加法运算,判等运算和关系运算兼容类型的指针进行判等运算和关系运算,得到的结果是bool类型关系运算包括:判断两个指针值是否相等或不相等=、!=;比较两个指针值的大小关系、=、=例子pxpy 判断px所指向的存储单元地址是否小 于py所指向的存储单元地址。px=py 判断px与py是否指向同一个存储单元px=0、px!=0、px=NULL、px!=NULL 都是判断px是否为空指针,一定要注意,参与关系运算的指针值必须是类型兼容的,如果p指向一个int类型变量,而q指向一个 float 类型变量,进行p与q的比较是错误的,例7-4 指针运算实例,#include void main()char str255,*p;int v;scanf(%s,str);p=str;while(*p!=0)p+;printf(The string length is%d n;p-str);,程序运行若输入:abcdef则输出结果为 The string length is 6,7.3 指针与数组,密切的关系数组名是数组的首地址,即a0的地址指针值也是一个地址,如一个指针p指向数组a的首地址即指向a0,则p与a表示的是同一个对象事实上,在C中把指针和数组当作同一个概念看待,数组名是指针,指针也是数组数组名是常量指针,7.3.1 用指针标识数组,例如 int a5;int*iptr iptr=a;/也可以使用 iptr=&(a0)如下操作等价 ai*(a+i)iptri*(iptr+i),例7-5 展示指针与数组间关系,void main()int a5;int i,*p;for(i=0;i5;i+)printf(a%d=,i);scanf(%d,假设分别输入1、2、3、4、5 在输出阶段:执行第一个for语句输出结果:执行第二个for语句输出结果:执行第三个for语句输出结果:,a0=1a1=2a2=3a3=4a4=51 2 3 4 5 1 2 3 4 5 1 2 3 4 5,注意1:数组名是指针常量,数组名是指针常量,不能改变其值如:对数组变量a的增1运算“a+”是非法的 for(a=p;ap+5;a+)printf(%2d,*a);指针变量可以参与运算,比如例7-5中有语句:for(p=a;pa+5;p+)printf(%2d,*p);该语句中,对指针变量p不断进行增1运算,注意2:指针变量的当前值,指针变量指向数组的首地址。如有int a10,*iptr;iptr=a;若访问a数组的第i个元素,可用下标法和指针法ai/下标法*(a+i)/指针法iptri/下标法*(iptr+i)/指针法注意:当指针不是指向数组的首地址,这四种写法不等价,如若有语句iptr=被执行后,则:iptri、*(iptr+i)、ai+2、*(a+i+2)等价 写程序时要特别注意指针变量的当前值,有时由于忽略指针变量的当前值,会使程序产生严重错误,而且这种错误还十分难于查找。例7-6说明该问题。该程序原想输入10个整数,再把它们打印出来,但是程序运行结果却不然,例7-6 输入10个整数,再把它们打印出来。(指针变量错误使用实例),#include“stdio.h”void main()int i,*p,a10;p=a;for(i=0;i10;i+)scanf(“%d”,p+);for(i=0;i10;i+)printf(“%dn”,*p);p+;,错误在:当执行完输入语句后,p已经指向数组的最后一个元素,执行输出时从a9开始的单元输出十个元素,使用指针形式访问数组元素,是从指针变量当前所指的位置开始“向前”或“向后”计算。而不是当初给指针变量赋的初值,写程序时要特别注意指针变量的当前值,有时由于忽略指针变量的当前值,会使程序产生严重错误,而且这种错误还十分难于查找,注意3:数组超界,数组超界是指访问不存在的数组元素。如有如下声明:int a10,*aptr;aptr=a+5;*(aptr+v)指针法访问元素 当v的值大于4时,就会出错。这种错误是最隐蔽、最难于查找的 C系统根本不检查数组超界,注意4:指针变量的运算,自增“+”和自减“-”运算,设有如下声明和运算:int a10,*ptr,v,*q,u;v=5;ptr=,讨论如下运算的意义 ptr+*(ptr+)*ptr+(*ptr)+ptr*(+ptr)*+ptr,运算“ptr+”的意义是把 ptr 的值加 1,运算结束后 ptr指向a6。表达式的值是ptr加1之前时ptr的值,即a5的地址。若有q=ptr+;则结果:q 指向a5;ptr 指向a6,按刚才解释,ptr+的值是a5地址,*(ptr+)是求 a5 的值,为 50该运算相当于“av+”。若有u=*(ptr+);则结果u的值为50。,*ptr是求ptr所指变量的值,即a5的值,为50;“(*ptr)+”是a5+,最后a5的值为51,而该表达式的值为加 1 之前的值,仍然是50。若有u=(*ptr)+;则结果:u的值为50;a5的值为51。,由于运算符“*”和“+”优先级相同,结合方向是从右向左,因此该表达式相当于前述*(ptr+)它的运算与该表达式完全相同。,与ptr+类似,运算“+ptr”意义是把ptr的值加1,运算结束后ptr指向a6表达式的值是ptr加1之后ptr的值,即a6的地址。若有 q=+ptr;则结果:q 指向 a6;ptr指向 a6。,按刚才的叙述,“+ptr”的值是 a6 的地址,则“*(+ptr)”是求 a6的值,得 60。该运算相当于“a+v”。若有u=*(+ptr);则结果 u 的值为60。,由于运算符“*”和“+”优先级相同,结合方向是从右向左,因此该表达式相当于*(+ptr)有关它的运算与完全一致,最后 u 的值为 60。,7.3.2 多维数组与指针,1.二维数组元素的地址二维数组 amn 可以看作是由 m 个一维数组 a0、a1、am-2、am-1构成。每个一维数组有 n 个元素,即每个ai都是由 n 个变量ai0、ai1、ain-1组成的数组,如图所示,按数组与指针的关系:从一维角度看,a表示一维数组的首地址,该一维数组的数组元素仍然是数组,则:a=&a0,a+i=&ai每个ai也都表示一维数组的首地址,该一维数组是a数组的第i行,它的各个元素是int类型,则:a0=&a00,ai=&ai0*(a)=a0,*(a0)=a00,则*(*(a)=a00,实际上,a、a0、a1、am-1 是数组的名字,a、a0、a1、a2、am-1都是指针常量,C 系统不给它们分配内存,只分配 m*n 个变量的内存空间。下图是一个示意图,给出的a、a0、a1、am-1只是一个示意,读者不要误会。,进一步,ai是a数组第i个元素。如果a是一维数组,则ai是一个变量并且可以有值,C系统会给它分配相应的存储空间,它实实在在占用计算机内存如果a是二维数组,则ai代表一维数组,它是一个指针常量,C系统不给它分配存储区,它不占用内存空间,仅仅是一个地址。,指针常量,指针常量,注意:在该表的各种表示形式中:&(aij)、ai+j、*(a+i)+j 是实际计算机内存的物理地址,不占用计算机内存aij、*(ai+j)、*(*(a+i)+j)是它们各自成分的值,占用计算机内存 其它形式都是表示地址的指针常量,没有被分配具体内存空间。例如,并不存在 ai 这样一个实际的变量,它只是一个指针常量,运算“&(ai)”只是一种地址计算方式而已,并不是求内存实际存在的变量ai的内存地址。,2.指向二维数组元素的指针变量,二维数组的存储方式用指针变量表示二维数组的元素,C数组的存储分配方式,在C中,按“行优先”原则分配数组元素的存储空间。即对数组amn来说,它的各个成分被分配的内存空间的顺序是:首先分配第0行元素;然后分配第1行元素;最后再分配第n-1行元素,如此等等。每行元素按下标值从小到大进行。,int a34;假设从首地址A000开始分配内存空间每个元素占2个字节数组a的存储分配图,使用成分类型指针访问两维数组元素,设有声明:int*aptr,amn,x;则可以直接用aptr访问a的成分。使用方法是:首先使aptr指向a的某个成分aij,aptr=最常用的地址基点是a数组的第一个成分a00 aptr=&(a00)或 aptr=a0 然后以该成分的地址为基点,计算所要访问的数组成分的相对地址,并进行访问,在上述赋值运算的前提下,a的成分auv的地址为 aptr+u*n+v若把auv的值送入变量x中,可以使用赋值运算:x=*(aptr+u*n+v)等价于 x=auv若把某表达式e的值送入数组a的成分auv中,可以使用赋值运算:*(aptr+u*n+v)=e 等价于auv=e,地址计算公式“aptr+u*n+v”基点+行数*每行元素个数+剩余行的零头元素个数例如,a21的地址是:aptr+2*4+1,若再考虑每个元素占用的内存尺寸,2*4+1 还要乘以 int 类型一个变量占用内存空间数 2,最后 a21 对应的具体内存地址是:A000+(2*4+1)*2为A012。,注意:程序中只要写地址计算公式“aptr+u*n+v”即可没有必要也不允许考虑每个元素占用的存储空间尺寸没有必要,也不允许,更不可能写出具体的地址计算计算算式“A000+(2*4+1)*2”。,例7-7 编函数,求m*n个元素的给定float型数组各 个元素之乘积。,float arrmul(int m,int n,float*arr)int u,v;float mul;mul=1;for(u=0;um;u+)for(v=0;vn;v+)mul=mul*(*(arr+u*n+v);return mul;,设有声明float a1015;则可以用如下任何一种形式调用该函数:arrmul(10,15,&a00)arrmul(10,15,a0)arrmul(10,15,*a),在本例中,形式参数是一个指向float类型的指针变量,实在参数把a数组第一个元素a00的指针(地址)送入形式参数arr之中。&(a00)、a0、*a都是a00的地址。实际上本例是把二维数组作为一维数组对待的,地址计算是采用一维方式进行的。,7.3.3 指针数组,一维指针数组的声明形式是T*pn其中T 是类型p 是标识符,是被声明的数组变量n 是数组尺寸,运算符“*”的优先级低于运算符“”,该声明相当于声明T*(pn)按此格式,“pn”表示p是n个元素的数组,数组元素是“T*”类型,即指向T类型变量的指针类型。所以p是n个元素的指针数组,数组成分是指向T类型变量的指针。也可以认为p本身是一个指针常量,指向n个元素的指针数组。这个声明形式,声明了一个指针数组p,如图所示。,指针数组是由指针构成的数组,数组中每个元素都是指针。下例定义了指针数组p,p包含5个元素,其中每个元素都是int型指针。int*p5;则有:p=&p0,例7-8 指向指针的指针、指针数组与数组的关系void main()int a5=23,24,25,26,27;int*n5,i;int*p=n;for(i=0;i5;i+)ni=i+,p+)printf(“%4d”,*p);程序运行结果将输出:23 24 25 26 27,“指向指针的指针变量p”“指针数组n”与“数组a”关系图,注:int*p=对否,注:int*p=n;或 int*p=对否,【例7-9】二维数组、指针数组和指针间的关系int a44=1,2,3,4,11,12,13,14,21,22,23,24,31,32,33,34;int*pa4=a0,a1,a2,a3;int*p=a0,i;void main()for(i=0;i3;i+)printf(“%d%d%dn”,ai2+i,*(ai),*(*(a+i)+i);for(i=0;i4;i+)printf(“%d%d n”,*(pai),pi);,程序运行结果将输出:3 1 114 11 1231 21231 111 221 331 4,a、pa、p之间关系图,注:int*p=对否,例7-10 把若干给定的字符串按字母 顺序排序并输出,void sort_string(char*arr_str,int n);void out_string(char*arr_str,int n);int str_cmp(char*str1,char*str2);char*name=basic,programming“,great wall,language,computer;void main()sort_string(name,5);out_string(name,5);,该程序首先调用sort_string函数对name数组排序;然后调用out_string函数输出name。程序声明部分产生如下图的数据结构:,经过sort_string排序后数据结构如下,运行结束产生如下输出:basiccomputergreat walllanguage programming,常量字符串保存在内存的常量区,void sort_string(char*arr_str,int n)char*temp;int i,j,k;for(i=0;i 0)k=j;temp=arr_stri;arr_stri=arr_strk;arr_strk=temp;,void out_string(char*arr_str,int n)int j;for(j=0;jn;j+)printf(%sn,arr_strj);,本程序没有使用标准函数比较两字符串大小,自编函数int str_cmp(char*str1,char*str2)do if(*str1*str2)/比较当前字符 return 1;else if(*str1*str2)return-1;while(*(str1+)!=0,7.3.4 指针与数组总结,数组名(数组变量)本身就是一个指针,它是一个常量指针指针数组和指向数组的指针例子int w5;int*pw=w;/pw表示数组w 或者说指向变量w0int*q=/q指向变量w2,1.指针与数组的关系:,int y,*qr5;/qr是指针数组,有5个元素,/每个元素是一个指向int类变量的指针 qr1=/pr指向一维数组rr0,/该rr0有6个元素,2.注意有关数组声明的几种形式以及它们的区别T anT*pnT*(pn)T(*p)n其中T是类型符,设 T 为 int,n=5,下面说明这几种形式的意义。T an声明“int a5”表示a是5个元素的int类型数组,如图所示。,T*pn“int*p5”相当于“int*(p5)”。“p5”表示p是5个元素的数组,元素类型为int*,即指向int类型变量的指针类型。这个声明,声明了一个指针数组p,如图所示。下标变量p2是数组p的编号为2(第三个)的成分,它是一个指针,可以指向一个int类型变量。,T*(pn)声明“int*(p5)”与“int*p5”等价。p是5个元素的指针数组,p本身是一个指针常量。,T(*p)n声明“int(*p)5”表示(*p)是5个元素的int类型数组,即p是“指向5个元素的int类型数组”的指针变量,P是一个指针类型变量,p可以指向5个元素的int类型数组。注该声明是一个指向数组的指针变量p。事实上,可省略元素个数5,写成“int(*p)”,表示p是“指向int类型数组”的指针变量,如图所示。p:int类型数组(目前还不存在),7.4 指针与字符串,字符串实质上是常量字符数组,同时还可以使用字符数组变量保存字符串。指针与数组有着密切的关系,数组名是指针,指针也可以指向数组。显然指针与字符串有着密切的关系。在C中,除了用字符数组保存并标识字符串外,还可以使用字符类型指针指向一个字符串,从而使用指针访问它所指的字符串。该字符串既可能是字符数组,也可能是字符串常量。,char*sp=“I love china”,*sv;char string=“I am a student”;定义两个字符指针变量sp、sv 以及一个字符数组变量string;并对变量作初始化,使sp指向字符串“I love china”,而string初始化为“I am a student”。设变量从A000开始分配内存空间,常量从AA00开始分配内存空间,这个声明产生下图的内存分配结果。,编译系统为字符类型指针变量sp、sv分配指针类型空间;为字符串数组string分配字符型空间,并初始化,把字符串“I am a student”保存在相应数组string的内存空间中。为了保存字符串常量“I love china”编译系统在常量区给它开辟存储空间,这块存储空间的结构与字符数组相同;并初始化字符类型指针变量sp,使它指向字符串“I love china”。,字符串“I love china”是常量字符串,只有通过指向它的指针变量才能访问它,它本身没有名字,sp绝对不是它的名字。目前sp指向它,当然也可以用其它指针变量指向它,sp也可以用于其它用途。例如,下述赋值运算sv=sp;sp=string;使得sv指向字符串“I love china”而sp又去指向另一个字符串。,使用字符数组sp可以访问相应字符串,也可以参与一切与字符串有关的运算。例printf(“%sn”,sp);将打印出I am a student而sp3*(sp+3)的值都是字符“m”。分析字符串指针与字符数组之间的关系。,从字符串角度看:字符串指针是一个指针类型变量,被分配指针类型存储空间,它虽然可以指向一个字符串,但是字符串指针变量的存储空间不能保存一个字符串。比如sp地址是A000,占用指针类型空间A000A003;sv地址是A004,占用指针类型空间A004A007。开始sp指向字符串“I love china”sv无值。字符数组是一个数组类型变量,被分配数组类型存储空间,该空间可以保存一个字符串。比如string地址是A008,占用空间A008A016。数组string保存字符串“I am a student”,从指针角度看:字符串指针名字是一个指针变量,它可以指向任何字符串。可以把任意字符串赋值给字符串指针,让字符串指针变量指向相应字符串,但是决不是字符串指针变量本身存储了整个字符串。比如,sp是字符串指针变量,变量声明初始化时它指向常量字符串“I love china”后来经过赋值运算,它又指向字符串“I am a student”还可以让字符串指针变量指向任意一个字符串。下述运算都是正确的。sp=string;sp=“I am a teacher”;,字符数组名字是一个指针常量,它只能指向分配给它的那块内存空间,不能让它指向其它字符串或数组。也不能用一个赋值运算把整个字符串赋值给一个字符数组。比如string地址是A008,占用空间A008A016。初始化时,数组string保存字符串“I am a student”如果作运算string=sp或string=“I am a teacher”都是错误的。当然数组的内容是可改变的。,AA00,A008,从使用角度看:用字符串指针或字符数组,访问字符串,使用时十分小心,有些操作是正确的,有些操作是错误的。举例如下,请从中深刻体会字符串指针和字符数组的各种用法。,char str20=“I am a teacher”;/正确,初始化数组str char str020,str20=“I am a teacher”;str0=str;/错误,给指针常量赋值,数组不能整体赋值 char str20;str=“I am a teacher”;/错误,给指针常量赋值,数组不能整体赋值 char str20;str=“I am a teacher”;/错误,给指针常量赋值;/“str”出现在“=”左端,意义不明确。,char*str;str=“I am a teacher”;/正确,给指针变量赋值,str指向常量字符串 char*str,str020;str=str0;scanf(“%s”,str);/正确,输入字符串数据从str00开始存放 char*str,str020;str=str0+5;scanf(“%s”,str);/正确,输入数据将从str05开始存放 char*str;scanf(“%s”,str);/错误,str不指向任何变量,输入字符串无处放 char str20;scanf(“%s”,str);/正确,输入字符串,char str20=“I am a teacher”;printf(“%s”,str);/正确,打印:I am a teacher char*str=“I am a teacher”;printf(“%s”,str);/正确,打印:I am a teacher char*str=“I am a teacher”;str=str+5;printf(“%s”,str);/正确,打印:a teacher char*str=“x=%d y=%fn”;printf(str,x,y);/正确,相当于:printf(“x=%d y=%fn”,x,y);,字符串指针或字符数组,可用相同的方法访问其分量。设有说明char*sp=“I love china”,sv20,*p,*q;int v;如下三个语句列功能相同,都是把 sp 所指字符串复制到字符数组 sv 之中。v=-1;do v+;svv=spv;while(spv!=0);第一个语句列完全使用数组和下标表达式形式访问每个成分,v=-1;do v+;*(sv+v)=*(sp+v);while(*(sp+v)!=0);第二个语句列使用指针法访问每个成分,指针变量始终指向字符串和字符数组的首位。在赋值时,先把指针值加一个整数,然后用间接寻址达到赋值目的。,p=sp-1;q=sv-1;do p+;q+;*q=*p;while(*p!=0);第三个语句列使用两个指针变量 p、q 间接访问每个成分,开始 p、q 分别指向源字符串和目标字符数组首位,在循环过程中 p、q 不断移动,也使用间接寻址达到赋值目的。,7.5 指向指针的指针,指向指针的指针:一个指针变量指向的变量仍然是一个指针变量,就构成指向指针变量的指针变量。如图所示,变量 p 指向一个变量,该变量仍然是指针类型的,它指向一个int类型变量。,P的声明形式是:类型符*p如下程序片段构造下图结构。int*p,*s,v;p=其中声明符“*p”声明了指向指针变量的指针变量p。,使用p访问v内容的形式是*p这个间接寻址运算的意义是:p 是指针变量,它的值是指向“指向int类型的指针变量”*p 取上述p的内容,得到一个指针值,该指针值“指向int类型变量”*p 即“*(*p)”再取上述“*p”的内容,得到一个int类型的值。,上述给 v 赋值的语句 v=300;可以使用指针变量s实现,也可以使用指向指针的指针变量p实现,下述三个语句等价。v=300;*s=300;*p=300;,使用s访问v内容的形式是*s,指向指针的指针在实际程序中有很大用处,程序的命令行参数使用指向指针的指针。经常使用指针数组实现指向指针的指针。设有程序片段char c1=“copy”,c2=“jilin.dat”;char c3=changchun.dat,c4=beijing;char*aptr4,*ptr1,*ptr2;ptr1=aptr;*ptr1=c1;*(ptr1+1)=c2;aptr2=c3;ptr13=c4;ptr2=,下图描述了上述程序片段产生的结果,数组aptr是一个指针数组,它的每个元素值是一个指针,指向一个char型变量,数组名aptr是一个指向指针的指针(指针常量);变量ptr1,ptr2 也是指向指针的指针(指针变量);,如下操作都访问c3的第8个元素,得到字符值“u”。c37、*(c3+7)aptr27、ptr127、ptr227*(aptr2+7)、*(ptr12+7)、*(ptr22+7)(*(aptr+2)7、(*(ptr1+2)7、(*(ptr2+2)7*(*(aptr+2)+7)、*(*(ptr1+2)+7)、*(*(ptr2+2)+7),7.6 命令行参数,到目前为止,我们编的一切程序都没有涉及与操作系统之间的联系。考虑问题:编程序实现操作系统的copy命令。命令格式是:copy 文件名1 文件名2copy命令把一个文件的内容拷贝到另一个文件中,当然要针对一切可能的文件名。比如:copy jilin.dat changchun.dat把文件jilin.dat的内容复制到文件changchun.dat中,问题是如何把两个文件名传入我们的程序。使用命令行参数可以解决该问题。,C程序从主函数main开始执行,当操作系统启动C程序执行时,一定把三个信息传入程序。这三个信息是 命令行中字符串个数 命令行中每个字符串内容 表示程序运行环境的各个字符串内容 第一个信息是一个整数;第二个信息由一个个字符串组成,用一个指针数组 保存这些字符串的首指针,操作系统传递给程序的 是相应指针数组的首地址 第三个信息不常用,我们不涉及它。,为了与操作系统联系,传递上述信息,一般 main 函数有两个形式参数:第一个对应操作系统传递过来的“命令行中字符串 个数”,经常用argc表示;第二个对应操作系统传递过来的“指针数组的首地 址”,经常用argv表示。带参数的main函数的常用形式为:main(int argc,char*argv)int型参数argc标识数组argv的元素个数,字符指针数组argv的各个元素分别指向命令行中可执行文件名和各个参数的字符串,argv0总是指向可执行文件名。,显然,argv是指向指针数组的指针变量;argv1是字符型指针变量,指向字符串“jilin.dat”;而argv14是字符类型变量,值是字符“n”。,带参数的主程序非常有用,几乎所有实用程序都涉及程序参数。C程序在DOS操作系统下,执行命令的形式是:可执行文件名 参数1 参数2 参数n,命令行C:copy jilin.dat changchun.dat产生如下信息。,例7-11 编程序输出命令行的参数内容,#include void main(int argc,char*argv)printf(argc=%dn,argc);printf(command name:%sn,argv0);for(int i=1;iargc;i+)printf(Argument%d:%s n,i,argvi);,如果执行该程序,在DOS下键入命令行:C tt se.txt hope ee efe,程序运行结果为:argc=5commard name:ttArgument 1:se.txtArgument 2:hopeArgument 3:eeArgument 4:efe,本章小结,本章主要介绍指针的概念与操作,并对指针与数组关系进行详细介绍。重点掌握指针变量与指针所指变量之间的关系,指针与数组之间的关系。只有掌握好这两种关系才能够正确理解指针概念以及操作,从而正确使用指针。,