精通C程序设计第六章指针初步.ppt
第六章 指针初步,本章内容提要:6.1 指针的概念与基本操作6.2 指针与一维数组 6.3 字符串处理 重点小结作业,6.1 指针的概念与基本操作,6.1.1 指针的概念1.什么是指针?指针是C语言的一种数据类型,该数据类型以内存地址为值,并提供了相关的一系列操作。2.使用指针的优点与缺点直接访问内存,增强了高级语言的功能编程灵活度增加不易学习、掌握和正确安全地使用好指针,6.1 指针的概念与基本操作(续1),3.指针的基本属性(1)值属性:指针值即内存地址,它是一个非负整数(2)存贮属性:老式16位编程有三种,分别是:a.near(近)指针:16位段内偏移地址b.far(远)指针:16位段地址16位段内偏移地址c.huge(巨)指针:32位规格化的内存地址 32位编程模式下,只有一种32位规格化地址(指针),不再区分near,far和huge。,(3)对象属性:指该地址开始(指针指向)的内存单元中存放的数据。定义指针变量时,必须指定指针变量的对象类型。我们使用指针的主要目的就是通过指针访问内存中的数据。如:double p;/指针p的对象是一个double型数据/指针p指向一个double型数据 void p;/指针p指向nothing/无值指针不能进行运算和存取操作,6.1 指针的概念与基本操作(续2),6.1 指针的概念与基本操作(续3),6.1.2 指针常量与变量1.指针常量(只有三种)(1)空指针:NULL符号常量NULL定义于头文件stdio.h中,其值为整数0,代表地址0和空指针的概念。空指针是值为0的指针(即NULL);空对象(无值)指针是没有对象不能进行存取操作的指针。(2)数组的名字:它是数组的首地址(3)函数的名字:代表子程序调用的入口地址,6.1指针的概念与基本操作(续4),2.指针变量(1)定义方法:对象类型名 指针变量名;例:int p,q;double r;char s;注意:4/VC+6.0sizeof(p)=sizeof(q)=sizeof(r)=sizeof(s)=2/TC2.0说明:以VC+6.0为例,每个指针变量用于保存一个32位(4字节)内存地址。指针变量的存储长度与其对象类型无关。(2)指针类型名 T 例如:double r;char p;p=(double)r;,6.1指针的概念与基本操作(续5),(3)指针变量的初始化 C语言允许用指针常量表达式对指针变量初始化.例:int a4,p=NULL,q=a+1;#include double(f)(double)=sin;注意:(a)不能将一个整数值直接作为内存地址对指针变量进行初始化。如:long p=0 x410;,6.1指针的概念与基本操作(续6),(b)作为一种特殊情况,C语言允许用一个字符串常量初始化一个char*型的指针变量如:char s=ABCD;变量s得到的是字符串常量第一个字符(字母A)的地址,即串首地址。6.1.3 指针的基本运算与操作1.取地址运算:,6.1指针的概念与基本操作(续7),2.取对象运算:指针表达式例1:int a=123,p;p=/则打印结果为A理解帮助-指针、指针变量与指针对象的关系,6.1指针的概念与基本操作(续8),若有定义:double p;char q;假设指针变量p的值(即变量p保存的地址值)为0 xB8000000,表达式p表示从p保存的地址值指定的内存中取一个double型数据(即从内存地址0 xB8000000开始连续取8字节单元)。若q的值也是0 xB8000000,表达式q表示从q保存的地址值指定的内存中取一个float型数据(即从内存地址0 xB8000000开始连续取4字节单元)。若有定义:float a=0.5f,r=后,有 表达式r表达式a 表达式 r表达式&a,6.1指针的概念与基本操作(续9),3.指针对象的有关操作 指针对象可进行与之同类型变量的所有运算。例:int a,p=,6.1指针的概念与基本操作(续10),4.正确建立指针对象的方法(1)用已有的变量或数组空间建立对象例:int a=15,p,q,b3;p=(2)用内存分配函数建立对象 介绍两个函数:头文件均为stdlib.h 函数原形:void malloc(unsigned n)函数功能:分配n字节连续内存单元供用户使用,分配成功,返回首地址,分配失败,返回NULL。,6.1指针的概念与基本操作(续11),函数原形:free(void p)函数功能:释放由malloc分配的内存单元。自变量p给出内存块的首地址。malloc与free函数一般配对使用(有分配就应有释放).例:double p;p=(double)malloc(8);/分配8字节内存 p=0.5;/p所指对象存入0.5 free(void)p);/释放p所指对象,6.1指针的概念与基本操作(续12),极不安全的对象用法:(1)double p;p=0.5;/p是指向哪里呢?(2)double p;p=(double)malloc(4);/空间没有分配够 p=0.5;(3)double p;p=(double)malloc(8);p=(double)malloc(8);/先前分配的8字节呢?后果:突然死机/重启动/出现对话框“程序执行了非法操作”(往往不得不手工重启动),6.1指针的概念与基本操作(续13),5.指针运算(指针表达式也称为地址表达式)(1)指针加减整型表达式n:仍得同类型指针运算规则:地址值增减n倍对象类型长度例:long a=1,2,3,4,p=a;p=a+2;/*p指向元素a2,即p=即p指向下一个对象*/,6.1指针的概念与基本操作(续14),特殊表达式:(a)p+;和 p-;先取用对象,然后p自加减1(b)+p;+p;-p;-p;这四种形式都是p先自加减1,然后再取用对象对比:+(p);(p)+;-(p);(p)-;这是对象自加减1,6.1指针的概念与基本操作(续15),(2)两个对象类型相同的指针进行加法运算 例:int p,q,s;s=p+q;/*合法但无用处*/(3)两个对象类型相同的指针进行减法运算:得相距的对象长度的倍数(可正可负)例:int a5,p,q;p=则pq1而pq0,6.1指针的概念与基本操作(续16),6.指针的输入与输出 scanf与printf函数采用p格式符。很少实际应用。,TC2.0,6.2 指针与一维数组,6.2.1 对象表达式与元素表达式的等价互换形式C/C+语言中,对任意指针ptr,整型表达式i,恒有对象表达式 元素表达式(都是L_value)(ptr+i)ptriC/C+语言为什么不检查数组下标越界?答:若有数组定义T an;则元素表达式ai,不论i取何值,永远是合法表达式,因为 ai(a+i),a是一个指针常量,6.2 指针与一维数组(续1),6.2.2 一维数组名若有定义:T an;T:数据类型名 n:整型常量则数组名 a的数据类型为 T;值为则 a+ip+i&ai&pi/地址等价形式(a+i)(p+i)aipi/元素等价形式,6.2 指针与一维数组(续2),应用举例:将数组下标变成从1开始若有定义:T an,p=a-1;则 p1,p2,pn 依次等价于 a0,a1,an-1程序例1(教材例6.2)建立N元(N用#define定义)整型数组,实现数组元素的逆序存储,然后输出逆序后的数组元素。要求算法不得另辟数组空间。比如:N=8,数组元素原来的值是 3 5 27910 64 则逆序存储以后的值是 4 6 10 9 7 2 5 3,6.2 指针与一维数组(续3),算法设计:以数组下标中点为对称轴,交换对称位置的数组元素。下标算法:aiaN1i i=0,1,N/21指针算法:初态p=a;q=a+N1;p+q 直到p=q/*下标法*/#include stdio.h#define N 8void main()int aN,i,t;printf(Input%d integers:n,N);for(i=0;iN;i+)scanf(%d,a+i);for(i=0;iN/2;i+)t=ai;ai=aN1i;aN1i=t;for(i=0;iN;i+)printf(%6d,ai);printf(n);,6.2 指针与一维数组(续4),/指针法/#include stdio.h#define N 8void main()int aN,p,q=a+N1,t;printf(Input%d integers:n,N);for(p=a;p=q;p+)scanf(%d,p);p=a;while(pq)t=p;p=q;q=t;p+;q;for(p=a;pa+N;p+)printf(%6d,p);printf(n);,6.2 指针与一维数组(续5),一维动态数组程序例2(教材例6.3)输入n个整数,存于n元整型数组,然后求数组元素中的最大值与第二大值,输出最大值与第二大值。要求程序在运行时,首先从键盘输入整数的数目n。算法设计:(1)n元整型数组p用malloc函数建立。(2)用指针法求最大值与第二值算法:q1,q2分别指向最大值与第二大值元素初态:若p0p1,则q1=p;q2=p+1;否则,q1=p+1;q2=p;循环:q=p+3,p+4,p+n1 扫描数组余下的元素让q与q1和q2比较大小,有以下三种情况,若qq1,令q2=q1;q1=q;若q1qq2,令q2=q;若q2q,则q1和q2保持不变,6.2 指针与一维数组(续6完),#include stdio.h#include stdlib.hvoid main()int n,q1,q2,q,p;printf(Input n=);scanf(%d,/释放动态数组内存空间/,6.3 字符串处理,6.3.1 字符串的存储1.内存格式 串长为n的字符串在内存中连续存放,每个字符存贮其ASCII码,占一个字节,共n个字节,最后填一个全0字节作为串的结束标志。空串也有一个字节,即只有结束标志字符0。2.程序中怎样存贮字符串(1)用字符数组存贮串:例:char s=A,B,C,D,0;char s41=ABCD;char s41=ABCD;char s41=A,B,C,D,0,(2)用字符串常量初始化一个字符指针变量建立串例:char s=ABCD;于是 s0=A,s1=B,s2=C,s3=D,s4=0(3)用内存分配函数建立串例:char t;t=(char)malloc(81);strcpy(t,DEF);free(void)t);3.程序中不安全的字符串存储方法,6.3 字符串处理(续1),6.3 字符串处理(续2),例:(1)char s;scanf(%s,s);strcpy(s,abcd);/s指向哪里?(2)char s;s=(char)malloc(4);strcpy(s,abcd);/存储空间不足(3)char s=abc,p=xyz;s=p;/怎样访问串abc?(成为内存垃圾),6.3 字符串处理(续3),6.3.2字符串的输入与输出1.输入字符串 scanf(%s,串首地址);/串首地址的类型为char 以空格、TAB、回车换行为字符串的输入结束 不能输入含有空格的字符串 gets(串首地址);/头文件stdio.h 以行作为字符串的输入单位例:char a10;scanf(“%s”,a);gets(a);,输入为:hello world,a的赋值结果有何不同,6.3 字符串处理(续4),2.输出字符串 printf(%s,串首地址);puts(串首地址);/头文件stdio.h例如:char a10;scanf(“%s”,a);gets(a);printf(“%s”,a);puts(a);,6.3 字符串处理(续5),6.3.3字符串的基本操作库函数(头文件均为string.h)1.拷贝串(串赋值):char strcpy(char s,char t)功能:t所指内存单元开始直到结尾的0(包括0)依次复制到地址s开始的内存空间。例:char s81,t41;strcpy(t,ABCD);strcpy(s,t);puts(s);/输出:ABCD对比:char s=ABCD,t;t=s;/*地址赋值*/t=(char)malloc(11);strcpy(t,s);/*串赋值*/,6.3 字符串处理(续6),2.连接串(串加法):char strcat(char s,char t)功能:串t连接到串s的结尾例:char s41=ABCD,t=EF;strcat(s,t);puts(s);/输出:ABCDEF3.求串长:int strlen(char s)例:char s41=ABCD;printf(%d,strlen(s);/输出44.英文字符大/小写变换:char strupr(char s)/英文小写变大写,其余不变char strlwr(char s)/英文大写变小写,其余不变,6.3 字符串处理(续7),5.两串比较大小:int strcmp(char s,char t)功能:串t串s,返回正数。两串比较大小的方法:对应字符比较ASCII码,ASCII码大的则串大;对应字符位置均相同,则串长大的串大.ABCDABC ABCD9A8B英文字典单词的编排顺序正好是字符串的升序排列,6.3 字符串处理(续8),对比:若s,t均为char 型变量或者常量,则 if(strcmp(s,t)0)/*比较两串相等*/if(s t)/*比较两指针值是否相等*/if(s=t)/*可能合法但不合理*/6.查找子串:char strstr(char s,char sub)功能:若子串sub包含于主串s中,则返回第一次出现的位置;若查找失败,返回空指针NULL。例如,char s=ABCDEFGHI,p;p=strstr(s,CDE);则p的值为s+2,即p指向字符C。,6.3 字符串处理(续9),7查找字符(1)char strchr(char s,int ch);该函数查找字符ch在字符串中第一次出现的位置,查找失败,返回空指针NULL。(2)char strrchr(char s,int ch);该函数查找字符ch在字符串中最后一次出现的位置,查找失败,返回空指针NULL。例如,char s=01234500,p;p=strchr(s,0);则p的值为s,它指向最左边的字符0。p=strrchr(s,0);则p的值为s+7,它指向最右边的字符0。,6.3.4 字符串处理程序设计举例例1(教材例6.4)不用字符串处理函数,求串长。#include stdio.hvoid main()char s81,p;int n=0;printf(Input a string:n);gets(s);p=s;while(p)n+;p+;/*p!=0 或 p!=0*/printf(length=%dn,n);/*利用串结束标志为0作为循环结束条件*/字符串处理的其它典型扫描结构for(p=s;p;p+)n+;/*for指针循环法*/for(n=0;sn;n+);/*for下标循环法*/,6.3 字符串处理(续10),6.3 字符串处理(续11),例2(教材例6.5)编程统计一个字符串小写英文字母的数目。#include stdio.hvoid main()char s81,k;int n=0;printf(Input a string:n);gets(s);for(k=0;sk;k+)if(sk=a 应该利用串结束标志字节0作为循环退出条件,6.3 字符串处理(续12),例3(教材例6.6)不用字符串处理库函数,自编程序实现字符串拷贝。#include stdio.h/源程序1void main()char s41,t41,i=0;/串t复制到串s/gets(t);while(si=ti+);puts(s);#include stdio.h/源程序2void main()char p,q,s41,t41;/串t复制到串s/gets(t);for(p=s,q=t;q!=0;)p+=q+;puts(s);问题与思考:源程序2的缺限是什么?,6.3 字符串处理(续13),例4(教材例6.7)不用系统库函数,自编程序实现两个字符串比较大小。要求:串s1串s2输出正数#include stdio.hvoid main()char s181,s281,p=s1,q=s2;gets(s1);gets(s2);while(p,6.3 字符串处理(续14),例5(教材例6.8)输入一句英文,按单词分行输出。例如,若程序的输入是 We are student 则输出是 We are students#include stdio.hvoid main()char s81,p;printf(Input an Engish sentence:n);gets(s);for(p=s;p;p+)if(p=32)/*遇空格字符则换行*/if(p+1)!=32)printf(n);/*注意多个空格的处理*/else putchar(p);printf(n);,6.3 字符串处理(续15完),例6(教材例6.8)字符串逆序存储。#include stdio.h#include string.hvoid main()char s81,p,q,temp;printf(Input a string:n);gets(s);p=s;q=s+strlen(s)1;while(pq)temp=p;p=q;q=temp;p+;q;printf(Reversed string:n%sn,s);,重点小结与作业,本章重点:1.指针的概念与基本操作(&,*,+n,相减)2.对象表达式与元素表达式的等价关系3.一维数组名的含义4.指针与一维数组的关系5.程序中怎样存储字符串?6.字符串的输入/输出与基本库函数7.逆序算法与字符串常用基本算法作业:,Thank You!,第六章完,