C语言指针.docx
- 文档编号:7423517
- 上传时间:2023-01-23
- 格式:DOCX
- 页数:38
- 大小:43.86KB
C语言指针.docx
《C语言指针.docx》由会员分享,可在线阅读,更多相关《C语言指针.docx(38页珍藏版)》请在冰豆网上搜索。
C语言指针
第八章 指针
本章主要内容:
指针与地址 1课时
指针变量的使用 1课时
指针与数组 2课时
指针在函数中的使用 2课时
指针和字符串 1课时
指针数组与多级指针 1课时
指向函数的指针 1课时
重点、难点:
指针概念、指针与数组、函数、字符串的关系
上机试验:
4课时
第一节指针与地址
内存储器为程序的存放和执行提供场所,而内存储器中存储单元的组织是按照线性结构来实现。
为了能够正确的访问这些单元,必须为每个单元进行编号,每个存储单元的编号称为地址。
程序运行之后,系统根据存储对象的类型在内存中分配相应的存储空间。
在TurboC系统中,为char类型对象分配1byte,为int类型对象分配2byte,为float类型对象分配4byte,为double类型对象分配8byte。
除了char类型之外,其它类型的对象都要分配多个字节。
由于为它们所分配的空间都是连续的,因此用连续分配的第一个单元的地址作为该对象的内存地址。
例8.1 int a;
float b;
char c;
double d;
假设它们的空间分配如图8.1,他们的地址分别为:
3000,3002,3020,2040。
为了将存储单元的地址与变量之间的关系有一个清晰的认识,我们来看一个形象的例子:
假设办公楼2楼每个办公室都有一个门牌号,201为教务处处长办公室,202为教务处实验科办公室,203为教务处教学科办公室,……。
这些房间就相当于存储单元,而房间号相当于地址。
处长办公室,实验科办公室,教学科办公室,……就相当于变量名。
因而变量对应存储单元,而变量名是给存储单元取得符号名。
可见存储单元对应于一个地址,又对应一个变量名。
因此,对存储单元的访问,既可以通过变量名来实现(前面所介绍的一般是采用这种方式),又可以通过地址来访问。
这正是C语言有别于其他高级语言的特点之一。
而存储对象的地址又称为指向该对象的指针。
因此指针实际上是内存单元的地址码。
指针类型与其他数据类型一样,也有相应的变量——指针变量。
指针变量是用来存放其他对象的内存地址。
如:
int a;
int*p=&a;
p为指针变量,用来存放变量a对应的内存单元的地址。
对于a的存单元,既可以用变量名a访问,也可以用指针p来实现访问:
*p
此时*p和a的值一样,即p中存放a的地址,而*p代表a。
注意:
● int*p=&a;中“*”相当于一特殊类型说明符,说明p是指针型变量。
在定义p的同时,将系统为a所分配单元的地址赋给p(而不是给*p),在程序的其他地方只能用p=&a;
● 要区分指针与指针变量的概念:
指针是一地址码,为数据对象所分存储空间的首地址;而指针变量是一个数据对象,需要分配存储单元,其中存放的是地址码(即指针),并且只能存放地址,不能存放其他数据。
第二节指针变量的使用
一、指针变量的定义及赋值
指针变量与其他变量一样,都是变量,都需要通过定义来分配存储单元,然后才能使用。
当然也可以在定义的同时进行初始化。
指针变量定义的一般形式为:
基类型* 指针变量名;
例如:
int*p;
表示定义一个指针变量p。
定义中的“*”表示该变量为一个指针变量而不是其他类型的变量,而前面的基类型,不是指针变量p所存放的类型,而是p的值所指向变量的数据类型,如上例中,p所存放的指针所指向的类型为int。
注意:
在定义中的“*”虽和前面所介绍的类型标识符有同样的功能,但只对一个变量名有效,如果要定义多个指针变量,需要多个“*”。
如:
int*p1,p2,*p3;
表示定义了两个指针变量p1和p3,而p2为一整型变量。
在定义指针变量的同时,也可以初始化,其初值为基类型变量的地址。
如:
inta;
int *pa=&a;
在定义指针变量pa的同时将整型变量a的地址赋值给它。
注意:
✍ 要求在赋初值时,初值对应的变量必须先定义。
上例可以写成:
inta,*pa=&a;
但不能写成:
int*pa=&a,a;
✍ 要求初值对应变量的类型要与基类型相同。
即一个指针变量只能指向同一种类型的变量。
如:
int a;
float*p=&a;
这种初始化是错误的。
✍ 对指针变量初始化时,只能给他赋地址,而不能是其他类型的数据。
虽然地址是存储单元的地址码,是整数,但一般不能将整数作为初值。
因为程序中变量的地址只能由编译程序分配而不能人为指定。
如:
int *p=1000;/*错误赋值*/
但可以将0赋值给指针变量,表示该指针未指向任何对象,为空指针。
如:
float*fp=0;/*表示fp未指向任何float对象*/
与其他变量一样,指针变量除了初始化赋值之外,还可以在定义之后给它赋值,让它指向一个对象。
如:
int a,*p;
p=&a;
二、 指针的类型
对于不同的数据类型,其对象在内存中要占用不同数目的存储单元,并且其运行方法也完全不同。
如在TurboC下,int类型数据占2byte,float类型数据占4byte,他们的运算一种是整型运算,一种是浮点运算。
但对于指针变量来说,不管它指向的是什么类型的对象,他们的值都是地址,因而都占有相同数目的单元,在TurboC为2byte。
通过指针变量可以访问所指向的对象,进行操作与运算。
由于不同类型的数据占用的单元数不一样,进行的运算也不相同,因而也要对指向它们的指针变量进行区别,即指针变量也有类型。
指针变量的类型就是它所指向对象的类型。
如:
doube d=3.5;
double*dp=&d;/*dp为一个double类型的指针变量*/
一个指针变量要有相应类型,并且只能指向同一类型,如dp只能指向double类型的变量,而不能指向其他类型的变量。
注意:
要区分开指针变量的值和指针变量所指向变量的值。
如上例中,指针变量dp的值为变量d的地址,所指向变量的值为d的值,即为3.5。
三、 指针运算符
C语言提供了两种指针运算符:
取地址运算符(&)和引用目标运算符(*)。
1.取地址运算符(&)
取地址运算符是单目运算符,其结合性为从右→左,其功能是取变量的地址,使用格式为:
&左值
左值是具有内存单元的数据,如变量、数组元素及结构变量和联合中的数据成员都是左值,可以用“&”运算符取得它们的地址,因而&左值是一个指针表达式,可以用它来作为指针变量的值。
注意:
要区分开取地址运算符&与双目运算符&(按位与)。
2.引用目标运算符(*)
与取地址运算符“&”相对应的是引用目标运算符“*”。
该运算符也是单目运算符,结合性为从右→左,这种运算符只能作用在指针表达式的数据上。
其使用格式为:
*指针表达式
功能:
“*”运算符形成的表达式表示指针表达式所指向的对象,通过它可以完成对该对象的访问:
读和写。
例8.2 分析下面程序运行结果。
main( )
{
int n,*pi;
double d,*pd;
pi=&n;
pd=&d;
printf("pleaseinputnandd:
");
scanf("%d%lf",pi,pd);
printf("n=%d,*pi=%d\n",n,*pi);
printf("d=%f,*pd=%f\n", d,*pd);
}
程序运行结果为:
pleaseinputnandd:
23 76.8↙
n=23,*pi=23
d=76.800000,*pd=76.800000
通过scanf()函数中使用pi、pd实现对指向对象的赋值。
在printf()函数中通过*pi、*pd实现对变量n和d的输出(即变量的读操作)。
也可以在一个语句中同实现对指向对象的读和写操作,如:
int n=10,*pi=&n;
(*p)++;
首先通过*p取出变量n的值作为表达式的值,然后再将该值加1之后,又赋值给变量n。
注意:
✍ 在对指针变量使用“*”运算符时,要求指针变量已经指向了一个确定的对象。
也即对指针变量必须先赋值,再使用“*”运算符。
✍
(*p)++和*p++之间的关系:
(*p)++表示对p所指向的对象加1修改,p仍指向原来的对象;而*p++表示先通过p去访问所指向的对象,再让p指向下一个对象,它等价于*(p++);
✍ 几个等价关系:
假设有int a, *p=&a;则有如下等价关系:
① *p与a等价,等价于*&a;
② p与&a等价,等价于&*p;
例8.3 分析下面程序的运行结果:
main( )
{
int a=10, b=20;
int*pa,*pb,*p;
pa=&a,pb=&b;
printf("a=%d,b=%d,*pa=%d,*pb=%d\n",a,b,*pa,*pb);
p=pa; pa=pb;pb=p;
printf("a=%d,b=%d,*pa=%d,*pb=%d\n",a,b,*pa,*pb);
}
运行结果为:
a=10,b=20,*pa=10,*pb=20
a=10,b=20,*pa=20,*pb=10
指针值的变化过程如图8.2。
说明:
图中的“
”表示指针值所指向的对象,“ ”表示存储单元之间值的拷贝,本章所有图形中的这两种箭头均表示相同意思。
例8.4 分析下面程序的运行结果。
main( )
{
int a=10,b=20,t;
int*pa,*pb;
pa=&a,pb=&b;
printf("a=%d,b=%d,*pa=%d,*pb=%d\n", a, b,*pa,*pb);
t=*pa,*pa=*pb,*pb=t;
printf("a=%d,b=%d,*pa=%d,*pb=%d\n", a, b,*pa,*pb);
}
运行结果为:
a=10,b=20,*pa=10,*pb=20
a=20,b=10,*pa=20,*pb=10
通过指针交换所指对象的过程如图8.3。
四、指针常量
同其他类型一样,有变量就有常量。
在C语言中,指针常量有:
数组名、函数名、字符串常量等。
数组名代表该数组在内存中分配的存储区的首地址,指向数组中的第一个元素,实际上就是指针类型的数据。
由于数组分配空间之后面,直到其生命期结束之前不会重新分配空间,因而数组名为指针常量。
函数代码存放在代码区,在程序的整个运行期,它不会改变,而函数名正好代表函数在代码区的首地址,因而函数名也是指针常量。
如:
int a[100],*p;
a就代表数组的首地址,指向第一个元素a[0],而p为一般的指针变量。
对于数组名a和一般的指针变量p它们的区别:
✍ a是指针常量,不能修改它的值,它总是代表数组的首地址;而p为变量,可以重新赋值,即让它指向不同的对象。
✍ a的意义是代表一个能存放100个int类型的存储区;而p在赋值之前,未指向任何有意义的单元。
第三节 指针与数组
在C语言中指针与数组的关系非常密切。
一、 指针与数组名之间的关系
前面已经介绍过数组名代表整个数组空间的首地址,指向的是数组中的第一个元素,而指针也是地址,因此数组名与指针都具有完全相同的数据类型,这使得他们的运算都具有通用性。
对数组元素的访问,是通过数组名利用下标运算符“[]”来实现访问,即:
数组名[下标]
可以转化为:
地址[整数]
也即:
(指针表达式)[整数]
例8.5 分析下面程序的运行结果。
#define N 5
main( )
{
int a[N],*p,i;
for(i=0;i a[i]=i+10; p=a; for(i=0;i printf("%5d",a[i]); printf("\n"); for(i=0;i printf("%5d",p[i]); } 运行结果为: 10 11 12 13 14 10 11 12 13 14 由上例可知: a[i]等价于p[i],说明指针变量也可以用下标方式来访问对应单元中的值。 二、 定义指向数组元素的指针变量 定义指向数组元素的指针变量与指向简单变量的指针变量完全一样。 首先定义与数组元素类型一致的指针变量,然后再将数组元素的地址赋给指针变量,最后通过指针变量来引用数组元素。 例8.6 分析下面程序的运行结果。 #define N 5 main( ) { inta[N],*p,i; for(i=0;i { p=&a[i]; *p=i*2; } for(i=0;i { p=&a[i]; printf("%5d",*p); } } 运行结果为: 0 2 4 6 8 通过指针引用数组元素实现了对数组元素的读和写操作。 三、 指针的运算 指针是一种特殊的类型,它是以指针变量所持有的地址作为运算对象进行运算。 除了前面介绍的“*”和“&”可以使用,还有指针的加、减、比较等运算,而这些运算主要与数组操作相关。 1.指针与整数的加减运算 一个指针可以加上或减去一个整数,包括加1、减1。 前面介绍的数组元素访问方式为: a[i],C语言中,实际上是先将a[i]转换成*(a+i)的形式然后再去求值。 这表明a+i是数组中第i+1个元素的地址,i为数组元素a[i]相对于a[0]的偏移量。 一个地址加减上一个整数其结果仍为地址,并且加减的单位不是以字节为单位,而是以指向的数据类型所占用的字节数为单位。 如int指针,以2byte为单位,double指针,以8byte为单位。 因此,p+n表示的实际地址为(假设p指针的基类型为type): p+n*sizeof(type) 例8.7 分析下面程序运行结果。 main( ) { int a[10]={1,2,3,4,5,6,7,8,9,10}; int *p=a; printf("ais: %X,a+3is: %X\n",a,a+3); printf("pis: %X,p+3is: %X\n",p,p+3); printf("*ais: %d,*(a+3)is: %d\n",*a,*(a+3)); printf("*pis: %d,*(p+3)is: %d\n",*p,*(p+3)); printf("p[0]is: %d,p[3]is: %d\n",p[0],p[3]); } 程序运行结果为: ais: FFC8,a+3is: FFCE (结果有可能不相同) pis: FFC8,p+3is: FFCE *ais: 1,*(a+3)is: 4 *pis: 1,*(p+3)is: 4 p[0]is: 1,p[3]is: 4 由运行结果可见: a+i等价于p+i,同时还有: *(a+i)等价于*(p+i),并且等价于a[i]和p[i]。 对p++、p--也是以基类型占用的存储单元进行,同时要修改p本身的值,让p指向后一个或前一个数据。 2. 指针之间的减运算 一般是同类型指针之间进行减运算。 对同类型的指针p、q,p-q表示p与q所指对象之间的元素个数。 例8.8 分析下面程序运行结果。 main( ) { double d[5]; double *p,*q; p=d, q=d+4; printf("p: %X,q: %X,q-p: %d\n",p,q,q-p); } 运行结果: p: FFB0,q: FFD0,q-p: 4 由结果可知: 两指针相减并不是两指针值之差,而是两指针所指对象之间的元素个数,所以实际值为指向type类型的两指针p和q之间的的差为: (q-p)/sizeof(type) 3.指针之间的关系运算 指针之间的关系运算主要用在同类型的指针之间,用来表示两指针所指对象之间的前后关系。 可以使用所有的关系运算符。 例8.9 将数组中的元素按位置颠倒。 分析: 可由两个指针p和q分别指向数组的两端,p和q分别向中间移动,同时交换它们所指向的值,停止交换的条件是: p≥q。 (8-3.1)中的 符号表示交换,p=a,q=a+N-1对应赋值复句,下面部分对应一while型循环语句,循环头为: while(p 程序如下: #define N 10 main( ) { int a[N]={1,2,3,4,5,6,7,8,9,10}; int *p,*q,t; p=a,q=a+N-1; while(p { t=*p;*p++=*q;*q--=t; } p=a,q=a+N; printf("theitemsof array are: \n"); while(p printf("%5d",*p++); } 运行结果为: the itemsofarrayare: 10 9 8 7 6 5 4 3 2 1 注意: ✍ (*p)++和*p++之间的关系: (*p)++表示对p所指向的对象加1修改,p仍指向原来的对象;*p++根据运算符的优先级和结合性,先计算表达式p++的值,其值为p原来所指向变量的地址,然后p指向下一个单元,最后再“*”运算符作用于表达式p++的值,取出p原来所指向的对象的值。 它们的区别为: (*p)++改变的是p所指向对象的值,整个表达式的值为p所指向对象值加1;而*p++改变的是指针p的值,整个表达式的值为p原来所指对象的值。 ✍ *(p++)、*++p和*p++: 都要修改p的值;实际上*(p++)与*p++是等价的,都是先取出p所指对象的值,再修改p的值;而*++p先修改p的值,再取出p当前所指向的对象的值。 为了增加可读性,建议使用*(p++)和*(++p)。 实际上(8-3.1)是等价于: 而 对应一个for型循环语句: for(i=0;i t=a[i],a[i]=a[N-i-1],a[N-i-1]=t; 加上说明和输入输出则可以编出另一程序,这里程序略。 四、 指针与一维数组 假设有: int a[N], *p=&a[0], *q=&a[N-1]; 用指针来引用一维数组的元素,主要有以下方法: ✍ 偏移量法: 用指针加上偏移量,形如*(p+i)或p[i],表示数组a的第i个元素a[i]; ✍ 修改指针变量p或q的值,让他们依次指向数组中的每一个元素,一般形式为: *p++,*q--。 ✍ 将数组名用作指针,利用偏移量法: *(a+i)或a[i]。 例8.10 求字符串的长度。 分析: 若p存放字符串的首地址,q存放字符串结束字符的地址,则字符串的长度为: len=q-p; 程序如下: main( ) { char c[10]="abcde"; char*p=c, *q=p; int len; while(*q! ='\0') q++; len=q-p; printf("thelenof string is: %d\n", len); } 运行结果为: thelenof string is: 5 例8.11 将数组中的元素先从中间向前输出前半部分的元素,然后再从中间向后输出后半部分的元素。 分析: 首先要确定数组中间位置的元素下标,假设数组的长度为N,则可定中间元素的下标为(注意数组的下标从0开始): ,再分两步完成输出操作。 程序如下: #define N 10 main( ) { int a[N]={1,2,3,4,5,6,7,8,9,10}; int*p,*q, m, i; m=(N-1)/2; p=a;/*利用指针偏移量方式*/ i=m; while(i>=0) { printf("%5d",*(p+i)); i--; } i=m+1; while(i { printf("%5d",*(p+i)); i++; } printf("\n"); } 程序运行结果为: 5 4 3 2 1 6 7 8 9 10 思考: 该程序是通过指针偏移量来实现的,请问如何通过指针下标方式、指针增量方式、数组名偏移量方式实现? 注意: 在用指针变量引用数组元素时,一定要注意是否越界(即超出数组的范围)。 五、 指针与二维数组 通过指针来引用二维数组中的元素要比处理一维数组复杂得多。 前面已经介绍二维数组在内存中的存放方式: 由于内存单元的组织是以线性结构组织的,因此二维数组也是以线形方式组织,并且以行序为主序进行处理。 虽然他与一维数组的存放形式一样
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 指针
![提示](https://static.bdocx.com/images/bang_tan.gif)