1、第八章,地址和指针,指针的概念,指针类型是C语言中使用十分普遍的数据类型,它与一般的变量不同之处是它包含的不是数据的值,而是另一变量的地址。 指针是C语言中的一个重要概念,正确而熟练地掌握了指针的概念和指针的使用,就能设计出复杂的数据结构和高效的程序,没有掌握指针就没有掌握C语言的精华。,凡是程序中定义的变量,在编译时系统都给他们分配相应的存贮单元,VC6.0系统给短整型分配2个字节,给整形和实型分配4个字节,每个变量所占的存贮单元都有确定的地址,具体地址在编译时分配。,指针的概念,C程序的变量所存放的数据:, 数值型数据:整数、实数,通过前面的学习,我们已知道:, 字符型数据:字符、字符串,
2、 占有一定长度的内存 单元 如:int x; x占4字节、4个单元, 每一个变量都有一个地址,为无符号整数,它不同于一般的整数。,问题: 能否对地址运算?, 能否用一个变量保存地址?,这些变量具有的性质:,例: short int a=3, b=4; float c=4.5,d=8.6; char e=x,f=y;,1010,1012,1014,a,1018,1022,1023,b,c,d,e,f,3,4,4.5,8.6,x,y,要访问内存中的变量,在程序中是通过变量名来引用变量的值。但实际上,在编译时将每个变量名对应一个地址,在内存中不再出现变量名而只有地址。,若程序中引用变量a,系统找到对
3、应地址1010,然后从1010,1011两个字节中取出其中的值。,一、数据在内存中的存放,设:系统分配i的起始地址为2000的单元,内存:为一个连续编号(连续地址)且以一个单元为一个字节的连续存贮区。 若程序中定义了三个int变量i, j, k short int i= 5, j=5, k=10;,则: j的起始地址有可能为为2002的单元,k的起始地址有可能为2004的单元,当程序中要用它们的值时: y=i+j+k;,找到j的地址2002,将2002,2003中的数据5读出;,找到k的地址2004,将2004,2005中的数据10读出。,分别 找到i的地址2000,将2000,2001中的数
4、据5读出;,则系统通过一张变量名与地址对应关系表:,上述过程称为变量的 “直接访问”,然后把这些数据进行算术运算。,直接访问:直接使用存放该数据的变量名。,如:用pi, pj, pk来存放i, j, k的地址,若要得到变量i的值,可以先访问pi,得到i的地址,再通过该地址找到它i的值。显然,pi与i是通过i的地址联系起来的。,间接访问:如果将某一变量的地址(如i的地址2000)存放到另一个变量x,则可通过x来存取i的值。,直接访问:通过变量名或地址访问一个变量的方式为“直接访问”。,间接访问:把地址存放在一个变量中,然后通过先找出地址变量中的值(一个地址),再由此地址找 到最终要访问的变量的方
5、法称为“间接访问”,注: 存放地址的变量是一种特殊的变量,它只能用来存放地址而不能用来存放其它类型的数据,需要专门加以定义。,8.2 指针和地址,指针变量定义:一个变量的地址称为该变量的指针。因此,i的指针的值为2000。而存放地址(指针)的变量叫做指针变量。,Pi就是指针变量,指针变量的定义与初始化:,表示该变量为指向某类型变量的指针变量。,存储类型 类型名 变量名,注,1. 标识符前面的“*”标示该变量为指针变量。2. 一个指针变量只能指向同一类型的变量。,指针变量的定义,int *p, i=3;,如: int p; (p为指向整型变量的指针) char *s;(s为指向字符型变量的指针)
6、 float *t;(t为指向浮点型变量的指针),p中只能存放整型变量的地址,习惯用语:若指针变量p存放了变量a的地址,我们称“p指向a”,请区别: 指针:就是地址 变量的指针:就是变量的地址 指针变量:存放地址的变量,重要概念: 指针变量也有各种类型(如上面所示),但指针变量的值只能是整型值。,指针变量的引用方式: *变量名-表示所指变量的值。 变量名-表示指向变量的指针(地址)。,如 int *p; char *s; float *t; *p=5; *s=a; *t=3.6; 但 p=p+1; 并不代表p=6,它代表的是地址1,若有:float *t;且 *t=3.6; 设t的地址为200
7、0,则t+12004,例:若有:int *p 且 *p=5; 设p的地址为2000,则p+12004,若有:char *s 且 *s=a; 设s的地址为2000,则s+12001,三、引用指针变量,将一个变量的地址(指针)赋给一个指针变量,用取地址运算符:&,int i, j, p; i=3; p=,求出整型变量i的地址付给指针变量p,例:&k 取变量k地址 &c2 取数组元素c2的地址 &(st.name) 取结构st变量name项的地址 &233, &(i+233),&:(取地址运算符)取当前变量的地址 运算对象不能是常量表达式或寄存器变量,p=,存取指针变量所指向变量(目标变量)的值:用
8、指针运算符“ * ”, 即:*p 为 i。 , &为同级运算符,结合性自右至左。当&或&在一起时,具有抵消作用。,则:p=&i相当于p=i,如上例:,int i, p ;i=3;p=,指针变量不要谈”指”色变,指针是C语言学习中的一大难点。难难在概念。,main() int a,*p1,*p2= ,首先搞定*p,请看以下变量声明语句 int a,*p1; char b,*p2; a,b 普通变量(存放某个数值或字符) p1,p2 指针变量(存放某个实体的地址),变量声明时,如果变量名前带 *号,表示该变量是个指针变量,有关*p的小结,变量声明时,*p表示定义了一个用来存放变量地址而非数据(数值
9、、字符等)的指针变量。程序中引用时,*p表示取指针变量p所指变量的值。,main() int a,*p1,*p2= ,int *p; p=,小考一下,如何?,以下程序的运行结果是什么?main() float x,y; int *p; x=3.14; p=,结果:y=-2621.000000,把int *p改为float *p后,结果正确:y=3.140000,指针变量能参加运算吗?,指针变量和其他变量一样,可以在各种表达式中参加运算。但指针变量和普通变量不同,只能进行以下三种运算:赋值运算算术运算 指针比较,C语言中有关指针的运算符 & 运算符: 取地址运算符 * 运算符: 指针运算符或指明
10、运算符,*p代表p所指变量注意:此处的*p与定义指针变量时用的*p的含义是不同的。,定义 int *p; 中的 * 不是运算符, 它只是表示其后的变量是 一指针变量程序中的 *p,其中的*是一个指针运算符,*p表示p指向的变量,如:printf(“%d”,*p); printf(“%d”,a);,结果都为,3,指针运算,指针变量初始化 变量声明时赋值,main( ) int a=5,*p= printf(“%d,%d,%dn”,p,*p,a),指针变量一般赋值 程序处理时赋值,结果:2000,5,5,1. 指针变量赋值运算,例 指针赋值运算。# include main()int a,b;a=
11、100;b=200;int *pa, *pb; /*定义int类型的指针变量*/pa= /*将变量a的地址赋给指针变量 pa */,pb=,运行结果:*pa=100,*pb=200*pa=200,*pb=200,2. 指针变量算术运算,(1)指针变量与整数相加减,指针变量加上或减去一个整数n,相当于指针变量从当前的位置向后或向前移动m个内存单元,其中m的值为:m = n*指针数据类型的长度 不同数据类型占据不同的长度,char类型占1个字节,short int类型占2个字节,int,float类型占4个字节。,(2) 指针和整数可以进行加减,例如: p是指向某数组的第一个元素的指针,则p+n就
12、表示这数组第n+1个元素的位置。 一般说来:p+n表示超过指针p的当前位置的第n个对象的地址。在计算具体地址时,编译程序根据p所指对象的长度(sizeof)将n放大。,(3) 空指针不能指向任何对象,3. 指针变量的关系运算,p和q是两个相同类型的指针变量,则它们之间存在p= =q, p!=q, pq, p=q等关系。,例如: int *p,i=2; p=执行后,p的内容是多少呢?因为p是int类型的变量,所以占的长度为4个字节,不难得出p的值为3000+243008。要注意,这里不是简单的p加2就行了,所以p的值不是3002。,指针变量的初始化和指针所指的变量一般形式:存储类型 数据类型 *
13、指针名=初始地址值;,赋给指针变量,不是赋给目标变量,例 int i; int *p=,变量必须已说明过类型应一致,例 int i; int *p=,用已初始化指针变量作初值,注: 指针变量的使用,例 main( ) int i=10; int *p; *p=i; printf(“%d”,*p); ,危险!,例 main( ) int i=10,k; int *p; p= ,指针变量必须先赋值,再使用,空指针,空指针: int *p; p=NULL;,NULL是什么?在stdio.h中,定义#define NULL 0所以 p=NULL;相当于 p=0;,内存使用常识:任何C程序的变量在内存中
14、的地址均由操作系统自动分配,不能由编程者通过赋值指定。p=NULL 表示p不指向任何变量。内存的低端只供由操作系统使用(相当于政府机关,普通百姓不能使用)。,例,main( ) int *p1,*p2,i1,i2; scanf(“%d, %d”,i1,i2); p1=,运行情况: 3,5,3,53,3,main( ) int *p1,*p2,*p,i1=3,i2=5; p1=,例,运行情况:,5,3,P1,P2,*p1,*p2,p1=&i1,p2=&i2,p=p1,P,*p,&i1,p1=p2,&i2,p2=p;,&i1,main( ) int *p1,*p2,i,i1,i2; i1=3; i
15、2=5; p1=,例 交换两指针变量所指向的值,运行情况:,i1=5, i2=3,P1,3,i1,P2,5,i2,*p1,*p2,i,*p,3,5,&i2,p1=&i1,p2=&i2,i=*p1,*p1=*p2,*p2=i;,3,4、指针使用的几个细节。,设指针p指向变量a则: p+(或 p += 1),p指向下一个元素。 *p+,相当于*(p+)。因为,*和+同优先级,+是右结合运算符。 *(p+)与*(+p)的作用不同。 *(p+):先取*p,再使p加1。 *(+p):先使p加1,再取*p。 (*p)+表示,p指向的元素值加1。,8.2 指针与函数,指针和函数的关系主要有三个方面:,一是指
16、针可以作为函数的参数,二是函数的返回值可以是指针,三是指针可以指向函数,在前面介绍函数时,已经介绍了C语言的函数的参数传递是以“传值”方式进行变量参数的信息传递,被调函数不能直接改变主调函数中参数的值。 当引入指针的概念后,我们可以在主调函数中将要改变内容的变量地址作为参数传递给被调函数,而被调函数执行时,就按这个地址去访问变量参数的值,相应的参数要被说明成指针类型。,一、指针作为函数参数,例如:main( ) void sub(int *px, int *py ); int x, y; sub( ,void sub(int *px, int *py) *px=10; *py=20;,xy,p
17、xpy,1012,1014,10,20,运行结果:10,20,结论:用指针作函数的参数,可以实现“通过被调函数改变主调函数中变量的值”的目的。,1012,1014,1. 指针作形参,实参为变量地址或指针,例 9.4 编写一程序,通过函数输出两个数中的最大值。,#include void max(int *i, int *j) /*定义指针作形参的函数max */ if(*i*j) printf(max:%dn,*i); else printf(max:%dn,*j);,main( ) int i=100, j=200; int *pi, *pj; pi= /*用指针作为函数的实参*/,运行结果
18、:max: 200max: 200,(1) 数据复制方式 例 9.5 通过函数交换数据程序a。,2. 参数的传递方式,函数的参数传递方式有两种,数据复制(亦称为按值传递)和地址传递。,main() int a=5, b=9; swap(a, b); printf(a=%d, b=%d, a, b);运行结果:a=5,b=9,请思考:结果为什么没有交换呢?,#include void swap(int x,int y) int temp; temp=x; x=y; y=temp;,参数的传递方向:单向值传递,而且是由调用者传给子函数.,swap(int x, int y) int temp; t
19、emp=x; x=y; y=temp;main() int a=5,b=9; swap(a,b); printf(a=%d, b=%d, a, b);,5,9,5,5,9,COPY,swap(int x,int y) int temp; temp=x; x=y; y=temp;main() int a=5,b=9; swap(a,b); printf(n%d,%dn,a,b);,值传递,5,9,运行结果:5, 9,分析:,函数调用时,程序把a,b的值5,9传递给x,y,在swap( )函数中x,y交换之后,没有把它们的结果返回给实参a,b,所以a,b并没有交换,交换的只是x,y。a与x,b与y
20、占用的是不同的内存单元。,数据复制方式传递数据时,由于数据在传递方和被传递方占用不同的内存空间,所以被调用函数中的变量不管怎么变化,都不会影响到调用函数中的实参的值。这就是上面程序中两个数没有交换的根本原因。,(2)地址传递方式,main() int a=5,b=9; int *pa,*pb; pa=运行结果:a=9,b=5,这次为什么交换了?,#include void swap(int *p1, int *p2) int p; p = *p1; *p1 = *p2; *p2 = p;,swap(int *p1, int *p2) int p; p=*p1; *p1=*p2; *p2=p;m
21、ain() int a=5,b=9; int *pa,*pb; pa=,5,9,2000,2002,5,9,COPY,5,例 8.5 将数从大到小输出,swap(int *p1, int *p2) int p; p=*p1; *p1=*p2; *p2=p;main() int a=5,b=9; int *pa,*pb; pa=,5,9,2000,2002,5,9,例 将数从大到小输出,运行结果:9,5,地址传递,分析:,在调用swap(pa,pb);函数时,就把变量a的地址给了指针pa,变量b的地址给了pb,也就是,指针pa访问的就是变量a,指针pb访问的就是变量b;a与*pa共用同一内存单元
22、,b与*pb也是共用的同一内存单元。所以,当用指针pa和pb将数据交换了之后,实际上a和b内的数据也被交换了。,&与*组合使用时,若 int a, *p; p= *&a = a = *p,返回指针值的函数(指针函数),一个函数可以带回一个整型值、字符型值、实型值等,也可以带回指针型的数据,即地址。 返回指针值的函数的一般定义形式为:,类型标识符 *函数名(参数表),例如:int *fun(int a,int b) fun是函数名, 调用它以后得到一个指向整型数据的指针(地址)。 a,b是函数fun的形参。,例如:int *fun(int a,int b),上面这个函数fun( )即是一个指针函数,要求返回值是int类型。,在指针型函数中,使用return语句返回的可以是变量的地址、数组的首地址或指针变量,还可以是后面几章介绍的结构体、共用体等构造数据类型的首地址等。,例 指针型函数来找出两个数中的最大值。,#include int *max(int *i, int *j)return (*i*j ? i:j); /*返回最大数的指针*/main() int a, b, *p; scanf(%d, %d, ,运行结果:输入:12,2输出:max=12,