C语言(第六章)_第1页
C语言(第六章)_第2页
C语言(第六章)_第3页
C语言(第六章)_第4页
C语言(第六章)_第5页
已阅读5页,还剩88页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第六章 指针,信息学院自动化仪表研究所,C语言程序设计,本章主要内容,指针概述 地址 指针 指针变量 多重指针 指针变量作为函数参数 指针与数组 数组、指针与函数调用 行指针 指针数组 指针与函数 指向函数的指针变量 返回指针值的函数,一、指针概述,1地址(address) P201 如果在程序中定义了一个“实体”(变量、数组、函数 ),编译时系统就要给这些实体分配内存单元。 分配规则:,什么是内存单元“地址”?,内存单元是以字节为单位,每个字节都有一个编号(即“地址”)。如果将内存比作一个旅馆,内存单元就好比“床位”,而实体则好比“旅客”。这些“旅客”(实体)中,有单人型(char)、夫妇型

2、(int)、家庭型(float,long,double等),还有团体型(数组等)。每个“实体”占用的内存单元是不同的。如: char a;int b;float c;int d3;int max( ),内存单元与地址,main() char a; int b; float c; int d3; int max( ); ,地址,通常我们关心的不是各个内存单元的具体地址值,而是每个实体的“起始地址”。,如何表示实体地址?,实体地址表示法1:直接访问(实体名) 普通变量a,b,c char *b; float *c;,通常在C语言中,所谓“指针”就是指“指针变量”。从现在开始,我们所说的“指针”除非

3、另加说明,否则均表示“指针变量”。,是不是说地址有”整型” , ”字符型” , ”实型” 之分?,为什么要使用指针变量?,C程序中访问(读写)变量有两种方式: 直接访问 (按名单预留的座位入座) 利用实体名访问变量。访问变量的过程 变量(实体)名定义时分配的地址变量值 好比“先坐再买票”看电影:来一个观众,分配一个空位给他去坐,并且还要在纸上记一个某人坐在哪里。这种方式对用户来说很方便(“直接就座”),但对系统来说,“找某人”就极不方便(间接:查名字座号)。 间接访问 (先买票,后按号入座) 把变量地址先存放在“指针”中,再通过“指针”访问变量。 好比先买票(票指针,座号地址),再“按号入座”

4、看电影。这种方式对用户来说属于“间接就座”,但对系统查找来说就很直接,且便于处理。尤其对于数组(团体),可通过指针简单自加或自减,对整个数组进行处理。,习惯用语: 若指针变量p存放了变量a的地址,我们称“p指向a”。,指针变量不要谈”指”色变,指针是C语言学习中的一大难点。 难难在概念。,main() int a,*p1,*p2= ,学了半天,我还是一头雾水,首先搞定*p,请看以下变量声明语句 int a,*p1; char b,*p2; a,b 普通变量(存放某个数值或字符) p1,p2 指针变量(存放某个实体的地址),如果是 int *a,p1; char *b,p2;,变量声明时,如果变

5、量名前带 *号,表示该变量是个指针变量,注意不同的*p,以下程序中哪些语句是错误的? main() int a,*p; a=3; p=3; /*或者 p=a;*/ p= /*或者*p=3*/ ,讨论: 程序中引用变量时, 对指针变量p, 不带*号引用表示? 带*号引用表示?,两个特殊的运算符 system(cls); p= ,main() int *p,a12=1,2,3,4,5; clrscr(); p=a; for ( ;*p5;p+) printf(%d,*p); ,结果:a=13,b=10,结果:1234,p指向字符数组时的*p,如果p被定义成指向某个字符数组或某个字符串的指针变量,则

6、*p代表某个字符。 如 char *p, a3=”abcd”; p=a; *p代表a中的某个字符,main() char *p,a12=abcde; p=a; for (;*p;p+) printf(%c,*p); ,main() char *p; p=abcde; for (;*p;p+) printf(%c,*p); ,如果直接用a进行循环,行不行?,在for语句中用*p控制循环,是否适用于数值数组?, p= * int *p; x=3.14; p= ,结果:y=-2621.000000,怎么会这样?,把int *p改为float *p后,结果正确:y=3.140000,指针变量能参加运算

7、吗?,指针变量和其他变量一样,可以在各种表达式中参加运算。 但指针变量和普通变量不同,只能进行以下三种运算: 赋值运算 算术运算 指针比较,指针变量的赋值运算,指针变量初始化 变量声明时赋值,main( ) int a=5,*p= printf(“%d,%d,%dn”,p,*p,a) ,指针变量一般赋值 程序处理时赋值,结果:2000,5,5,main() int x; int *p1,*p2; p1= %p 以16进制显示指针,典型错误,指针变量定义后,未指向具体存储单元(实体地址)就使用,此时指针变量所指单元是任意的, 是个”危险指针”。,【例一】若有定义 char *p,ch; 则不能正

8、确赋值的语句组是 : A) p=,【例二】若有定义 char *a,b30; 则以下各语句正确的是 : A) a=”abcde”; B)b=”abcde”; C) scanf(“%s”,a); D) scanf(“%s”,b);,X,X,为什么未指向实体的指针是“危险指针”?,“危险指针”?不要耸人听闻好不好!,一个指针未指向任何实体就被使用,属于”内存盗用”!因为该指针将随意指向内存中某一单元,轻则误取或破坏其他实体的值,重则破坏操作系统的工作。,一个指针变量被声明后,在没有被赋予某个实体地址之前,如果使用它,不仅可能破坏你的程序,而且可能导致计算机操作系统崩溃,出现灾难性的错误。因为它可能

9、指向内存的任何部分。,空指针 P256,空指针: int *p; p=NULL;,NULL是什么? 在stdio.h中,定义 #define NULL 0 所以 p=NULL; 相当于 p=0;,内存使用常识: 任何C程序的变量在内存中的地址均由操作系统自动分配,不能由编程者通过赋值指定。p=NULL 表示p不指向任何变量。 内存的低端只供由操作系统使用(相当于政府机关,普通百姓不能使用)。,讨论:以下程序中的*p1,*p2,#include main() int *p1=NULL,*p2; clrscr(); *p1=100; *p2=200; printf(%d,%dn,*p1,*p2);

10、 ,*p1有确定地址,但未指向任何变量,*p2无确定地址,是“危险指针”,在指针p指向某个实体的地址之前,不可对*p进行赋值。否则可能发生意想不到的错误(p随便指向某个单元)。,指针变量的算术运算,指针只有两种算术运算加、减 p+5 p+ p-1 p- 注意加减运算是以实体为单位而不是以字节为单位。 此外,两个指针变量可以相减。即:如果两个指针变量指向同一数组时,两个指针变量值之差是两个指针之间的元素个数 但两个指针变量相加并无实际意义。,例 p指向float数, 则 p+1 p+1 4,例 p指向int型数组,且p= 则p+1 指向a1,例 int a10; int *p=,例 int a1

11、0; int *p1=,1,以下程序哪个语句执行时会出错?,#include main() int a10,*p1=a; clrscr(); a+; p1+; ,X,例 void main() int a =5,8,7,6,2,7,3; int y,*p= ,输出:5 6,例 注意指针变量的运算,6,main() int i,*p,a7; p=a; for(i=0;i7;i+) scanf(%d,p+); printf(n); for(i=0;i7;i+,p+) printf(%d,*p); ,例 注意指针的当前值,p=a;,指针变量可以指到数组后的内存单元,指针的逻辑比较 P257,指针变量

12、指向同一个对象(如数组)的不同单元地址时,才可以进行比较。地址在前者为小。 任何指针变量或地址都可以与NULL作相等或不相等的比较。如 if(p=NULL),#include #include fun(char *w,int n) char t,*s1,*s2; s1=w; s2=w+n-1; while (s1s2) t=*s1+; *s1=*s2-; *s2=t; ,main() char *p; p=1234567; fun(p,strlen(p); puts(p); ,结果:1711717,【注意】对*s1+,因*与+同级,且自右至左结合,所以等价于*(s1+),执行时是先做*s1,后

13、做s1+ 。 P215,*p:多重指针 (指向指针的指针)P251,对于 int *p; 定义一个二级指针(指向指针的指针) 存放某个指针变量的地址:等效于 int *(*p) 在引用时,*p是p间接指向的对象的地址。 *p是p间接指向的对象的值。,晕!,用一个*声明一个变量p int a=5,*p= 使用时p带两个*是a的值 (“两重间接取值),p带一个*是a的地址。,看了例子也许会明白的,以下程序段的输出是什么? int *pp,*p,a=20,b=30; pp=,对不对?,结果:30,30(多重间接取值),20,指针变量作为函数参数使用,main() void swap(int,int)

14、; int a=5,b=3,*p1,*p2; clrscr(); p1= ,main() void swap(int *,int *); int a=5,b=3,*p1,*p2; clrscr(); p1= ,结果:x=2,y=10 a=5,b=3,结果:x=2,y=10 a=2,b=10,再看一个例子,#include fun(int *i) static int a=1; *i+=a+; main() int k=0; fun( ,结果:3 第一次调用fun( 【讨论】如果fun()函数中没有static,结果呢?,为了实现: 在被调函数中改变实体值,然后在主调函数中使用这些改变了的实体值

15、 主要技术要点在于: 主调函数的实参和被调函数对应的形参都必须用地址表示(地址传递) 用于作实参的地址可以是: for(i=0;i=m;i+) j=n-1-i; t=xi; xi=xj; xj=t; main() int i,a10=3,7,9,11,0,6,7,5,4,2; inv(a,10); printf(The array has been reverted:n); for(i=0;i10;i+) printf(%d,ai); printf(n); ,m=4,例 将数组a中的n个整数按相反顺序存放,void inv(int *x, int n) int t,*p,*i,*j,m=(n-

16、1)/2; i=x; j=x+n-1; p=x+m; for(;i=p;i+,j-) t=*i; *i=*j; *j=t; main() int i,a10=3,7,9,11,0,6,7,5,4,2; inv(a,10); printf(The array has been reverted:n); for(i=0;i10;i+) printf(%d,ai); printf(n); ,实参用数组,形参用指针变量,例 将数组a中的n个整数按相反顺序存放,void inv(int *x, int n) int t,*i,*j,*p,m=(n-1)/2; i=x; j=x+n-1; p=x+m; f

17、or(;i=p;i+,j-) t=*i; *i=*j; *j=t; main() int i,a10,*p=a; for(i=0;i10;i+,p+) scanf(%d,p); p=a; inv(p,10); printf(The array has been reverted:n); for(p=a;pa+10;p+) printf(%d,*p); ,实参与形参均用指针变量,例 将数组a中的n个整数按相反顺序存放,void inv(int x, int n) int t,i,j,m=(n-1)/2; for(i=0;i=m;i+) j=n-1-i; t=xi; xi=xj; xj=t; ma

18、in() int i,a10,*p=a; for(i=0;i10;i+,p+) scanf(%d,p); p=a; inv(p,10); printf(The array has been reverted:n); for(p=arr;parr+10;p+) printf(%d ,*p); ,实参用指针变量,形参用数组,二、指针与数组,1、一维数组中的有关规定,#define N 9 main() int a10,i,*p; clrscr(); p=a; for(i=0;iN;i+) scanf(%d, ,Lets try 运行程序,观看结果 在scanf语句中,分别用a+i、p+i和 其中0

19、i10,则对a数组元素不正确的引用是 。 A)ap-a B)*( int *p,i,j=0; system(cls); p=a0; /*p=a*/ /* for (i=0;i9;i+) */ printf(%dn,*(a0+1); ,Lets try 1、将a0改为 a1、a2 int *p,i; system(cls); p=a0; for (i=0;i3;i+) printf(%d ,a+i); printf(%d %d %dn, ,改成*(a+i)、ai分别试试 再把%d改为%p试试,对于二维数组: (1)a是数组名, 包含三个元素 a0,a1,a2 (2)每个元素ai 又是一个一维 数

20、组,包含4个 元素,int a34;,基类型,行指针与列指针,对二维数组 int a34,有 a-二维数组的首地址,即第0行的首地址 a+i-第i行的首地址 ai *(a+i)-第i行第0列的元素地址 ai+j *(a+i)+j -第i行第j列的元素地址 *(ai+j) *(*(a+i)+j) aij,a+i= int *p,i,j; system(cls); p=a; for (i=0;i3;i+) for(j=0;j3;j+) printf(%d ,*(*(a+i)+j); ,改成aij试试,用简单指针变量指向二维数组时,用简单指针变量指向二维数组时: int a1010,*p; /*p=

21、a;*/p=a0; p都是列地址性质的指针(姑且称“列指针”)。此时,p可与“排队法”中的a0互换使用,但不能与“行列法”中的a互换使用。,比较一下,#include main() int a33=1,2,3,4,5,6,7,8,9; int *p,i; system(cls); p=a; for (i=0;i9;i+) printf(%dn,*(p+i); ,1 2 3 4 5 6 7 8 9,换成*(a0+i)试试,再看下一个例子,#include main() int a33=1,2,3,4,5,6,7,8,9; int *p,i,j; system(cls); p=a; for (i=

22、0;i3;i+) for(j=0;j3;j+) printf(%d ,*(*(a+i)+j); ,a改成p试试,【讨论】 如果将输出语句中的*(*(a+i)+j)改为*(*(p+i)+j)可不可以?,不行!,Why?!,因为p不是“行指针”!,行指针 P229,形式: int (*p)n 含义:p为指向含有n个元素的一维数组的指针变量。 使用:二维数组可以视为由若干一维数组组成。 行指针p是行地址性质的指针。此时,p可与“行列法”中的a互换使用,但不能与“排队法”中的a0互换使用。,行指针是如何使用的?,若 int a45; int (*p)5; p=a; 则 (*p)0=a00; (*p)1

23、=a01; (*p)2=a02; (*(p+1)0=a10; (*(p+1)1=a11; ,行指针是一种行地址,可以与二维数组用数组名表示的行地址互换使用。 事实上,有 (*(p+ i)j=pij= * (*(p+ i)+j)=aij;,示例一,main() int a33=1,2,3,4,5,6,7,8,9; int (*p)3,i,j; p=a; for(i=0;i3;i+) for(j=0;j3;j+) printf(%d ,*(*(p+i)+j); ,结果:1 2 3 4 5 6 7 8 9,示例二: 二维数组与指针运算,main() int a34=1,2,3,4,3,4,5,6,5

24、,6,7,8; int i; int (*p)4=a,*q=a0; for(i=0;i3;i+) if(i=0) (*p)i+i/2=*q+1; else p+,+q; for(i=0;i3;i+) printf(%d,aii); printf(“%d,%dn,*p,*q); ,运行结果:2,4,7,5,3,2,示例三,若有以下定义和语句,且0i4,0j3,则不能访问a数组元素的是 。 int i, (*p)3; int a 3=1,2,3,4,5,6,7,8,9,10,11,12; p=a; A)*(*(a+i)+j) B)pij C)(*(p+i)j D)pi+j,答案:D (pi+j是个

25、地址),示例四,main() char a310=abc,123456,ABCDE; char (*p)10; p=a; printf(%s,%sn,p+1,*(p+1); printf(%c,%c,%cn,*(*(p+1),*(*(p+2)+1),(*(p+2)1); ,结果:123456,123456 1,B,B,示例五,以下程序运行结果是 。 main() char aa 3=a,b,c,d,e,f; char (*p)3=aa; int i; for(i=0;i2;i+) if(i=0) aaii+1=*(p+); printf(% cn,*p); ,答案:d,说明 行指针p实际上是一

26、个二级指针,*p相当于取行首地址,*p相当于取行首元素的值。因为*和+优先级相同,且均为自右向左结合,所以*(p+)和*p+一样,均表示选取*p值,然后p+(移到下一行)。 本程序运行后,数组aa的值为a,a,c,d,e,f,字符串表示形式 用字符数组实现,例 main( ) char string=“I love China!”; printf(“%sn”,string); printf(“%sn”,string+7); ,指针与字符串,用字符指针实现,例 #include main( ) char *string=“I love China!”; printf(“%sn”,string);

27、 string+=7; while(*string) putchar(*string); string+; ,字符指针初始化:把字符串首地址赋给string char *string; string=“I love China!”;,*string!=0,指针与字符串,【例】求以下程序的运行结果。 main() char *pc=#Fujian#Province#; while (*pc) while(*pc=#) pc+; if (*pc=0) break; printf(%c,*pc); pc+; printf(n); ,结果:FujianProvince,许多字符串操作通常使用指针和指针

28、运算来实现,因为字符串趋向于严格 地逐一访问的形式,指针与字符串,【例二】以下函数的功能对应于 。 int fun(char *s,char *t) while ( (*s) A)strlen(s)+strlen(t) B)strcmp(s,t) C)strcpy(s,t) D)strcat(s,t),结果:B,多数字符串函数在涉及到循环控制时,均采用与strcmp()函数 相似的指针方式。这种方式比使用下标更快,有效且更容易理解。,例 main() char *p1,s80; p1=s; do gets(s); while (*p1) printf(“%c”,*p1+); while(str

29、cmp(s,”done”); ,Lets try,指针与字符串,字符串指针作函数参数,例 用函数调用实现字符串复制,(1)用字符数组作参数,(2)用字符指针变量作参数,void copy_string(char from,char to) int i=0; while(fromi!=0) toi=fromi; i+; toi=0; main() char a=I am a teacher.; char b=You are a student.; printf(string_a=%sn string_b=%sn,a,b); copy_string(a,b); printf(nstring_a=%

30、snstring_b=%sn,a,b); ,void copy_string(char *from,char *to) for(;*from!=0;from+,to+) *to=*from; *to=0; 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); ,字符指针变量与字符数组 char *cp; 与 char str20; str由若

31、干元素组成,每个元素放一个字符;而cp中存放字符串首地址 char str20; str=“I love China!”; () char *cp; cp=“I love China!”; () str是地址常量;cp是地址变量 cp接受键入字符串时,必须先开辟存储空间,例 char str10; scanf(“%s”,str); () 而 char *cp; scanf(“%s”, cp); (),改为: char *cp,str10; cp=str; scanf(“%s”,cp); (),字符串与数组关系 字符串用一维字符数组存放 字符数组具有一维数组的所有特点 数组名是指向数组首地址的地

32、址常量 数组元素的引用方法可用指针法和下标法 数组名作函数参数是地址传递等 区别 存储格式:字符串结束标志 赋值方式与初始化,char str=“Hello!”; () char str=“Hello!”; () char str=H,e,l,l,o,!; () char *cp=“Hello”; () int a=1,2,3,4,5; () int *p=1,2,3,4,5; (),char str10,*cp; int a10,*p; str=“Hello”; () cp=“Hello!”; () a=1,2,3,4,5; () p=1,2,3,4,5; (),指针数组,定义: char

33、*p5; 功能:定义数组p5,其每个元素 p0、p1、p4都是指针变量。通常用于指向一组字符串。 此时,对于pi,其下标表示第i个字符串,pi本身是第i个字符串的首地址。,指针数组赋值与初始化,char name59=“gain”,“much”,“stronger”, “point”,“bye”;,char *name5=“gain”,“much”,“stronger”, “point”,“bye”;,二维数组与指针数组区别:,二维数组存储空间固定 字符指针数组相当于可变列长的二维数组 分配内存单元=数组维数*2+各字符串长度,指针数组元素的作用相当于二维数组的行名 但指针数组中元素是指针变量

34、 二维数组的行名是地址常量,示例,main() char *str=AA,BB,CC; str1=str2; printf(%s,%s,%sn,*str,str1,*(str+2); ,结果:AA,CC,CC,示例 对字符串排序(简单选择排序),main() void sort(char *name,int n), print(char *name,int n); char *name=Follow me,BASIC, Great Wall,FORTRAN,Computer ; int n=5; sort(name,n); print(name,n); void sort(char *name

35、,int n) char *temp; int i,j,k; for(i=0;i0) k=j; if(k!=i) temp=namei; namei=namek; namek=temp; ,示例,serror(int num) static char *err = “can not open filen”; “read errorn”; “write errorn”; “media failuren” ; printf(“%s”,errnum); ,指针数组常用于指向出错信息的指针,三、指针与函数,1、指向函数的指针变量 P241 可以用指针变量指向一个函数。一个函数在编译时被分配给一个“入口

36、地址”。 定义方法: 类型标识符 (*指针变量名)(); 如 int (*p)( ); 指向一个返回整型值的函数 用法: 设有函数fun(a,b) 令p=fun; 则有 (*p)(a,b) 相当于 fun(a,b); 此时c=(*p)(a,b ) 与c=fun(a,b)等效。,【例1】求a和b中的大者 main() int max(); int a,b,c; scanf(“%d,%d”, ,改为: main() int max(); int (*p)(); int a,b,c; p=max; scanf(“%d,%d”, ,#include #include #include #include

37、 main() int strcmp(); int numcmp(); char s180,s280; gets(s1); gets(s2); if(isalpha (*s1) check(s1,s2,strcmp); else check(s1,s2,numcmp); ,check(char *a,char *b, int (*cmp)() ) printf(“testing for equalityn”); if(!(*cmp)(a,b) printf(“equal”); else printf(“not equal”); numcmp(char *a,char *b) if(atoi(a

38、)=atoi(b) return 0; else return 1; ,例2,例3 用函数指针变量作参数,求最大值、最小值和两数之和,三、指针与函数,2、返回指针值的函数 一般形式 int *a(x,y); 表示该函数的返回值是一个指针。,例 4 :有n个学生,每个学生有4门课程的成绩。要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现,main() float score 4= 60,70,80,90,56,89,67,88,34,78,90,66; float *search(float (*pointer)4,int n); float *p; int i,m; printf(“enter the number of student:”); scanf(“%d”, ,如果pt=*pointer+n; 结果怎样?,例5 : 写一个函数,求两个int型变量中居于较大值的变量的地址,2,3,2002,2000,*,例5 写一个函数,求两个int型变量中居于较大值的变量的地址,2002,例6 写一个函数,求两个int型变量中居于较大值的变量的地址,2,3,3,2,*,例 写一个函数,求两个int型变量中居于较大值的变量的地址,不能返回形参或局部变量 的地址作函数返回值,200A,本章作业,1、所给程序段是把从键盘输入的一行字符作为字符

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论