




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C语言程序设计(下)
第一、二次课4学时第六章指针
第十一次课2学时学习目的1、通过本章的学习使学生掌握指针的意义及使用。2、应知数组的指针的意义,数组元素的指针的意义,及使用数组名作函数参数,应会通过指针引用数组元素。学习重点1.指针的使用2、指针和数组的关系学习难点1.通过指针引用变量的意义2、指针和数组的关系3指针的根本概念指针变量的定义与引用指针与数组
为什么要学习指针?指针是C语言一个重要的概念也是C语言的重要特色。正确灵活的运用指针可以有效地表示负责的数据结构。能动态的分配内存,方便的使用字符串,有效的而方便的使用数组,在调用函数时能获得一个以上的结果,能直接处理内存单元地址等。而这些是设计系统软件非常必要的,掌握指针可以有效的使程序简洁、紧凑、高效。6.1指针的根本概念6.1.1指针的概念1.内存及其地址“程序存储〞就是在程序运行之前将程序和数据存入计算机内存。所以在计算机中,所有的数据都是存放在存储器中的。一般把存储器中的一个字节称为一个内存单元,为了正确地访问这些内存单元,必须为每个内存单元编号。根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。6.1指针的根本概念6.1.1指针的概念
main(){floatx;inty;
…}变量的两个物理意义200020012002200320042005…xy变量的内容变量的地址6.1指针的根本概念6.1.1指针的概念2.变量地址的获取变量的存储单元是在编译时〔对静态存储变量〕或程序运行时〔对动态存储变量〕分配的,因此变量的地址不能人为确定,而要通过取地址运算符&获取。例如在如下的程序段中:inta;floatb;charc;scanf〔"%d%f%c",&a,&b,&c〕;由&a、&b和&c分别得到变量a、b和c的内存地址。值得注意的是,由于常量和表达式没有用户可操作的内存地址,因此&不能作用到常量或表达式上。6.1指针的根本概念6.1.1指针的概念3.指针与指针变量根据内存单元的编号或地址就可以找到所需的内存单元,通常把这个地址称为指针。在C语言中,专门存放变量〔或其它程序实体〕地址的变量是指针变量。指针变量也需要存储单元〔存放地址值的整数〕,它本身也有地址。例如让变量p存放整型变量a的地址〔如下图〕,这样,由变量p的值〔地址,图中为1012〕就可以找到变量a,因此称变量p指向变量a,变量p就是指针变量,它存放的地址就称为“指针〞。因此,指针就是地址。6.1指针的根本概念6.1.1指针的概念4.直接访问方式与间接访问方式有了指针变量以后,对一般变量的访问即可以通过变量名进行,也可以通过指针变量进行。通过变量名或其地址〔如a或&a〕访问变量的方式叫直接访问方式;通过指针变量〔如p〕访问它指向的变量〔如a〕的方式叫间接访问方式。6.2指针变量的定义与引用6.2.1指针变量的定义与初始化指针变量的一般定义形式为:类型名*标识符;其中“标识符〞是指针变量名,标识符前加“*〞号表示该变量是指针变量,用于存放地址,“类型名〞表示该指针变量所指向变量的类型。6.2指针变量的定义与引用6.2.2指针变量的赋值1.通过地址运算“&〞赋值地址运算符“&〞是单目运算符,运算对象放在地址运算符“&〞的右边,用于求出运算对象的地址。通过地址运算“&〞可把一个变量的地址赋给指针变量。2.指针变量的初始化与动态变量的初值一样,在定义了一个〔动态的〕指针变量之后,其初值也是一个不确定的值。可以在定义变量时给指针变量赋初值,如floatf,*p=&f;那么把变量f的地址赋值给指针变量p,此语句相当于floatf,*p;p=&f;这两条语句。6.2指针变量的定义与引用6.2.2指针变量的赋值3.通过其它指针变量赋值可以通过赋值运算符,把一个指针变量的地址值赋给另一个指针变量,这样两个指针变量均指向同一地址。4.用NULL给指针变量赋空值
除了给指针变量赋地址值外,还可以给指针变量赋空值,如
p=NULL;6.2指针变量的定义与引用6.2.3指针的运算及引用1.指针变量的赋值运算在函数的执行局部给指针变量赋地址值有以下几种情况。(1)赋给同类型普通变量求地址运算得到的地址值。如:intk=10,*p,*q;q=&k;这时scanf〔"%d",&k〕;与scanf〔"%d",q〕;作用相同。(2)通过已有地址值的指针变量赋值。(3)通过标准函数获得地址值。(4)给指针变量赋“空〞值,如:p=NULL;这样做的目的是:让指针变量存有确定的地址值又不指向任何变量〔类似于给数值型变量赋初值0〕。6.2指针变量的定义与引用6.2.3指针的运算及引用2.指向运算和指针变量的引用(1)指向运算符**运算符作用在指针〔地址〕上,代表该指针所指向的存储单元〔及其值〕,实现间接访问,因此又叫“间接访问运算符〞。如:inta=5,*p;p=&a;printf〔"%d",*p〕;*p的值为5与a等价。*运算符为单目运算符,与其他的单目运算符具有相同的优先级和结合性〔右结合性〕。根据*运算符的作用,*运算符和取地址运算符&互逆:*〔&a〕==a&〔*p〕==p注意,在定义指针变量时,“*〞表示其后是指针变量;在执行局部的表达式中,“*〞是指向运算符。(2)指针变量的引用知道了指针变量的作用以及相关的运算符以后,我们就可以引用指针变量了。6.2指针变量的定义与引用6.2.3指针的运算及引用【例6-1】请理解以下程序中各语句的含义:#include<stdio.h>voidmain(){inta=10,*p=&a;printf("*p=%d\n",*p);/*打印指针变量p所指向的变量a的值10*/printf("Entera:");scanf("%d",p);/*对指针变量p所指向的变量a的地址读入整数*/printf("a=%d\n",a);printf("p=%x\n",p);/*输出指针变量p存储的变量a的地址*/printf("&p=%x\n",&p);/*输出指针变量p自身的地址*/*p=5;/*把5赋给p所指向的存储单元,相当于a=5;*/printf("a=%d\n",a);(*p)++;/*使指针变量p所指向的存储单元的值自增,相当于a++;*/printf("a=%d\n",a);}程序运行结果:*p=10Entera:15a=15p=13ff7c&p=13ff78a=5a=66.2指针变量的定义与引用6.2.3指针的运算及引用【例6-2】输入两个整数i1和i2,利用指针将大数存放到i1中,小数存放到i2中,最后按i1、i2的顺序输出。按题意,定义两个指针变量p1、p2,将i1、i2的地址分别存入p1、p2,当i1<i2时利用指针变量p1、p2交换i1、i2的值然后输出。程序如下:6.2指针变量的定义与引用6.2.3指针的运算及引用#include<stdio.h>voidmain(){inti1,i2,*p1,*p2,t;p1=&i1;p2=&i2;printf("Entertwonumbers:\n");scanf("%d%d",p1,p2);/*利用指针变量输入i1、i2的值*/if(i1<i2){t=*p1;*p1=*p2;*p2=t;}/*利用指针变量的指向操作交换i1、i2的值*/printf("i1=%d,i2=%d\n",i1,i2);}Entertwonumbers:510程序运行结果:i1=10,i2=56.2指针变量的定义与引用6.2.3指针的运算及引用【思考】如果将变量定义改为inti1,i2,*p1,*p2,*p;交换i1、i2值的语句改为:if(i1<i2){p=p1;p1=p2;p2=p;}或者if(i1<i2){*p=*p1;*p1=*p2;*p2=*p;}将会怎样?【分析】第一种情况是:在i1<i2的情况下,利用临时指针变量p交换指针变量p1、p2存放的地址值,而i1、i2的值没有改变,因此题目的要求没有实现〔如图6-5所示〕。但如果同时将输出语句改为:printf("max=%d,min=%d\n",*p1,*p2);可实现从大到小输出。第二种情况是:在i1<i2的情况下,利用三个指针变量的指向操作交换i1、i2的值。问题是:指针变量p没有存放普通变量的地址,因此也是错误的〔运行时有警告〕。6.2指针变量的定义与引用6.2.3指针的运算及引用【思考】如果将变量定义改为inti1,i2,*p1,*p2,*p;交换i1、i2值的语句改为:if(i1<i2){p=p1;p1=p2;p2=p;}或者if(i1<i2){*p=*p1;*p1=*p2;*p2=*p;}将会怎样?【分析】第一种情况是:在i1<i2的情况下,利用临时指针变量p交换指针变量p1、p2存放的地址值,而i1、i2的值没有改变,因此题目的要求没有实现〔如图6-5所示〕。但如果同时将输出语句改为:printf("max=%d,min=%d\n",*p1,*p2);可实现从大到小输出。第二种情况是:在i1<i2的情况下,利用三个指针变量的指向操作交换i1、i2的值。问题是:指针变量p没有存放普通变量的地址,因此也是错误的〔运行时有警告〕。6.2指针变量的定义与引用6.2.4指针作为函数参数函数的参数可以是我们在前面学过的简单数据类型,也可以是指针类型。使用指针类型做函数的参数,实际向函数传递的是变量的地址。由于函数中获得了所传递变量的地址,在该地址空间的数据当函数调用结束后被物理地保存下来。6.2指针变量的定义与引用6.2.4指针作为函数参数【例6-3】利用指针变量作为函数的参数,用函数的方法再次实现上述功能。#include<stdio.h>voidmain(){voidchang(); /*函数声明*/int *p1,*p2,a,b,*t;scanf("%d,%d",&a,&b);p1=&a;p2=&b;chang(p1,p2); /*调用函数*/printf("%d,%d\n",*p1,*p2);return0;}6.2指针变量的定义与引用6.2.4指针作为函数参数voidchang(int*pt1,int*pt2) /*函数实现将两数值调整为由大到小*/{intt;if(*pt1<*pt2) {t=*pt1;*pt1=*pt2;*pt2=t;}/*交换内存变量的值*/return;}由于在调用函数时,实际参数是指针变量,形式参数也是指针变量,实参与形参相结合,传值调用将指针变量传递给形式参数pt1和pt2。但此时传值传递的是变量地址,使得在函数中pt1和pt2具有了p1和p2的值,指向了与调用程序相同的内存变量,并对其在内存存放的数据进行了交换,其效果与例6-2相同。6.2指针变量的定义与引用6.2.5多级指针的概念按照上述二级指针的思路,显然可以推广到三级指针、四级指针……。使用多级指针变量的要点是:①多级指针变量均用基类型定义,定义几级指针变量要将变量名前放几个“*〞号;②各指针变量均应取得低一级指针变量的地址后才能引用;③引用几级指针变量访问最终的普通变量时,变量名前需用几个指向运算符“*〞号。6.2指针变量的定义与引用6.2.5多级指针的概念【例6-4】运行下面的程序#include<stdio.h>voidmain(){int*p1,**p2,***p3,****p4,x=10;p1=&x;p2=&p1;p3=&p2;p4=&p3;printf〔"x=%d\n",****p4〕;}程序运行结果:x=106.3指针与数组6.3.1指针与一维数组一个数组的元素在内存中是连续存放的,数组第一个元素的地址称数组的首地址。在C语言中,数组名是该数组的首地址。例如有以下定义语句:inta[10],*p;那么语句p=a;和p=&a[0];是等价的,都表示指针变量p指向a数组的首地址。数组首地址的值在C语言中是一个地址常量,是不能改变的。因此,语句a=p;或a++;都是非法的。6.3指针与数组6.3.1指针与一维数组1.通过一维数组名所代表的地址存取数组元素.
假设已定义一维数组a,由上述可知a+i是元素a[i]的地址,根据指针运算符“*〞的运算规那么知*(a+i)与元素a[i]等价。例如,下述程序段:inta[]={1,2,3,4,5,6,7,8,9,10};*(a+5)=50;/*相当于a[5]=50;*/scanf("%d",&a[8]);/*相当于scanf("%d",a+8);*/printf("%d\n",*(a+5));/*相当于printf("%d\n",a[5]))*/6.3指针与数组6.3.1指针与一维数组2.通过指针运算符“*〞存取数组元素设有如下程序段:inta[10],*p;p=a;即p指向a数组的首地址,由上述可知p+i是元素a[i]的地址,根据指针运算符“*〞的运算规那么知*(p+i)与元素a[i]等价。例如,下述程序段:inta[]={1,2,3,4,5,6,7,8,9,10},*p=a;*(p+5)=50;/*相当于a[5]=50;*/scanf("%d",&a[8]);/*相当于scanf("%d",p+8);*/printf("%d\n",*(p+5));/*相当于printf("%d\n",a[5]))*/6.3指针与数组6.3.1指针与一维数组3.通过带下标的指针变量存取数组元素C语言中的下标运算符“[]〞可以构成表达式,假设p为指针变量,i为整型表达式,那么可以把p[i]看成是表达式,首先按p+i计算地址,然后再存取此地址单元中的值。因此p[i]与*(p+i)等价。例如,下述程序段:inta[]={1,2,3,4,5,6,7,8,9,10},*p=a;p[5]=50;/*相当于a[5]=50;*/scanf("%d",&a[8]);/*相当于scanf("%d",&p[8]);*/printf("%d\n",p[5]);/*相当于printf("%d\n",a[5]))*/6.3指针与数组6.3.2指针与二维数组1.二维数组的地址表示法C语言规定,二维数组由一维数组扩展形成,即一维数组的每一个元素作为数组名形成一行数组,各行数组的元素个数相同,是二维数组的列数。例如定义了二维数组inta[3][4],它是由一维数组inta[3]扩展形成,即以a[0]、a[1]、a[2]为数组名〔首地址〕形成三行一维数组,元素个数均为列数4。因此a[0]、a[1]、a[2]为一级指针常量,指向各行的首列〔列指针〕。例如0行的a[0]=&a[0][0]指向0行0列。0行有四个元素,它们是a[0][0]、a[0][1]、a[0][2]、a[0][3]。另外a[0]、a[1]、a[2]又是数组名为a的一维数组的三个元素,首地址a=&a[0]指向的“元素〞为一级指针常量,因此a为二级指针常量,指向0行〔行指针〕。6.3指针与数组6.3.2指针与二维数组【例6-5】输出二维数组的有关值,程序编写如下,注意理解各语句的含义。#include<stdio.h>voidmain(){inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};printf("%u,%u\n",a,*a);/*0行首地址和0行0列首地址*/printf("%u,%u\n",a+1,*a+1);/*1行首地址和0行1列首地址*/printf("%u,%u\n",a,&a[0]);/*0行首地址*/printf("%u,%u,%u\n",*(a+1),a[1],&a[1][0]);/*1行0列首地址*/printf("%d,%d,%d\n",*(*(a+1)+2),*(a[1]+2),a[1][2]);/*1行2列的元素7*/}程序运行结果:1310544,13105441310560,13105481310544,13105441310560,1310560,13105607,7,76.3指针与数组6.3.2指针与二维数组2.用于二维数组的指针变量(1)指向数组元素的指针变量〔一级指针变量〕:将二维数组当成一维数组访问。6.3指针与数组6.3.2指针与二维数组【例6-6】用一级指针变量输出二维数组的全部元素。#include<stdio.h>voidmain(){inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12},i,j,*p;p=a[0];/*指针变量必须得到首元素地址a[0]或*a或&a[0][0]*/for(i=0;i<3;i++)for(j=0;j<4;j++)printf("%3d",*(p+4*i+j));printf("\n");}程序运行结果:
1234567891011126.3指针与数组6.3.2指针与二维数组(2)指向一维数组的指针变量〔行指针变量〕二维数组名〔设为a〕以及a+1、a+2等均为行指针〔二级指针〕常量,分别指向由一行元素组成的行一维数组,但它们不能移动〔例如不能由a++使a得到地址a+1〕。但是如果有定义:inta[3][4],(*prt)[4];prt=a;考虑其中的(*prt)[4],因为()和[]的优先级相同,*prt表示prt应为指针变量,它指向一个含有4个元素的整型一维数组,而不是指向一个元素,因此它是二级指针变量〔行指针变量〕,可以移动。指向一维数组的指针变量的一般定义形式为:类型(*指针变量名)[一维数组元素个数];6.3指针与数组6.3.2指针与二维数组【例6-7】输出二维数组任意行任意列的元素值。定义指向一维数组的指针变量,按照上面的说明表示二维数组任意行任意列的元素,程序如下:#include<stdio.h>voidmain(){inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};int(*p)[4]=a,row,col;printf("Enterarbitrarynumberofrowandcolumn:\n");scanf("%d,%d",&row,&col);printf("a[%d][%d]=%d\n",row,col,*(*(p+row)+col));}程序运行结果:a[2][3]=12Enterarbitrarynumberofrowandcolumn:2,36.3指针与数组6.3.3数组指针作函数的参数【例6-8】调用函数,实现求解一维数组中的最大元素。我们首先假设一维数组中下标为0的元素是最大和用指针变量指向该元素。后续元素与该元素一一比较,假设找到更大的元素,就替换。sub_max()函数的形式参数为一维数组,实际参数是指向一维数组的指针。6.3指针与数组6.3.3数组指针作函数的参数#include<stdio.h>voidmain(){int sub_max(); /*函数声明*/int n,a[10],*ptr=a; /*定义变量,并使指针指向数组*/int max;for(n=0;n<=9;n++) /*输入数据*/scanf("%d",&a[n]);max=sub_max(ptr,10); /*函数调用,其实参是指针*/printf("max=%d\n",max);}intsub_max(b,i)/*函数定义,其形参为数组*/int b[],i; {int temp,j;temp=b[0];for(j=1;j<=9;j++)if(temp<b[j]) temp=b[j];returntemp;}6.3指针与数组6.3.3数组指针作函数的参数程序的main()函数局部,定义数组a共有10个元素,由于将其首地址传给了ptr,那么指针变量ptr就指向了数组,调用sub_max()函数,再将此地址传递给sub_max()函数的形式参数b,这样一来,b数组在内存与a数组具有相同地址,即在内存完全重合。在sub_max()函数中对数组b的操作,与操作数组a意义相同。main()函数完成数据的输入,调用sub_max()函数并输出运行结果。sub_max()函数完成对数组元素找最大的过程。在sub_max()函数内数组元素的表示采用下标法。6.3指针与数组6.3.3数组指针作函数的参数#include<stdio.h>voidmain(){intsub_max();intn,a[10],*ptr=a;intmax;for(n=0;n<=9;n++)scanf("%d",&a[n]);max=sub_max(ptr,10);printf("max=%d\n",max);}intsub_max(b,i) /*形式参数为指针变量*/int *b,i;{inttemp,j;temp=b[0]; /*数组元素指针的下标法表示*/for(j=1;j<=i-1;j++)if(temp<b[j]) temp=b[j];returntemp;}6.3指针与数组6.3.3数组指针作函数的参数在sub_max()中,形式参数是指针,调用程序的实际参数ptr为指向一维数组a的指针,虚实结合,sub_max()的形式参数b得到ptr的值,指向了内存的一维数组。数组元素采用下标法表示,即一维数组的头指针为b,数组元素可以用b[j]表示。程序输入数据:13568912562378程序运行结果:max=896.3指针与数组6.3.3数组指针作函数的参数上述程序的函数中,数组元素还可以用指针表示intsub_max(b,i) /*函数定义*/int *b,i;{int temp,j;temp=*b++;for(j=1;j<=i-1;j++)if(temp<*b) temp=*b++;returntemp;}6.3指针与数组6.3.3数组指针作函数的参数【例6-8】用指向数组的指针变量实现一维数组的由小到大的冒泡排序。编写三个函数用于输入数据、数据排序、数据输出。6.3指针与数组6.3.3数组指针作函数的参数#include<stdio.h>#defineN 10voidmain(){voidinput(); /*函数声明*/voidsort();voidoutput();int a[N],*p; /*定义一维数组和指针变量*/input(a,N); /*数据输入函数调用,实参a是数组名*/p=a; /*指针变量指向数组的首地址*/sort(p,N); /*排序,实参p是指针变量*/output(p,N); /*输出,实参p是指针变量*/ }6.3指针与数组6.3.3数组指针作函数的参数voidinput(arr,n) /*无需返回值的输入数据函数定义,形参arr是数组*/intarr[],n;{int i;printf("inputdata:\n");for(i=0;i<n;i++) /*采用传统的下标法*/scanf("%d",&arr[i]);}voidsort(ptr,n) /*冒泡排序,形参ptr是指针变量*/int*ptr,n;{int i,j,t;for(i=0;i<n-1;i++)for(j=0;j<n-1-i;j++)if(*(ptr+j)>*(ptr+j+1)) /*相临两个元素进行比较*/{t=*(ptr+j); *(ptr+j)=*(ptr+j+1);*(ptr+j+1)=t;}/*两个元素进行交换*/ }6.3指针与数组6.3.3数组指针作函数的参数voidoutput(arr,n) /*数据输出*/intarr[],n;{int i,*ptr=arr; /*利用指针指向数组的首地址*/printf("outputdata:\n");for(;ptr-arr<n;ptr++) /*输出数组的n个元素*/printf("%4d",*ptr);printf("\n");}6.3指针与数组6.3.3数组指针作函数的参数【例6-10】用指向二维数组的指针作函数的参数,实现对二维数组的按行相加。#include<stdio.h>#define M 3#define N 4voidmain(){float a[M][N];float score1,score2,score3,*pa=a[0]; /*指针变量pa指向二维数组*//*score1,score2,score3分别记录三行的数据相加*/int i,j;voidfun();for(i=0;i<M;i++) /*二维数组的数据输入*/for(j=0;j<N;j++) scanf("%f",&a[i][j]);fun(pa,&score1,&score2,&score3);/*函数调用,不仅传递数组首地址,也传递变量的地址*/
6.3指针与数组6.3.3数组指针作函数的参数printf("%.2f,%.2f,%.2f\n",score1,score2,score3);}voidfun(b,p1,p2,p3)floatb[][N],*p1,*p2,*p3;{inti,j;*p1=*p2=*p3=0;for(i=0;i<M;i++)for(j=0;j<N;j++){if(i==0) *p1=*p1+b[i][j]; /*第0行的数据相加*/if(i==1)*p2=*p2+b[i][j]; /*第1行的数据相加*/if(i==2) *p3=*p3+b[i][j]; /*第2行的数据相加*/}}6.3指针与数组6.3.3数组指针作函数的参数程序中与形式参数p1、p2和p3相对应的是实际参数&score1、&score2和&score3,其实际含义为p1=&score1等,即将变量的地址传递给指针变量到达按行相加。程序输入数据:13593456788976654321程序运行结果:18.00,257.00,205.00本次课学习小结1.指针的根本概念2.指针变量的定义与引用3.指针与数组1.地址与指针变量的概念,地址运算符与间址运算符。
2.一维。二维数组和字符串的地址以及指向变量、数组、字符串、函数、结构体的指针变量的定义。通过指针引用以上各类型数据。
3.用指针作函数参数。
4.返回地址值的函数。
5.指针数组,指向指针的指针。本次课等级考试考点练习:等级考试真题一、选择题:〔2007年9月份考题〕〔33〕假设有定义语句:inta[2][3],*p[3];,那么以下语句中正确的选项是
A〕p=a;B)p[0]=a;C)p[0]=&a[1][2];D)p[1]=&a;
答案:C练习:等级考试真题二、填空题:〔2007年4月份考题〕(15)以下程序的功能是:利用指针指向三个整型变量,并通过指针运算找出三个数中的最大值,输出到屏幕上,请填空:
Main()
{intx,y,z,max,*px,*py,*pz,*pmax;
Scanf(“%d%d%d〞,&x,&y,&z);
Px=&x;
Py=&y;
Pz=&z;
Pmax=&max;
__________________
If(*pmax<*py)*pmax=*py;
If(*pmax<*pz)*pmax=*pz;
Printf(“max=%d\n〞,max);
}
*pmax=*px三、填空题:〔2007年9月份考题填空题〕
练习:等级考试真题(11)以下程序的输出结果是________.
#include
#include
char*fun(char*t)
{char*p=t;
return(p+strlen(t)/2);
}
main()
{char*str="abcdefgh";
str=fun(str);
puts(str);
}
efgh练习:本次课作业:习题集:第六章选择题:1、2、3、4填空题:1、2、3、4、5改错题:1、2编程题:1、2第6章指针
6.4
指向字符串的指针变量
6.5指针数组
6.6指针数组作main函数的形参
6.7指向指针的指针变量第十二次课2学时学习目的1.了解字符串的表示形式2.掌握字符串作函数参数和字符指针变量与字符数组的区别3.了解指针数组的一般定义形式4.掌握指针数组的应用5.了解带参数的main函数的一般形式6.掌握命令行参数的应用7.了解指向指针的指针变量的一般定义形式8.掌握指向指针的指针变量的应用
学习重点1.指向字符串的指针变量的定义、赋值和使用2.指针数组3.指向指针的指针变量学习难点1.字符串的指针作函数参数2.指针数组作函数参数
56指针字符串的表示形式
字符串作函数参数
字符指针变量与字符数组的区别
指针数组的一般定义形式
命令行参数的应用指向指针的指针变量的一般定义形式指针指针数组的应用
带参数的main函数的一般形式指向指针的指针变量的应用
6.4
指向字符串的指针变量6.4.1字符串的表示形式字符串是特殊的常量,它一般被存储在一维的字符数组中并且以‘\0’结束。字符串与指针也有着密切的关系。在C语言程序中,可以采用两种方法来实现访问一个字符串:其中一种方法是采用字符数组,另一种方法是采用字符指针。在字符串的处理中,使用字符指针比使用字符数组更方便。
程序运行输出结果:BeijingOlympicsBeijingOlympics〔1〕定义一个字符数组,并且将一个字符串存放在字符数组中,以空字符‘\0’结束。【例6-11】定义一个字符数组,然后通过下标和数组名引用字符或字符串。#include<stdio.h>voidmain(){charstring[]="BeijingOlympics";/*定义字符数组并且初始化*/inti;for(i=0;string[i]!='\0';i++)/*逐个选取字符数组中的所有数组元素*/printf("%c",string[i]);/*通过下标每次输出一个字符*/printf("\n");printf(“%s\n〞,string);/*从数组名string指向的元素开始,输出}字符串到‘\0’为止*/在【例6-11】中string是存放给定字符串的数组名,它代表字符数组第0号数组元素的地址。在C语言中规定,数组名代表数组的首地址,也就是数组中第0号数组元素的地址〔即指向该数组第0号数组元素的指针〕。所以将字符串存储在一个数组中以后,就可以通过该数组名对它进行存取。由于string+i是一个地址,那么*(string+i)表示其内容,它与代表数组元素的string[i]等价。程序运行输出结果:BeijingOlympics〔2〕定义一个字符指针变量,并且将字符指针指向一个字符串常量。【例6-12】定义一个字符指针,然后通过它引用字符串。#include<stdio.h>voidmain(){char*string=“BeijingOlympics〞;/*定义字符指针变量并且指向一个字符串*/printf("%s\n",string);/*输出字符串*/}注意,在对字符指针变量string赋初值为字符串常量时,并不是把整个字符串的内容都赋给该字符指针变量,而仅仅是把该字符串在内存单元的首地址〔即第一个字符的地址〕赋给该字符指针变量,这样就可以将字符指针指向字符串的第一个字符。在C语言中,对字符串常量的存放是按静态字符数组处理的。就是说,在内存中分配给字符数组一片连续的存储单元用来存放该字符串常量。一般情况下,每一个字符占用一个字节的存储单元。在内存中,由于字符串的最后被自动填加了一个‘\0’,所以使用字符指针变量来处理字符串的时候就很容易判断字符串的终止位置。对于使用字符指针变量处理字符串的情况,在输出字符串时要使用“%s〞格式符,输出项中要给出字符指针变量名,这样,计算机就先输出字符指针变量所指向字符串的第一个字符,然后字符指针变量自动加1而指向字符串的下一个字符,接着再输出该字符,…,重复上述操作直到遇到字符串结束标志‘\0’为止。所以,虽然一个数值型数组不能使用数组名来输出该数组的全部元素,而只能逐个元素进行输出;但是使用字符数组名或者字符指针变量却可以整体输出一个字符串。为便于理解,程序可以如下编写:#include<stdio.h>voidmain(){charstring[]="BeijingOlympics";/*定义字符数组并且初始化*/char*p=string;/*定义字符指针变量p并且赋值为字符串首地址string*/printf("%s\n",p);}/*输出字符串*/程序运行输出结果:string1is:Iamatearcher.
string2is:Iamatearcher.【例6-13】使用字符数组名的方法计算数组元素地址,完成字符串的复制。#include<stdio.h>voidmain(){charstring1[]="Iamatearcher.",string2[20];/*定义字符数组并且初始化*/inti;for(i=0;*(string1+i)!='\0';i++)*(string2+i)=*(string1+i);/*将string1数组中的字符串复制到string2数组*/*(string2+i)='\0';printf("string1is:%s\n",string1);printf("string2is:");for(i=0;string2[i]!='\0';i++)printf("%c",string2[i]);/*输出字符串*/}
在【例6-13】for循环中,首先判断string1[i]〔此处以(string1+i)的地址形式表示〕是否为‘\0’。假设不为‘\0’,那么将string1[i]的值赋给string2[i]〔此处以*(string2+i)的地址形式表示〕,完成一个字符的复制。…,重复上述操作,将string1数组中字符串全部都复制给string2数组直到string1[i]〔以*(string1+i)的地址形式表示〕遇到‘\0’为止。最后要将‘\0’复制给string2数组。程序运行输出结果:string1is:Iamatearcher.
string2is:Iamatearcher.【例6-14】使用字符指针变量的方法,完成字符串的复制。#include<stdio.h>voidmain(){charstring1[]="Iamateacher.",string2[20];/*定义字符数组并且初始化*/char*p1,*p2;/*定义字符指针变量*/inti;p1=string1;/*p1指向字符数组ch1的首地址*/p2=string2;for(;*p1!='\0';p1++,p2++)*p2=*p1;/*将p1指向的字符串复制到p2指向的字符串*/*p2='\0';printf("string1is:%s\n",string1);printf("string2is:");for(i=0;string2[i]!='\0';i++)printf("%c",string2[i]);/*输出字符串*/}
在【例6-14】中,首先定义p1和p2是指向字符型数据的指针变量。然后使字符指针变量p1和p2分别指向字符数组string1和string2的首地址。在for循环中,首先判断*p1是否为‘\0’。假设不为‘\0’,那么进行*p2=*p1,它的功能是将数组string1中字符串的第一个字符赋给数组string2中的第一个数组元素。然后再利用p1++和p2++使p1和p2都加1而分别指向各自的下一个数组元素,这样就保证p1和p2同步移动,…,重复上述操作,将string1数组中字符串全部都复制给string2数组,直到*p1的值遇到‘\0’为止。最后,需要将‘\0’复制给*p2。使用地址传递的方法〔即用字符数组名作函数参数或者用指向字符串的指针变量作函数参数〕可以将一个字符串从一个函数传递到另一个函数。【例6-15】实参、形参都用字符数组名作函数参数,完成字符串的连接。#include<stdio.h>voidmain(){voidstring_catenate(charfrom[],charto[]);/*字符串连接函数的原型声明*/charstring1[]="computer";/*定义字符数组并且初始化*/charstring2[]="language";printf("string1=%s\nstring2=%s\n",string1,string2);printf("catenatstring2tostring1:\n");string_catenate(string1,string2);/*调用函数,实参为字符数组*/printf("\nstring1is:%s\n",string1);}
6.4.2字符串作函数参数程序运行输出结果:string1is:computerlanguagevoidstring_catenate(charfrom[],charto[])/*字符串连接函数,形参为字符数组*/{inti=0,j=0;while(from[i]!='\0')i++;/*将指针移动到字符串的尾部*/while(to[j]!='\0'){from[i]=to[j];i++,j++;}/*将to数组中字符串连接到from数组中字符串的尾部*/from[i]='\0';}
【例6-16】实参、形参都用字符指针变量作函数参数,完成字符串的连接。
#include<stdio.h>voidmain(){voidstring_catenate(char*from,char*to);/*字符串连接函数的原型声明*/char*string1="computer";/*定义字符指针变量并且指向一个字符串*/char*string2="language";printf("string1=%s\nstring2=%s\n",string1,string2);printf("catenatstring2tostring1:\n");string_catenate(string1,string2);/*调用函数,实参为字符指针变量*/printf("\nstring1is:%s\n",string1);}程序运行输出结果:string1is:computerlanguage
voidstring_catenate(char*from,char*to)/*字符串连接函数,形参为字符指针变量*/{for(;*from!='\0';from++);/*空循环体,将指针移动到字符串的尾部*/for(;*to!='\0';from++,to++)*from=*to;/*将to指向的字符串连接到from指向的字符串的尾部*/*from='\0';}
数组名、字符指针变量既可以作函数的实参,也可以作函数的形参,归纳起来有如下几种情况。表6-2字符数组名、字符指针变量作函数参数的4种组合实参形参数组名数组名数组名字符指针变量字符指针变量数组名字符指针变量字符指针变量6.4.3字符指针变量与字符数组的区别
使用字符数组和字符指针变量都可以实现字符串的存储和运算,两种方式有相同之处,但也是有区别的。比较的项目字符数组字符指针变量存放的内容由若干个数组元素组成,每个数组元素中存放一个字符存放地址(如字符串中第1个字符的地址)存储空间字符串长度加1,一个字符占用1个字节一般使用2个字节存放比较的项目字符数组字符指针变量初始化可以初始化chara[]="Hi!";可以初始化char*p="Hi!";赋值不能用字符串整体给字符数组赋值,只能对字符数组单个元素赋值能用字符串整体对字符指针赋值(为字符串中第1个字符的地址)char*p;p="Hi!";地址值有确定地址,定义数组后在编译时分配内存单元定义字符指针变量时分配内存单元,但是它没有确定的值,没有指向具体的字符数据比较的项目字符数组字符指针变量可变性数组名为常量,其值不可变字符指针变量为变量,其值可变,可以参加运算运算效率数组元素下标的计算需要转换为指针后计算,如,a[i]要转换为*(a+i),效率较低直接使用指针计算,效率较高
6.5指针数组6.5.1指针数组的一般定义形式由假设干个指向同类型对象的指针数据可以组成一个数组,称为指针数组。其中每个数组元素都是指针变量。就是说,指针数组的所有数组元素都必须是具有相同存储类型和指向相同数据类型的指针变量,指针数组的每个数组元素的值均为指针。指针数组的一般定义形式为: 类型名*数组名[数组大小];例如,int*pa[10];例如,int*pa[10];因为*比[]优先级低,所以pa先要与[10]结合成为pa[10]的数组形式,它有10个数组元素;然后再与前面的int*结合来表示数组元素的类型是指向整型变量的指针,就是说,每个数组元素都可以指向一个整型变量。或者说,pa是一个指针数组,它有10个数组元素,并且每个数组元素的值都是一个指针,都指向整型的变量。请注意,不要把定义指针数组与定义指向含有假设干数组元素的指针变量相混淆。int(*pa)[10];/*表示定义一个指向含有10个数组元素的一维数组的指针变量*/指针数组处理字符串问题〔如排序或查找〕是指针数组的重要应用之一。例如,如果对多个字符串进行排序,一种方法是可以利用二维数组来处理。如chardim[M][N]形式,其中M代表行数〔即多个字符串的个数〕,N代表列数〔即最长的字符串的长度〕。在实际应用中,由于各个字符串的长度通常是不相等的,它们往往都小于N,按照最长的字符串的长度来定义N就会造成该二维数组占用内存单元的存储空间浪费。并且采用一般的排序方法,需要逐个比较字符串以便交换字符串的物理位置〔交换是通过字符串复制函数strcpy完成的〕。屡次的位置交换要消耗大量处理时间又使程序执行速度变慢。例如,以下图表示利用二维数组来处理字符串的时候,按照最长的字符串的长度来定义N会造成该二维数组占用内存单元存储空间的浪费〔即‘\0’后面的局部存储空间〕。PyramidsofEgypt\0StatueofZeus\0LighthouseofPharos\0TempleofArtemis\0ColossusofRhodes\0MausolusTombTemple\0GreatWallofChina\0AlexandriaPort\0另一种方法是采用指针数组,它可以解决上述问题。首先定义一些字符串,再把这些字符串的首地址存放在一个字符指针数组中〔即把字符指针数组的各个数组元素分别指向各个字符串〕。当对字符串排序而需要交换两个字符串时,只要交换字符指针数组中对应两个数组元素的值(为指向对应字符串的首地址)即可,也就是通过改变指针数组中相应数组元素的指向就可以实现排序目的,而不必交换具体的字符串本身,不必移动字符串的物理位置,这样将大大减少时间的开销,能提高运行效率,同时节省了存储空间。
6.5.2指针数组的应用【例6-17】将世界十大奇迹文明遗址〔埃及金字塔、宙斯神像、法洛斯灯塔、巴比伦空中花园、阿提密斯神殿、罗得斯岛巨像、毛索洛斯墓庙、中国万里长城、亚历山卓港、秦始皇兵马俑〕按照英文字母递增方式排序。世界十大奇迹文明遗址〔英文〕{"PyramidsofEgypt","StatueofZeus","LighthouseofPharos","HangingGardensofBabylon","TempleofArtemis","ColossusofRhodes","MausolusTombTemple","GreatWallofChina","AlexandriaPort","QinShihuangTerracottaArmy"};#include<stdio.h>#include<string.h>voidmain(){voidbubble_sort(char*name[],intn);voidprint(char*name[],intn);char*ruins_name[]={"PyramidsofEgypt","StatueofZeus","LighthouseofPharos","HangingGardensofBabylon","TempleofArtemis","ColossusofRhodes","MausolusTombTemple","GreatWallofChina","AlexandriaPort","QinShihuangTerracottaArmy"};intm=10;bubble_sort(ruins_name,m);print(ruins_name,m);}voidbubble_sort(name,n)/*冒泡法排序*/char*name[];intn;{char*temp;inti,j;for(i=0;i<n-1;i++){for(j=0;j<n-1-i;j++)if(strcmp(name[j],name[j+1])>0) {temp=name[j];name[j]=name[j+1];name[j+1]=temp;}/*交换字符串的地址*/}}
voidprint(name,n)/*将排序后的字符串进行输出*/char*name[];intn;{inti;for(i=0;i<n;i++)printf("%s\n",name[i]);}在程序的main主函数中,定义了字符指针数组ruins_name并作了初始化赋值,使得每个数组元素的初值分别为各个字符串的首地址。函数bubble_sort使用冒泡法来完成排序,其形参n为字符串的个数,另一形参name为指针数组,接受实参传递过来的ruins_name指针数组的首地址〔即指向待排序各字符串的数组的指针〕,所以实参指针数组ruins_name和形参指针数组name就共占同一段内存单元,这样对形参指针数组name中元素排序后,就相当于对实参指针数组ruins_name中元素进行排序。在bubble_sort函数中,对两个字符串比较采用了strcmp函数,strcmp函数允许参与比较的字符串以指针方式出现。函数print用于将排序后的字符串进行输出,其形参与bubble_sort的形参相同。需要注意,在排序过程中假设发现位于前面的字符串大于后面的字符串,不是交换被比较的两个字符串本身,而是要交换被比较的两个字符串的指针。就是说,字符串的存储位置不变,改变的是字符串指针的存储位置,这样就防止了使用字符串复制函数strcpy进行字符串赋值的过程,简化了算法,减少了时间的开销,提高了运行效率,并且也节省了存储空间。ruins_name[0]ruins_name[1]ruins_name[2]ruins_name[3]ruins_name[4]ruins_name[5]ruins_name[6]ruins_name[7]ruins_name[8]ruins_name[9]PyramidsofEgyptStatueofZeusLighthouseofPharosHangingGardensofBabylonTempleofArtemisColossusofRhodesMausolusTombTempleGreatWallofChinaAlexandriaPortQinShihuangTerracottaArmyruins_name[0]ruins_name[1]ruins_name[2]ruins_name[3]ruins_name[4]ruins_name[5]ruins_name[6]ruins_name[7]ruins_name[8]ruins_name[9]PyramidsofEgyptStatueofZeusLighthouseofPharosHangingGardensofBabylonTempleofArtemisColossusofRhodesMausolusTombTempleGreatWallofChinaAlexandriaPortQinShihuangTerracottaArmy
ruins_name指针数组字符串
6.6指针数组作main函数的形参带参数的main函数的一般形式指针数组作为main函数的形式参数也是指针数组的重要应用之一。在前面的程序中,main函数都是不带参数的,即main函数之后的一对圆括号中为空,如main〔〕的形式。而在实际应用中,main函数是可以带参数的,这样的参数一般称为命令行参数。在操作系统状态下,为了执行某个程序而键入的一行字符称为命令行,它一般以回车作为结束符。命令行中必须要有程序的可执行文件名〔即命令名〕,有时也带有假设干参数〔即命令行参数〕。C程序是在main函数中使用参数〔形参〕来接受命令行参数〔实参〕的。〔1〕带参数的main函数的一般形式intmain(intargc,char*argv[]){函数体}其中,形参argc用来存放命令行中单词的个数,它表示传递给程序的参数个数〔指包括命令名在内的所有参数〕,为整型,它的值至少是1;而形参argv是指向字符串的指针数组,它用来存放命令行中单词的内容,实际上存放的是命令名和各个命令行参数字符串的首地址。形参argc和argv的名字可由用户任意命名〔一般情况下可以不用改变〕;但是它们的类型却是固定的而不能由用户改变。〔2〕命令行参数的一般形式可执行文件名参数1参数2……参数n当按照可执行文件名〔即命令名〕执行程序的时候,系统会把参数1、参数2、……、参数n依次传递给该文件名中main函数的形参。在C语言中,main函数可以调用其它函数,其它函数不能调用main函数,所以无法从程序中得到main函数的形参。实际上,在操作系统状态下〔如DOS环境下〕,输入main函数所在的文件名〔为包含该main函数并且已经进行编译、连接的可执行文件〕,系统才能调用该文件名中的main函数。就是说,在命令提示符后面输入一个命令行,在命令行中应包括命令名〔即可执行文件名〕和要传递给main函数的实参〔即命令行的参数〕。当输入一个命令行并按下回车键后,命令解释程序就开始对该命令行进行如下处理:首先根据命令名来搜索命令路径以便找到对应的程序文件,然后将命令行参数传递给该程序文件,最后执行该程序文件。由于命令名必须存在,所以表示传递给程序的参数个数argc的值至少是1。而argv[]是指针数组,其中的argv[0]指向命令名字符串,argv[1]到argv[argc-1]就分别指向命令名后面的各个实参字符串。请注意,在命令名和参数之间、各个参数之间要有空格,并且文件名应该包括文件所在的盘符、路径以及文件的扩展名。除了系统提供的各种命令可以通过命令行方式执行以外,用户编写的C程序经过编译、连接成为可执行文件后,也可以像使用命令一样使用可执行文件名。例如,DOS命令提示符下命令行的一般形式为:
C:\>可执行文件名参数1参数2……参数n当要运行一个可执行文件时,首先在DOS命令提示符下键入文件名,然后再输入实际参数就可以把这些实参传送给main函数的形参中去。6.6.2命令行参数的应用由于命令行中各个参数字符串的长度事先不知道并且通常不相同,所以利用指针数组作为main函数中的参数是非常合理的,可以节省内存存储空间。【例6-18】观察参数回送命令echo程序〔文件名为echo.c〕。它只是简单地输出命令行上的参数,每个参数后面是空格,最后是换行。#include<stdio.h>intmain(intargc,char*argv[]){printf("argc=%d\n",argc);while(--argc>0)printf("%s%c",*++argv,(argc>1)?'':'\n');}程序运行输出结果:argc=5systembrowserandshow把文件名为echo.c的程序保存后,经过编译、连接后生成echo.exe文件,在DOS环境下如果输入命令行:echosystembrowserandshow↙
对于*++argv来说,argv要先与++结合进行++argv运算,使得argv指向下一个数组元素;再与前面的*结合,以便找到当前所指向字符串的首地址,然后来输出该字符串。第一次,在指针数组argv中,argv指向第一个数组元素argv[0],而argv[0]指向字符串echo〔即命令字符串echo的首地址〕。进行++argv后使得argv指向下一
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 健康管理考试口诀及答案
- 高数题库及答案资源
- 2025年森林管护工考试题及答案
- 成长足迹课件
- 2025年重庆地理补考试卷及答案
- 特岗化学考试真题及答案
- 情景剧表演课课件
- 护理员理论考试题及答案
- 瑜伽导师考试题目及答案
- 火车司机选拔考试题及答案
- Unit 1 Happy Holiday 单元测试卷(含答案) 人教版(2024)八年级上册
- 炸药安全课件
- 泌尿男生殖系统肿瘤诊疗规范
- 恙虫病护理课件
- 肝癌介入治疗护理查房
- 2025至2030中国铅酸电池(铅酸电池)行业项目调研及市场前景预测评估报告
- 重晶石矿购销合同
- 货物应急响应管理制度
- 验收程序及管理制度
- 高中学生研究性学习 研究报告《垃圾分类的研究与调查》
- 养鱼温室大棚可行性报告
评论
0/150
提交评论