C语言程序设计实用教程第8章.ppt
2023/11/7,语言程序设计教程,1,8.1 指针与指针变量8.2 指向数组的指针变量8.3 指向字符串的指针变量8.4 指针数组与命令行参数8.5 指针与函数8.6 指针小结,第 8 章 指针,2023/11/7,语言程序设计教程,2,教学提示“指针”是C语言中广泛使用的一种数据类型,运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构,实现动态内存分配,不但能很方便地使用数组和字符串,还能像汇编语言一样处理内存地址,从而编出精练而高效的程序。但是,由于指针概念较复杂,使用较灵活,初学者常常感到较难理解,因此,学习时必须从指针的概念入手,正确理解指针及指针在数组和函数方面的应用。教学目标 要求学生掌握指针的概念和运算规则,掌握用指针访问变量、一维数组和二维数组的方法,以及用指针处理字符串的方法。通过多编程、多上机调试程序来体会指针的概念及其使用的规律,并应用于实际的编程中。,2023/11/7,语言程序设计教程,3,8.1 指针与指针变量,8.1.1 指针的概念1.内存与内存地址 内存地址:内存是计算机用于存储数据的存储器,以一个字节作为存储单元,为了便于访问,给每个字节单元一个唯一的编号,第一字节单元编号为0,以后各单元按顺序连续编号,这些单元编号称为内存单元的地址。变量地址:变量所分配存储空间的首字节单元地址(字节单元编号)。,2023/11/7,语言程序设计教程,4,2、变量、变量名、变量的地址、变量值 每个变量都通过变量名与相应的存储单元相连系,具体分配哪些单元给变量,由编译系统完成变量名到对应内存单元地址的变换。变量分配存储空间的大小由类型决定。变量的值则是指相应存储单元的内容。,2023/11/7,语言程序设计教程,5,3、指针、变量的指针和指针变量 指针:就是“内存单元的地址”。指针指向一个内存单元。变量的指针:就是“变量的地址”。变量的指针指向一个变量对应的内存单元。指针变量:就是地址变量。地址(指针)也是数据,可以保存在一个变量中。保存地址(指针)数据的变量称为指针变量。,2023/11/7,语言程序设计教程,6,系统为特殊变量p(用来存放地址的)分配的存储空间地址是4800,p中保存的是变量a的地址,即4000,当要读取a变量的值12345时,不是直接通过a变量,也不是直接通过保存12345的内存单元的地址4000去取值,而是先通过变量p得到p的值4000,即a的地址,再根据地址4000读取它所指向单元的值12345。,这种间接的通过变量p得到变量a的地址,再存取变量a的值的方式即为“间接存取”。通常称变量p指向变量a,变量a是变量p所指向的对象,2023/11/7,语言程序设计教程,7,4、变量值的存取通过变量在内存中的地址进行存取,系统执行“scanf(”%d“,”时,存取变量a值的方式可以有两种:直接访问直接利用变量的地址进行存取。1)上例中scanf(“%d”,&a)的执行过程是这样的:通过变量名a找到变量a的起始地址3000;然后将键盘输入的值(假设为)送到内存单元3000和3001中。此时,变量a在内存中的地址和值,如图8.4所示。,2023/11/7,语言程序设计教程,8,间接访问通过另一变量访问该变量的值。语言规定:在程序中可以定义一种特殊的变量(称为指针变量),用来存放变量的地址。,例如上例定义了这样一个指针变量p,它被分配到4000、4001单元,其值可通过赋值语句“p=a;”得到。此时,指针变量p的值就是变量a在内存中的起始地址3000,如图8.4所示。通过指针变量p存取变量a值的过程如下:,2023/11/7,语言程序设计教程,9,2023/11/7,语言程序设计教程,10,5.指针的概念,在语言中,用指针来表示一个变量指向另一个变量这样的指向关系。所谓指针即地址。一个变量的指针即该变量的地址,如4000就是指向变量a的指针。指针变量:专门存放地址的变量,如p即是一个指针变量,它存放的是a的地址4000。,2023/11/7,语言程序设计教程,11,8.1.2 指针变量的定义与初始化1.指针变量的定义 基类型*指针变量名;例:float*p1;(定义p1为指向实型变量的指针变量)char*p2;(定义p2为指向字符型变量的指针变量)在指针变量定义中,*是一个说明符,它表明其后的变量是指针变量,如p是指针变量,而不要认为“*p”是指针变量。指针变量定义时指定的数据类型不是指针变量本身的数据类型,而是指针变量所指向的对象(或称目标)的数据类型 指针变量存放的是所指向的某个变量的地址值,而普通变量保存的是该变量本身的值 指针变量并不固定指向一个变量,可指向同类型的不同变量,2023/11/7,语言程序设计教程,12,(1)指针运算符与地址运算符 与指针引用有关的两个运算符:&与*&:取地址运算符*:指针运算符,或称指向运算符、间接访问运算 符。指针指向的对象的表示形式:*指针变量此处*是访问指针所指对象的运算符,与指针定义时的*不同。,2.指针变量初始化,2023/11/7,语言程序设计教程,13,(2)指针变量初始化,若有定义:int a,*p;语句仅仅定义了指针变量p,但指针变量并未指向确定的变量(或内存单元)。因为这些指针变量还没有赋给确定的地址值,只有将某一具体变量的地址赋给指针变量之后,指针变量才指向确定的变量(内存单元)。指针变量初始化:在定义指针时同时给指针一个初始值如:int a,*p=,2023/11/7,语言程序设计教程,14,8.1.3 指针变量的引用,与指针引用有关的两个运算符:(1)&:取地址运算符(2)*:指针运算符(或称间接访问运算符),2023/11/7,语言程序设计教程,15,有定义:int a,*p=注意:*与&具有相同的优先级,结合方向从右到左。这样,&*p即&(*p),是对变量*p取地址,它与&a等价;p与&(*p)等价,a与*(&a)等价。,2023/11/7,语言程序设计教程,16,8.1.4 指针运算,1.指针的赋值运算(1)将变量地址值赋给指针变量,使指针指向该变量。设有如下定义:int a,b,*pa,*pb;float*pf;第一行定义了整型变量a,b及指针变量pa,pb。pa、pb还没有被赋值,因此pa、pb没有指向任何变量,下面语句完成对pa,pb的赋值:pa=,2023/11/7,语言程序设计教程,17,例如:int j,k;int*pointer1,*pointer2;pointer1=,2023/11/7,语言程序设计教程,18,(2)相同类型的指针变量间的赋值,pa与pb都是整型指针变量,它们间可以相互赋值,如:pb=pa;即 pa,pb都指向变量a,此时a、*pa、*pb是等价的。指针指向变化如下图:,注意:只有相同类型的指针变量才能相互赋值,如pf=pa;是不允许的。因为pa是整型指针,pf是浮点型指针。,2023/11/7,语言程序设计教程,19,()给指针变量赋空值,给指针变量赋空值,说明该指针不指向任何变量。“空”指针值用NULL表示,NULL是在头文件stdio.h中预定义的常量,其值为,在使用时应加上预定义行,如:#include stdio.h int*pa=NULL;亦可以用下面的语句给指针赋“空值”:pa=0;或:pa=0;这里指针pa并非指向0地址单元,而是具有一个确定的“空值”,表示pa不指向任何变量。注意:指针虽然可以赋值0,但却不能把其它的常量地址赋给指针。例如:pa=4000;是非法的。,2023/11/7,语言程序设计教程,20,例 8.1 指针定义与初始化,main()int a,b;int*pointer_1,*pointer_2;a=100;b=10;pointer_1=,2023/11/7,语言程序设计教程,21,程序运行结果:100,10100,10,2023/11/7,语言程序设计教程,22,例8.2 从键盘上输入两个整数到a、b,按由大到小输出。,#include main()int a,b,*pa=/*pa指向大数,pb指向小数*/,2023/11/7,语言程序设计教程,23,若输入:12 22输出结果:a=12,b=22 max=22,min=12,(b)(c)指针变化示意图,2023/11/7,语言程序设计教程,24,2.指针的算术运算,(1)加减运算:一个指针可以加、减一个整数n,其结果与指针所指对象的数据类型有关。指针变量的值应增加或减少“nsizeof(指针类型)”。加减运算常用于数组的处理。对指向一般数据的指针,加减运算无实际意义。例如:int a10,*p=a,*x;x=p+3;/*实际上是p加上3*2个字节赋给x,x指向数组的第三个分量*/对于不同基类型的指针,指针变量“加上”或“减去”一个整数n所移动的字节数是不同的。例如:float a10,*p=a,*x;p=p+3;/*实际上是p加上3*4个字节赋给x,x依然指向数组的第三个分量*/,2023/11/7,语言程序设计教程,25,(2)自增自减运算,指针变量自增、自减运算具有上述运算的特点,但有前置后置、先用后用的考虑,务请小心。例如:int a10,*p=a,*x;x=p+;/*x第一个元素分量,p指向第二个元素*/x=+p;/*x、p均指向数组的第二个分量*/*p+相当于*(p+)。*(p+)与(*p)+含义不同,前者表示地址自增,后者表示当前所指向的数据自增。,2023/11/7,语言程序设计教程,26,2.*&a的含意是什么?(答:a)3.(*p)+相当于什么?(答:a+),思考:1.若有定义 int a,*p;执行了“p=&a”,则:“&*p”的含意是什么?,(答:相当于&a),2023/11/7,语言程序设计教程,27,3.指针的关系运算,与基本类型变量一样,指针可以进行关系运算。在关系表达式中允许对两个指针进行所有的关系运算。若p,q是两个同类型的指针变量,则:pq,p=q都是允许的。指针的关系运算在指向数组的指针中广泛的运用,假设 p、q是指向同一数组的两个指针,执行pq的运算,其含义为,若表达式结果为真(非值),则说明p所指元素在q所指元素之后。或者说q所指元素离数组第一个元素更近些。注意:在指针进行关系运算之前,指针必须指向确定的变量或存储区域,即指针有初始值;另外,只有相同类型的指针才能进行比较。,2023/11/7,语言程序设计教程,28,8.2 指向数组的指针,数组名代表了数组的首地址(即第一个数组元素的地址)。根据指针的概念,数组的指针是指数组在内存中的起始地址,而数组元素的指针是指数组元素在内存中的地址。由于数组的各元素在内存中是连续存放的,所以利用指向数组或数组元素的指针变量来使用数组,将更加灵活、快捷。,2023/11/7,语言程序设计教程,29,8.2.1 指向一维数组的指针,数组名是一个常量指针,它的值为该数组的首地址 1.指向数组的指针的定义方法与指向基本类型变量的指针的定义方法相同,例如:int a10=1,3,5,7,9,11,13,15,17,19;int*p;p=(把数组的首地址赋给指针变量p),p=把a0元素的地址赋给指针变量p。也就是说,p指向a数组的第一个元素。,2023/11/7,语言程序设计教程,30,2023/11/7,语言程序设计教程,31,C语言规定:数组名代表数组首地址,是一个地址常量。因此,下面两个语句等价:p=两句。,2023/11/7,语言程序设计教程,32,指向数组的指针变量p,a+0,p+1 或 a+1,p+9 或 a+9,*(a+9)或*(p+9),2023/11/7,语言程序设计教程,33,8.2.2.通过指针引用数组元素*p=5;表示对p当前所指的数组元素赋以一个值5。C规定:p+1指向数组的下一元素(而不是将p值简单地加1)。p+1意味着使p的原值(地址)加d个字节(d为一个数组元素所占的字节数)。,如果p的初值为&a0,则:(1)p+i和a+i就是ai的地址,或者说它们指向a数组的第i个元素(见下页图)。(2)*(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即ai。(3)指向数组的指针变量也可以带下标,如pi与*(p+i)、ai等价。,2023/11/7,语言程序设计教程,34,综上所述,引用一个数组元素有二法:(1)下标法:如ai形式;(2)指针法:如*(a+i)或*(p+i)。其中a是数组名,p是指向数组的指针变量,其初值p=a。,2023/11/7,语言程序设计教程,35,main()int a10;int i;for(i=0;i10;i+)scanf(%d,例8.5 用三种方法输出数组全部元素。(1)下标法,2023/11/7,语言程序设计教程,36,main()int a10;int i;for(i=0;i10;i+)scanf(%d,(2)通过数组名计算数组元素地址,输出元素的值,2023/11/7,语言程序设计教程,37,(3)用指针变量指向数组元素main()int a10;int*p,i;for(i=0;i10;i+)scanf(%d,三种方法的比较:用下标法比较直观,能直接知道是第几个元素;而用指针法则执行效率更高。,2023/11/7,语言程序设计教程,38,使用指针变量时,应注意:(1)指针变量可实现使本身的值改变。P+合法;但a+不合法(a是数组名,代表数组首地址,在程序运行中是固定不变的。)(2)要注意指针变量的当前值。,2023/11/7,语言程序设计教程,39,(3)*p+相当于*(p+),因为*与+优先级相同,且结合方向从右向左,其作用是先获得p指向变量的值,然后执行p=p+1;(4)*(p+)与*(+p)意义不同,后者是先p=p+1,再获得p指向的变量值。若p=a,则输出*(p+)是先输出a0,再让p指向a;输出*(+p)是先使p指向a,再输出p所指的a。(5)(*p)+表示的是将p指向的变量值+,2023/11/7,语言程序设计教程,40,8.2.3用数组名作函数参数,用数组名作函数参数时,由于数组名代表的是数组起始地址,因此传递的值是数组首地址,所以要求形参为指针变量。引入指向数组的指针变量后,数组及指向数组的指针变量作函数参数时,可有种等价形式(本质上是一种,即指针数据作函数参数):(1)形参、实参都用数组名(2)形参、实参都用指针变量(3)形参用指针变量、实参用数组名(4)形参用数组名、实参用指针变量,2023/11/7,语言程序设计教程,41,例8.4 将数组a中n个整数按相反顺序存放。,算法为:将a0与an-1对换,再a1与an-2 对换,直到将a(n-1/2)与an-int(n-1)/2)对换。今用循环处理此问题,设两个“位置指示变量”i和j,i的初值为0,j的初值为n-1。将ai与aj交换,然后使i的值加1,j的值减1,再将ai与aj交换,直到i=(n-1)/2为止,如图所示。,2023/11/7,语言程序设计教程,42,2023/11/7,语言程序设计教程,43,程序如下:void inv(int x,int n)/*形参x是数组名*/int temp,i,j,m=(n-1)/2;for(i=0;i=m;i+)j=n-1-i;temp=xi;xi=xj;xj=temp;return;,2023/11/7,语言程序设计教程,44,main()int i,a10=3,7,9,11,0,6,7,5,4,2;printf(The original array:n);for(i=0;i10;i+)printf(%d,ai);printf(n);inv(a,10);/*实参是数组名*/printf(The array has been inverted:n);for(i=0;i10;i+)printf(%d,ai);printf(n);,2023/11/7,语言程序设计教程,45,对例8.4作一些改动,将函数inv中的形参x改成指针变量。实参为数组名a,即数组a的首地址,将它传给形参指针变量x,这时x就指向a0。x+m是am元素的地址。设i和j以及p都是指针变量,i指向x,j指向x+n-1,使*i与*j交换就是使ai与aj交换。见下例。,2023/11/7,语言程序设计教程,46,程序如下:void inv(int*x,int n)/*形参x为指针变量*/int*p,temp,*i,*j,m=(n-1)/2;i=x;j=x+n-1;p=x+m;for(;i=p;i+,j-)temp=*i;*i=*j;*j=temp;,2023/11/7,语言程序设计教程,47,main()int i,a10=3,7,9,11,0,6,7,5,4,2;printf(The original array:n);for(i=0;i10;i+)printf(%d,ai);printf(n);inv(a,10);/*实参是数组名*/printf(The array has benn inverted:n);for(i=0;i10;i+)printf(%d,ai);printf(n);,运行情况与前一程序相同。,2023/11/7,语言程序设计教程,48,归纳起来,如果有一个实参数组,想在函数中改变此数组的元素的值,实参与形参的对应关系有以下种:形参和实参都是数组名。如:main()int a10;f(a,10),f(int x,int n)这时形参x和实参a共用一段内存单元。,2023/11/7,语言程序设计教程,49,实参用数组,形参用指针变量。如:main()int a10;f(a,10),f(int*x,int n),2023/11/7,语言程序设计教程,50,实参、形参都用指针变量。如:main()int a10,*p=a;f(p,10),f(int*x,int n),2023/11/7,语言程序设计教程,51,实参为指针变量,形参为数组名。如:main()int a10,*p=a;f(p,10),以上四种方法,本质上都是地址的传递。其中(1)(4)两种只是形式上的不同,实际上都是使用指针变量。,f(int x,int n),2023/11/7,语言程序设计教程,52,例8.6 用选择法对10个整数排序。,main()int*p,i,a10=3,7,9,11,0,6,7,5,4,2;printf(The original array:n);for(i=0;i10;i+)printf(%d,ai);printf(n);p=a;sort(p,10);for(p=a;pa+10;p+)printf(%d,*p);printf(n);,2023/11/7,语言程序设计教程,53,sort(int x,int n)int i,j,k,t;for(i=0;ixk)k=j;if(k!=i)t=xi;xi=xk;xk=t;,说明:函数sort用数组名作为形参,也可改为用指针变量,这时函数的首部可以改为:sort(int*x,int n)其他可一律不改。这时将x定义为指针变量,在函数中仍可用xi、xk这样的形式表示数组元素,它就是x+i和x+k所指的数组元素。,2023/11/7,语言程序设计教程,54,8.2.4指向多维数组的指针变量,1.二维数组的地址设有一个二维数 组a,它有三行四列:int a34=0,1,2,3,4,5,6,7,8,9,10,11 数组名a:代表整个二维数组的首地址,也就是第0行的首地址。a+i:代表第i行的首地址。(见下页图),2023/11/7,语言程序设计教程,55,数组名a代表整个二维数组的首地址:,上图 a数组包含三个元素:a0,a1,a2.而每个元素又是一个一维数组,它包含4个元素(即4个列元素),如:a0又包含:a00,a01,a02,a03.,2023/11/7,语言程序设计教程,56,2023/11/7,语言程序设计教程,57,数组及数组元素的地址表示如下:从二维数组的角度来看,a是二维数组名,a代表整个二维数组的首地址,也是二维数组第0行的首地址,等于1000。a+1代表第一行的首地址,等于1008。如图8.11所示:,2023/11/7,语言程序设计教程,58,一维数组名ai:代表第i 行的首地址,即第i行中第0列元素的地址(既&ai0)。ai+j:代表第i行中的第j个元素的地址,即为&aij。注意地址变化的单位数值在不同的场合的实际字节数是不同的:“a+1”中的“1”实际代表数组中一行元素所占的总字节数;“ai+1”中的“1”代表数组中一个元素所占的字节数。,2023/11/7,语言程序设计教程,59,a代表第0行的首地址,a+1代表第1行的首地址,a+2代表第2行的首地址.每行存放4个整型数据(即1个元素占2个字节),因此,这里+1的含义是:+4*2=+8个字节.,2023/11/7,语言程序设计教程,60,注意:(1)从二维数组角度看,数组名a代表数组的起始地址,是一个以行为单位进行控制的行指针:a+i:行指针值,指向二维数组的第i行。*(a+i):(列)指针值,指向第i行第列(控制由行转为列,但仍为指针)。*(*(a+i):数组元素ai0的值。用a作指针访问数组元素aij的格式:*(*(a+i)j)(2)若a是一个一维数组,则ai代表数组的第i个元素,它占有实际的存储单元。,2023/11/7,语言程序设计教程,61,2023/11/7,语言程序设计教程,62,例8.7 用指针表示法输出二维数组的各元素。,#includemain()static int a23=0,1,2,3,4,5;int k,j,*p;for(j=0;j2;j+)/*方式1*/for(k=0;k3;k+)printf(%5d,*(aj+k);/*aj是j行首地址,aj+k是j行k列元素的地址*/putchar(n);,2023/11/7,语言程序设计教程,63,接上页:,for(j=0;j2;j+)/*方式2*/for(k=0;k3;k+)printf(%5d,*(*(a+j)+k);/*(a+j)是j行首地址,*(a+j)+k是j行k列元素的地址*/putchar(n);p=a;/*p指向数组的第一个元素*/for(j=0;j2;j+)/*方式3*/for(k=0;k3;k+)printf(%5d,*(p+);/*输出p所指示的元素*/putchar(n);,2023/11/7,语言程序设计教程,64,输出的结果是:0 3 4 5 0 3 4 5 0 3 4 5,2023/11/7,语言程序设计教程,65,对二维数组,注意区分以下表示的不同含义:a、二维数组名、数组首址是常量指针。a+i、&ai指向第i行(第i行首址)是二级指针。ai、*(a+i)、&ai0指向第i行第0列是一级指针,其地址值与a+i、&ai相同,注意不要认为*(a+i)是a+i指向的对象,因为在二维数组中,a+i是指向行,而不指向具体的单元。(但在一维数组中指向具体单元)。ai+j、*(a+i)+j表示元素aij的地址。*(*(a+i)+j)、*(ai+j)表示元素aij。,2023/11/7,语言程序设计教程,66,2.行指针变量指向由n个元素组成的一维数组的指针变量(1)定义格式 类型(*指针变量)元素个数注意(*指针变量)外的括号不能缺,否则成了指针数组数组的每个元素都是一个指针(本章第5节介绍)。(2)赋值 行指针变量二维数组名;如:int a34;int(*p)4;p=a;,2023/11/7,语言程序设计教程,67,注意:(1)int(*p)4;定义一个指针变量p,p 指向包含4个元素的一维数组。(2)p+i与*(p+i)的区别:p+i是指向第i行的指针(第i行的首地址);*(p+i)是指向第i行第1个元素的地址;两者数值相等,但含义不同:p+i 的增值将以行长为单位,而*(p+i)增值将以元素长度为单位。,2023/11/7,语言程序设计教程,68,即:p+i+1将指向第i行再下一行的首地址,而*(p+i)+1将指向第i行首元素的下一个元素地址。(见下图)设 int a34,(*p)4;p=a;,如果p先指向a0,则p+1不是指向a01,而是指向a1,2023/11/7,语言程序设计教程,69,例8.7 main()static int a34=1,3,5,7,9,11,13,15,17,19,21,23;int(*p)4,i,j;p=a;scanf(i=%d,j=%d,2023/11/7,语言程序设计教程,70,例8.8用指向二维数组的指针变量输出二维数组,并将数组中的最大元素及所在行列号输出。,main()int a34=1,3,5,7,9,11,13,15,17,19,21,23;int i,j,max,*p;int row=0,col=0;p=a0;/*指针p指向数组a的第0行第0列*/max=*p;/*先把第一个元素作为最大值*/for(i=0;i3;i+)printf(“n”);for(j=0;j4;j+)printf(“%5d”,*p);if(max*p)max=*p;row=i;col=j;p+;printf(“n max is:a%2d%2d=%-4d”,row,col,max);,2023/11/7,语言程序设计教程,71,运行结果:1357911131517192123max is:a23=23,2023/11/7,语言程序设计教程,72,例8.9 用行指针变量输出二维数组,并将数组中的最大元素及所在行列号输出。,main()int a34=1,3,5,7,9,11,13,15,17,19,21,23;int i,j,max,row=0,col=0;int(*p)4;/*定义p为指向一个有4个元素的行指针变量*/p=a;/*指针p指向数组a的第0行*/max=*p;/*先把第一个元素作为最大值*/for(i=0;i3;i+)printf(“n”);,2023/11/7,语言程序设计教程,73,for(j=0;j4;j+)printf(“%5d”,*(*p+j);/*(*p+j)相当于*(*(p+0)+j)是第0行第j列的元素*/if(max*(*p+j)max=*(*p+j);row=i;col=j;p+;/*p指向下一行*/printf(“n max is:a%2d%2d=%-4d”,row,col,max);,运行结果与上例相同。,2023/11/7,语言程序设计教程,74,注意:例8.8与例8.9中p+所在的位置,例8.9中是在外循环内,处理完一行后再下移指针,而例8.8中是在内循环中,每处理完一个元素就下移指针。例8.9中使用的语句p=a,由于p是指向一维数组的行指针变量,与a一样都是二级指针,故可以直接赋值。而此处若改为p=a0或p=&a00就不对了。例8.8中的p是指向普通变量的指针,是一个一级指针,只能用p=a0来赋值。请读者仔细分析,以免出错。,2023/11/7,语言程序设计教程,75,8.3指向字符串的指针变量,8.3.1 字符串的指针表示法回顾:字符串可以保存在字符数组中,如。main()static char string=I Love China!;printf(%sn,string);运行时输出:I Love China!,2023/11/7,语言程序设计教程,76,用字符指针指向字符串:字符指针变量的定义:char*指针变量;如:char*p;(1)在定义时初始化指针变量使指针指向一个字符串。main()char*string=“I Love China!”;(初始化string)printf(%sn,string);运行时也输出:I Love China!,2023/11/7,语言程序设计教程,77,char*str=“I love china”,str140;等价于下列两句:char*str;str=“I love china”;以上语句的含义:定义str为指针变量,它指向字符型数据,且赋值语句把字符串“I love china”的首地址赋给了指针变量str。对字符串的整体输出实际上还是从指针所指示的字符开始逐个显示(系统在输出一个字符后自动执行p+),直到遇到字符串结束标志符0为止。而在输入时,亦是将字符串的各字符自动顺序存储在p指示的存储区中,并在最后自动加上 0。,2023/11/7,语言程序设计教程,78,(2)用指针变量来实现对字符串的访问例8.8 将一已知字符串第n个字符开始的剩余字符复制到另一字符串中。,main()int i,n;char a=computer;char b10,*p,*q;p=a;q=b;scanf(%d,/*指针指到要复制的第一个字符*/,2023/11/7,语言程序设计教程,79,for(;*p!=0;p+,q+)*q=*p;*q=0;/*字符串以0 结尾*/printf(String a:%sn,a);printf(String b:%sn,b);,输入:3输出:computer mputer考虑一下,若输出语句改为如下语句会如何?printf(“string a is:%sn”,p);printf(“string b is%sn”,q);,2023/11/7,语言程序设计教程,80,8.3.2 字符串指针作函数参数,将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数或用指向字符串的指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。,2023/11/7,语言程序设计教程,81,例8.15 用函数调用实现字符串的复制。,void copy_string(char from,char to)int i=0;while(fromi!=0)toi=fromi;i+;toi=0;,2023/11/7,语言程序设计教程,82,main()char a=”I am a teacher.”;char b=”you are a student.”;printf(“string a=%snstring b=%sn”,a,b);copy_string(a,b);printf(“nstring a=%snstring b=%sn”,a,b);,运行结果如下:string a=I am a teacher.string b=you are a student.string a=I am a teacher.string b=I am a teacher.,2023/11/7,语言程序设计教程,83,8.3.3 字符串数组字符串数组:是指数组中的每个元素都是一个存放字符串的数组。字符串数组可以用一个二维字符数组来存储。例如:char languge3 10;数组的第一个下标决定字符串的个数,第二个下标是字符串的最大长度(实际最多9个字符,0占一位置)。可以对字符串数组赋初值。例如:char languge3 10;=“Basic”,“c+”,“pascal”,2023/11/7,语言程序设计教程,84,其内存存储情况如图8.14所示:,由于字符数组变量在定义时就确定了大小,每行元素都是固定的,而字符串长度不等,这样会浪费存储空间。使用字符型指针数组可以更方便地处理字符串数组。,2023/11/7,语言程序设计教程,85,8.4 指针数组与命令行参数8.4.1 指针数组 指针数组:是指针变量的集合。即它的每一个元素都是指针变量,且都具有相同的存储类别和指向相同的数据类型。指针数组的定义形式为:类型标识符*数组名数组长度说明;例如:int*p10;由于 比*的优先级高,因此p先与 10结合成p10,而p10正是数组的定义形式,共有10个元素。最后p10与*结合,表示它的各元素可以指向一个整型变量。,2023/11/7,语言程序设计教程,86,指针数组广泛应用于对字符串的处理例如有定义:char*p3;定义了一个具有三个元素p0,p1,p2的指针数组。每个元素都可以指向一个字符数组,或字符串。若利用数组初始化,则:char*p3=“Basic”,“c+”,“pascal”;p0指向字符串“Basic”;p1指向字符串“c+”;p2指向字符串“pascal”;,2023/11/7,语言程序设计教程,87,其存储结构如图8.15所示。p0指向字符串“BASIC”;p1指向字符串“C+”;p2指向字符串“PASCAL”;,2023/11/7,语言程序设计教程,88,例8.9 字符指针数组的赋值#define NULL 0 main()static char a=“Fortran”;static char b=“COBOL”;static char c=“Pascal”;int i;char*p4;p0=a;p1=b;p2=c;p3=NULL;for(i=0;pi!=NULL;i+)printf(“Language%d is%sn”,i+1,pi);,2023/11/7,语言程序设计教程,89,例8.10 有若干本书,将书名按字典顺序排序,#include#includemain()char*bname4=Programming in ANSI C,BASIC,Visual C+6.0 Programming,TRUBO C 2.0;int i;void sort(char*name,int);sort(bname,4);/*排序,改变指针的连接关系*/printf(n);for(i=0;i4;i+)/*输出排序结果*/printf(%8s,bnamei);,2023/11/7,语言程序设计教程,90,void sort(char*name,int n)/*选择排序*/char*t;int i,j,k;/*k记录每趟最小值下标*/for(i=0;i0)k=j;/*第j个元素更小*/if(k!=i)/*最小元素是该趟的第一个元素 则不需交换*/t=namei;namei=namek;namek=t;,输出结果为:BASIC Programming in ANSI C TRUBO C 2.0 Visual C+6.0 Programming,2023/11/7,语言程序设计教程,91,注意:(1)字符数组中每个元素可存放一个字符,而字符指针变量存放字符串首地址,而不是存放在字符指针变量中。(2)对字符数组,与普通数组一样,不能对其进行整体赋值,只能给各个元素赋值,而字符指针变量可以直接用字符串常量赋值。例如,若有如下定义:char a10;char*p;则语句 a=”computer”;是非法的,因为数组名a是一个常量指针,不能对其赋值。只能对各个元素分别赋值:a0=c;a1=o;a2=m;a3=p;a7=r;但语句:p=”computer”;是合法的。,2023/11/7,语言程序设计教程,92,8.4.2 指针数组与命令行参数,在操作系统命令状态下,可以输入程序或命令使其运行,称命令行状态。输入的命令(或运行程序)及该命令(或程序)所需的参数称为命令行参数。如:copy fd fs copy是文件拷贝命令,fd、fs是命令行参数。main函数是可以有参数的,但与普通函数不同。带形参的main()函数的一般形式是:main(int argc,char*argv)形参argc记录命令行中字符串的个数,argv是一个字符型指针数组,每一个元素顺序指向命令行中的一个字符串。,2023/11/7,语言程序设计教程,93,1.main()函数的形参与实参,main()函数由系统自动调用,而不是被程序内部的其它函数调用,main()函数所需的实参不可能由程序内部得到,而是由系统传送。main()函数所需的实参与形参的传递方式也与一般函数的参数传递不同,实参是在命令行与程序名一同输入,程序名和各实际参数之间都用空格分隔。格式为:执行程序名 参数1 参数2 参数n形参argc为命令行中参数的个数(包括执行程序名),其值大于或等于1,而不是象普通C语言函数一样接受第一个实参。形参argv是一个指针数组,其元素依次指向命令行中以空格分开的各字符串。即:第一个指