370 likes | 510 Views
《 C 语言程序设计》. 第 8 讲. 第 8 章 指针. 目标要求. 掌握指针的概念、定义。 掌握指针变量的使用。 掌握指针在函数中的运用。 掌握指针的运算。 掌握指针在数组中的运用。. 第 8 章 指针. 讲课提纲. 概述 指针的定义 指针变量作函数参数 指针在数组中的运用 指针其他用法 典型实例剖析 小结 课后作业. 第 8 章 指针. 8.2 指针的定义. 8.2.1 地址的概念 8.2.2 指针的定义 8.2.3 指针变量的操作 8.2.4 指针用法小结. 第 8 章 指针.
E N D
《 C语言程序设计》 第 8 讲
第8章指针 目标要求 • 掌握指针的概念、定义。 • 掌握指针变量的使用。 • 掌握指针在函数中的运用。 • 掌握指针的运算。 • 掌握指针在数组中的运用。
第8章指针 讲课提纲 • 概述 • 指针的定义 • 指针变量作函数参数 • 指针在数组中的运用 • 指针其他用法 • 典型实例剖析 • 小结 • 课后作业
第8章指针 8.2 指针的定义 8.2.1 地址的概念 8.2.2 指针的定义 8.2.3 指针变量的操作 8.2.4 指针用法小结
第8章指针 8.1 概述 指针的本质就是地址,指针变量是一种专门用来在存储器中存储地址的特殊变量。在第6章函数中,已提到访问某个数其实质是先找到存放这个数的存储单元(地址),然后再找到这个地址中所存储的数。有了指针这一概念后,就可以先使指针变量指向某个变量的地址,然后通过对指针的操作实现对这个变量的操作。这种操作尤其对数组、结构体等复杂数据类型时非常简便。引入指针后: • 可以使程序简洁化、紧凑化和高效化。 • 为函数之间提供了简洁而便利的参数传递方法。 • 可以实现动态分配存储空间。 • 可以使程序员浏览整个内存空间从而能够改变内存中的数据。
第8章指针 8.2.1 地址的概念 计算机中的地址与我们现实生活中的地址非常相似。例如,在一幢学生宿舍里,每1个房间都有1个编号,以便于别人访问该房间里居住的同学;在计算机的内存中,每1个存储单元(1个字节的存储空间)同样也有1个编号,以便于计算机访问该内存单元中的数据,这个编号就是存储单元的地址。在内存单元中存储的数据就像房间里居住的学生,通过房间门牌号码可以找到该房间居住的学生,同样,通过内存单元的地址可以实现对存储在该单元中数据的读写操作。
第8章指针 8.2.2 指针的定义 • 指针的定义格式: 既然指针变量是一种特殊的变量,因此在使用之前也必须先定义。指针的定义格式如下: 指针类型 *指针变量名; • 对指针定义作以下说明: • 指针类型是指指针所指向变量中存放数据的类型。 • 指针变量名是指针的名字,它遵循标识符的命名规则。 • “*”符号可以靠近定义中任何1个部分,甚至也可以独立地放在中间,在这里“*”主要起一个标识作用,用于说明定义的变量为指针变量。
第8章指针 8.2.2 指针的定义 • 应用举例: • 定义1个指向整型变量的指针变量p: int *p; • 定义1个指向实型变量的指针变量q: float *q; • 定义1个指向字符型变量的指针变量point: char *point; • 此外,我们还可以同时定义多个相同类型的指针,例如: int *p,*q;
第8章指针 8.2.3 指针变量的操作 (1)取地址运算符“&” (2)将变量地址赋给指针 (3)取内容运算符“*” (4)除了给指针赋1个确定的地址外还可以为1个指针赋1个“空”值,这时该指针并没有指向1个确定的地址,例如: • p=NULL; • 或者: • p=’\0’; • 或者: • p=0;
第8章指针 8.2.3 指针变量的操作 (5)在C语言中,虽然在定义1个变量后系统就为该变量在内存中分配了一定大小的存储空间。但究竟此单元的地址是多少,用户是很难知道的。其实,在实际的操作中,通常并不需要知道1个变量在内存中的地址编号是多少,在程序中只需用相应的表达式表示出1个变量的地址即可,例如使用语句: “p=&a;q=&b;”
第8章指针 8.2.4 指针用法小结 • 指针变量的值就是它所指向的目标变量的地址值,换言之,指针变量所存储的不是1个普通的数据值,而是1个变量的地址值。指针变量是一种特殊的变量,它首先具有变量的基本属性,要求指针变量在使用之前必须先定义。 • 指针类型通常是指指针指向的目标变量的类型,即要处理数据的类型。不过习惯上将指针指向变量的类型称为指针的数据类型。 • 指针是一种功能强大同时又具有潜在危险的工具,在使用时务必小心。其中必须注意的1个重要问题就是指针必须在定向后才能使用,否则会出现严重错误。指针定向是指在定义1个指针变量后将其指向1个特定的变量。
第8章指针 8.2.4 指针用法小结 • 1个未被定向的指针可以随机指向内存中的1个空间,使用未定向的指针可能导致对不可预知的数据空间中数据的改写,甚至导致系统崩溃。可以通过给指针变量赋初值的方法为指针定向。 • 尽管指针可以指向不同数据类型的变量,但是各种指针变量自身所占的存储空间的大小都是相同的。这是因为指针存储的是地址量,而任何变量的地址都是使用1个存储单元的地址来表示(1个字节),大于1个字节长度的变量的地址使用首地址表示。因此,指针的数据长度仅仅与计算机硬件有关,而与指向的数据类型无关。这是指针变量特殊性的表现。 • 指针变量特殊性的另1个表现是并不是任何数据都可以赋给指针作为初始值,只能是1个与其类型相符的常量或变量地址,因此可以使用的数据只是1个非常有限的数据集合
第8章指针 8.3 指针变量作函数参数 • 函数的参数不仅可以是整型、字符型和实型等基本数据类型的变量,还可以是指针类型。使用指针作为函数的参数更能体现函数的特点,增加程序设计的灵活性,增强函数的功能。使用指针作为函数参数实质上是采用地址传递的方式,将1个变量的地址传到另一函数中。 • 和基本函数调用一样,指针应该在使用之前先定义,否则应在使用之前加以声明,否则在执行程序时系统要出现错误提示,程序不能编译通过。 • 不能企图通过改变指针形参的值而使指针实参的值改变,也就是说不能通过修改形参的指向来改变实参指针的指向。C语言中实参变量和形参变量之间的传递是单向的“值传递”方式,指针变量作函数参数也要遵循这一原则。虽然在调用函数时不能改变实参指针变量的值,但可改变实参指针变量所指变量的值,这正是指针参数的优势所在。
第8章指针 8.4 指针在数组中的运用 • 指针变量不仅可以指向基本类型的变量,也可以指向数组中的元素,在讲述相关内容之前,我们先回顾一下有关数组的定义及其概念。数组实质上是一组变量的有序集合,具有以下特点: • 同1数组中的每个元素的数据类型相同。 • 数组在内存中占有一段连续的存储空间。 • 数组下标是从0开始的自然数。 • 数组名代表数组的首地址,也就是第1个数组元素的地址。
第8章指针 8.4 指针在数组中的运用 使用指针指向数组 • 例如: • int a[10]; • int *p; • p=a; a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] p
第8章指针 8.4 指针在数组中的运用 用法说明 • 数组的数据类型应和指针变量的数据类型一致,否则将出现语法错误。 • 语句“p=a;”的作用是把数组a的首地址赋给指针变量p,而不是把数组a的各个元素的值赋给p,a不代表整个数组,而是代表该数组的首地址。因此,以下几种形式的语句是和语句“p=a;”是等效的: p=a+0; 或者:p=&a[0]; • 当然,如果要使指针变量p指向数组中的第3个元素可用以下的几种语句: p=a+2; 或者:p=&a[2]; • 其余类推!
第8章指针 8.4 指针在数组中的运用 • 与其他变量一样,指针变量也可以在定义的同时完成赋值,程序段“int a[10]; int *p; p=a;”可以等效于下面的程序段: int a[10]; int *p=&a[0]; 或者: int a[10]; int *p=a ;
第8章指针 8.4 指针在数组中的运用 使用指针访问所指向数组中的元素 • 指针的加减运算。 • 如果用户想要控制指针变量指向数组中其它元素,可以用指针变量的加减运算,它有两种基本形式(其中p代表指针变量,i是1个自然数): • p+i(p-i):获得指针当前指向位置之后(前)i个元素的地址,以实现对该元素的访问,注意此时指针的指向并未改变。 • p++(p--):将指针从当前指向位置后移(前移)1个元素的位置。注意此时指针的指向已经发生改变。
第8章指针 8.4 指针在数组中的运用 使用指针访问所指向数组中的元素 特别注意:p+i(p-i)不是简单地将p值加(减)i。对于不同的数据类型,它们的含义是不同的。例如:char型指针变量移1个位置是1个字节;int型指针变量移1个位置是2个字节;实型指针变量移1个位置是4个字节。所以,如果指针移动i个存储单元时,它所经过的字节数为: p+i*d 其中,d是一种类型数据对应的存储空间的长度。对于字符型数据“d=1”;对于整型数据“d=2”;对实型数据“d= 4”。
第8章指针 8.4 指针在数组中的运用 使用指针访问所指向数组中的元素 • 综上所述,“p+i”和“a+i”就是a[i]的地址,或者说它指向数组a的第“i+1”个元素,这里需要强调的是a代表数组的首地址,“a+i”也是地址,“p+i”的计算方法与之相同。所以,“a+4”和“p+4”的值即是&a[4],它们都指向元素a[4]。 • 简单地说,指针加上(或减去)1个整数i就是获得指针当前指向元素前面(或后面)i个元素的地址,这个结论无论指针指向何种类型的数据都成立。
第8章指针 8.4 指针在数组中的运用 使用指针访问数组元素 • 当指针指向某一数组元素后,通过取内容运算“*”即可获得该指针所指向的数组元素的值。一般地,“*(p+i)”或“*(a+i)”是“p+i”或“a+i”所指向的元素的值,即a[i]。 • 例如,“*(p+5)”或“*(a+5)”就是a[5]。实际上,在编译时,对于“*(a+i)”的处理就是按数组首地址加相对位移量得到要找的元素的地址,然后找出该单元中的内容,假设数组a的首地址为1006,数组为整型,则a[7]单元的地址为1006+7*2 =1020。
第8章指针 8.4 指针在数组中的运用 • 指向数组的指针变量也可以带下标,如对于以下程序段: int p[10],*x; x=p; 则x[i]也是正确的,它与p[i]是等价的。
第8章指针 8.4 指针在数组中的运用 使用指针处理字符串。 • 在第7章数组中,我们讲过可以对一个数组赋字符串,其形式如下: char string[]= “I Love China !”; 同样可以对指针做同样的操作,其形式如下: char *string=“I Love China !”; 这两种形式是等效的,系统都是默认数组或指针变量是存放字符串的首地址,它们的存储形式也是相同的,只不过是“一个实质,两种表述而已”。
第8章指针 8.5 指针其他用法 • 指向指针的指针 指向指针的指针的基本形式如下: ** 指针变量名 • 指向二维数组的指针变量 若有以下程序段: int a[10][10]; int m,n; 并且0≤m≤9,0≤n≤0,则元素a[m][n]可以有以下几种表示方法: • a[m][n] • *(a[m]+n) • *(*(a+m)+n) • (*(a+m))[n] • *(&a[0][0]+10*m+n)
第8章指针 8.6 典型实例剖析 • 有一个数列为:2/1、3/2、5/3、8/5、13/8、21/13……编写程序求这个数列的前20项之和。 • #include<stdio.h> • void add(int *p,int *q, float *g ) • { • int r; • *g=*g+(float)*q/(*p); /*求某项的值并实现数据的累加*/ • r=*p; /*把前项的分母保存*/ • *p=*q; /*把前项的分子赋给后项的分母*/ • *q=r+*p; /*把前项分母与分子的和赋给新项的分子*/ • }
第8章指针 8.6 典型实例剖析 • void main( ) • { • int a,b,i; • float x=0.0; • a=1; • b=2; • for(i=0;i<20;i++) • add(&a,&b,&x); • printf("the result is:%f",x); • }
第8章指针 8.6 典型实例剖析 2.编写程序求1个数组中所有奇数之和以及所有偶数之和。#include<stdio.h> • #include<conio.h> • int n=6; /*定义全局变量n*/ • void fun(int *a,int *odd,int *even) • { • int i; • *odd=0; *赋初值,不能掉了符号“*”*/ • *even=0; /*赋初值,不能掉了符号“*”*/ • for(i=0;i<n;i++) • { • if(*a%2==1) *odd=*odd+*a; /*累加,不能掉了符号“*”*/ • else *even=*even+*a; /*累加,不能掉了符号“*”*/ • a++;/*实现地址的依次后移,注意:a是指针,若是数组则不能这样操作*/ • } • }
第8章指针 8.6 典型实例剖析 • void main() • { • int a[6],i,odd,even; • clrscr(); • printf(“please input the number to the array:”); • for(i=0;i<n;i++) /*对数组赋值*/ • scanf(“%d”,a+i); • printf(“\nthe original data is:”); • for(i=0;i<n;i++) /*输出数组中的值*/ • printf(“%5d”,*(a+i)); • printf(“\n”); • fun(a,&odd,&even); /*调用函数*/ • printf(“the sum of odd number:%d\n”,odd); • printf(“the sum or even number:%d\n”,even); • }
第8章指针 8.6 典型实例剖析 3.统计1个长度为2的字符串在另一个字符串中出现的次数 • #include<stdio.h> • #include<conio.h> • #include<string.h> • int fun(char *str,char *substr) • { • int n=0; • while(*str) • { • if(*str==substr[0]&&*(str+1)==substr[1]) • n++; /*出现个数的统计*/ • str++; /*指向长字符串中元素的指针后移*/ • } • return n; • }
第8章指针 8.6 典型实例剖析 • void main() • { • char str[81],substr[3]; • int n; • printf("please enter the first chars to the array:"); • gets(str); • printf("please enter the second chars to the array:"); • gets(substr); • n=fun(str,substr); • printf("n=%d\n",n); • }
第8章指针 8.7 小结 • 指针与地址的关系。 简单地说,指针是内存地址的代名词。通过指针(地址)操作,可以对存储单元中的数据实现读写操作。用于存放1个变量地址的特殊变量就是指针变量。 • 指针变量的定义。 指针变量定义的基本形式为: 指针类型 *指针变量名 例如: int *p;
第8章指针 8.7 小结 • 指针类型。 在指针变量定义中的类型符(int、char、float….)称为指针类型。由于指针代表1个内存单元的地址,程序将要根据此地址读出(或写入)存放在此单元中的数据,因此指针类型实际上表示该地址单元存储数据的类型。 • 指针变量的存储。 指针变量占有固定大小的存储单元。1个地址本质上是整数(或长整数,为简单计,可以认为是整数)。故指针变量都占2个字节的存储空间,与指针类型无关。
第8章指针 8.7 小结 • 取地址运算。 符号“&”是取变量地址的单目运算符,它直接作用于1个变量前表示该变量的地址,例如,若定义以下变量: float x; char y; 则&x和&y就是这两个变量的内存地址,通常,程序设计者并不关心&x和&y的具体值,只要知道&x和&y是变量x和y的指针就可以了。若在前两个语句后再有1个定义指针变量的语句: float *f1; char *f2; 则可进行以下赋值: f1=&x; f2=&y;
第8章指针 8.7 小结 这样就使指针变量f1指向变量x所在的地址,f2指向变量y所在的地址。注意,以下的赋值语句是错误的: f1=&y; f2=&x; 错误的原因是:指针基类型与变量的数据类型不一致。
第8章指针 8.7 小结 • 间接访问变量。 使用取内容运算符“*”可以间接访问1个指针指向的变量,只需要在指针名前加上“*”即可。 • 指针的自加和自减。 指针变量可以进行自加和自减运算,但其含义与普通变量的同种运算不同。若已有定义“float *p”,则: • 表达式p++(p--)将使指针向后(前)移动1个数据的位置。 • p+1指向数组的下1个元素,而不是简单地使指针变量p的值加1。其值实际变化为pointer+1*size(size为一个元素占用的字节数)。例如,假设指针变量pointer的当前值为3000,则pointer+1为3000+1*2=3002,而不是3001。
第8章指针 8.7 小结 • 指针作函数参数。 在将指针作为函数参数时,一定要保证参数对应。指针是一种可以使我们“绕开”1个变量的名字而存取它的工具或手段,指针本身并没有受间接引用的影响。并且实参和形参指向同一段内存地址,因此在函数调用过程中可修改实参变量的值,还可以从被调函数中获得多个返回值以提高以参数传递效率。
第8章指针 课后作业 • 将1个字符数组中的字符倒序输出。 • 有1个字符串,包含n个字符,写一函数,将此字符串中第m个字符开始的全部字符复制到1个字符串中。 • 输入10个整数,将其中最小的数与第1个数对换,把最大的数与最后1个数对换,写3个函数∶(1) 输入10个数;(2) 进行处理;(3) 输出10个数。