第9章 指针.docx
- 文档编号:26379061
- 上传时间:2023-06-18
- 格式:DOCX
- 页数:25
- 大小:68.98KB
第9章 指针.docx
《第9章 指针.docx》由会员分享,可在线阅读,更多相关《第9章 指针.docx(25页珍藏版)》请在冰豆网上搜索。
第9章指针
第九章指针
9.1指针和指针变量
一、指针和指针变量
1.再论变量及其属性
变量是一段可命名的连续的内存空间(由若干个地址相邻的内存单元组成),该段内存空间用来存储某种类型的数据。
变量有5个重要属性:
(1)变量名
为了能在程序中方便地访问变量的内存空间,需要对变量命名。
(2)变量的数据类型
变量中存储的数据的类型。
根据变量的类型,可将变量分为:
整型变量(存储整数)、实型变量(存储浮点数)、字符变量(存储字符的ASCII码)等。
(3)变量值
变量内存空间中存储的数据,可通过变量名来获得。
(4)变量的内存大小
变量所占的内存字节数,可以通过sizeof(变量名)来得到某个变量的内存大小。
(5)变量的地址
变量所占连续内存空间的第一个字节的地址,可以通过地址运算符获得某个变量的地址&变量名。
假设在程序中定义了一个整型变量i:
inti=3;在VC6.0环境中,系统会为变量i分配4个连续字节。
变量i以及存储情况如图所示:
变量i中存储整数3的32位补码:
00000000000000000000000000000011。
变量i的地址是其所占连续空间的第1个单元的地址,即0x12FF7C(其实就是该内存单元的十六进制编号)。
我们把变量的地址称为变量的指针,所以变量i的指针就是0x12FF7C。
2.指针变量
C语言中有一类非常特殊的变量,这类变量的内存空间用来存储别的变量的地址(即指针),称为指针变量。
指针变量也具有普通变量的5个属性:
(1)变量名
用户定义指针变量时候要为其命名。
(2)数据类型
不同数据类型的变量地址,要用相应类型的指针变量来存放。
例如定义一个int指针变量p:
int*p;(为了区别普通变量的定义,每个指针变量在定义时需要在数据类型和变量名之间加上“*”)。
p中只能存储int变量的地址:
int*p,a=3;
p=&a;
值得注意的是,变量名a前面没有“*”,a就是一个普通的int变量。
所以在定义多个同类型的指针变量时,每个变量名前面都要加上“*”,例如:
int*p,*q;
int变量a和int指针变量p的存储情况如图所示:
【课内思考题9.1】为什么指针变量p中存放“00000000000100101111111101111100”?
(3)指针变量的值
一个指针变量的值就是指针变量中所存储的另一个变量的地址。
(4)指针变量的内存大小
指针变量中存放的是另一个变量的地址,而变量的地址其实就是一个整数(变量所占连续空间的第一个单元的编号),所以指针变量的内存大小和int变量的内存大小是一样的。
在TC2.0等环境下所有的指针变量都占2个字节,而在VC6.0等环境下所有的指针变量都占4个字节。
(5)指针变量的地址
对任何一个变量使用地址运算符“&”就可以获得该变量的地址,指针变量也不例外。
例如,通过“&p”可获得指针变量p的地址。
二、指针变量的基本运算
对普通的指针变量可以进行的常见运算包括=(赋值运算)和*(指针运算)。
指向数组的指针变量往往还可以进行关系运算、++(自增运算)、--(自减运算)。
1.指针变量的赋值运算
#include
main()
{
inta,b;
int*p=&a;
int*q;
p=&b;
q=p;
printf("&a=%#x,&b=%#x\n",&a,&b);
printf("p=%#x,q=%#x\n",p,q);
}
说明:
(1)指针变量中只能存放另一个同类型的变量地址。
当指针变量p存放变量a的地址时,即“int*p=&a;”就称指针变量p指向了变量a。
(2)可以改变一个指针变量的值,让它指向其他变量,如本例程序中的“p=&b;”。
(3)可以在两个同类型的指针变量之赋值,如本例程序中的“q=p;”,即让指针变量q也指向p所指向的变量。
2.指针变量的指针运算
指针运算符“*”是一个单目运算符,其运算对象是某个变量的地址,其运算含义是间
接访问该变量,例如给变量赋值或者把变量的值打印出来,而通过变量名是直接访问变量,
所以在有些书上也把指针运算符称为间接访问运算符(简称间访运算符)。
指针运算的优先级
排第二,左结合。
【例9.1】指针运算示例一
#include
main()
{
intx=-112;
printf("x=%d\n",x);
printf("x=%d\n",*&x);
}
【例9.2】指针运算示例二
#include
main()
{
intx=-112;
int*p;
p=&x;
printf("x=%d\n",x);
printf("x=%d\n",*&x);
printf("x=%d\n",*p);
}
说明:
指针变量p中存放变量x的地址(见“p=&x;”),所以p指向了a,通过“*p”可间
接访问变量a,获得变量a的值。
【例9.3】下面这段程序深刻地揭示了指针变量的基本概念和指针运算的基本含义。
#include
main()
{
inta=2010;
int*p=&a;
printf("&a=%#x\n",&a);
printf("p=%#x\n",p);
printf("&p=%#x\n",&p);
printf("a=%d\n",a);
printf("*p=%d\n",*p);
printf("*&a=%d\n",*&a);
printf("*&p=%#x\n",*&p);
}
3.用指针变量作为函数的形参变量
【例9.4】使用指针变量作为函数形参,间接操纵实参变量。
#include
voidchange(int*p)
{
*p=-1;
}
main()
{
inta=2;
printf("a=%d\n",a);
swap(&a);
printf("a=%d\n",a);
}
说明:
本例程序中的main函数在调用函数change时,将变量a的地址传递给了指针变量p(相当于p=&a),这样一来指针变量p就指向了实参变量a,通过*p就可以间接访问变量a,例如可以改变a的值“*p=-1;”。
仿照例9.4,不难写出用来交换两个实参变量值的函数swap:
#include
voidswap(int*p,int*q)
{
inttemp=*p;
*p=*q;
*q=temp;
}
main()
{
inta,b;
scanf("%d,%d",&a,&b);
swap(&a,&b);
printf("%d,%d\n",a,b);
}
9.2一维数组和指针变量
一、数组的指针
数组的指针就是数组在内存中的起始地址,也就是数组中第0个元素的内存地址,用
数组名表示。
例如,定义int数组a:
inta[10];数组a的指针用数组名a表示或者用&a[0]
表示。
【例9.5】一维数组名的语法含义。
#include
main()
{
inta[10]={1,2,3,4,5,6,7,8,9,10};
inti;
for(i=0;i<10;i++)
printf("&a[%d]=%#x\n",i,&a[i]);
printf("a=%#x\n",a);
printf("a+1=%#x\n",a+1);
printf("a+5=%#x\n",a+5);
}
说明:
数组名表示数组中第0个元素的地址:
a==&a[0],以此类推a+i表示数组中第i个元
素的地址,即a+i==&a[i]。
既然知道了a+i表示数组a中第i个元素的地址,就可以用指针运算符间接访问数组的
第i个元素:
*(a+i),注意到指针运算符(*)的优先级高于加法运算符(+),所以要使用括号先得到&a[i],再使用指针运算符根据地址访问元素a[i]。
【例9.6】使用指针运算间接访问一维数组中的元素。
main()
{
inta[10],i;
for(i=0;i<10;i++)
scanf("%d",a+i);/*等价于scanf("%d",&a[i]);*/
for(i=0;i<10;i++)
printf("%d",*(a+i));/*printf("%d",a[i]);*/
}
说明:
既然a+i==&a[i],那么*(a+i)==a[i]了。
二、用指针变量访问数组
可以用指针变量来保存一个数组的起始地址,当然指针变量的数据类型要和数组的类型
一致。
main()
{
inta[10];
inti;
int*p=a;/*相当于int*p=&a[0]*/
for(i=0;i<10;i++)
scanf("%d",p+i);
for(i=0;i<10;i++)
printf("%d",*(p+i));
}
说明:
(1)当指针变量p保存了数组a的起始地址,也就意味着p指向了a中第一个元素a[0]。
(2)下面这些表达式总是等价的,都表示数组a的第i个元素的地址:
a+i==&a[0]+i==&a[i]==p+i,其中p=a。
(3)用指针运算符去访问数组元素和用下标运算符去访问数组元素是等价的:
既然*(a+i)=a[i],那么也有*(p+i)==p[i]。
其实在编译的时候,编译器一律把数组下标运算转换成指针运算。
所以程序最后一条语句“printf("%d",*(p+i));”等价于“printf("%d",*(a+i));”
从表面上看用指针变量去访问数组元素,与例9.6的代码相比似乎并无多大改进,但是
我们要意识到指针变量也是变量,其值是可以改变,暗示用户可以使用自增运算符,改变指
针变量的值,让它指向数组的不同元素。
【例9.7】对指向数组元素的指针变量使用自增运算符。
#include
main()
{
inta[10];
inti;
int*p;
for(p=a;p<=a+9;p++)
scanf("%d",p);
for(p=a;p<=a+9;p++)
printf("%d",*p);
}
说明:
(1)用指针变量p循环访问数组a中每一个元素时,在循环前先让p保存a中第一个元素
的地址,然后使用自增运算符不断改变变量p的值,使其不断地指向下一个元素。
(2)可将第二个for语句改用下面while语句实现:
p=a;
while(p<=a+9)
printf("%d",*p++);
注意在理解“*p++”时,*和++优先级同列第二,而结合性都是右结合性。
(3)由于数组定义好以后,数组中每个元素的地址就固定下来了,所以数组名是个常量地址,
不能对数组名使用自增运算符,即“a++”。
【课内思考题9.2】分析下面程序的错误原因:
#include
main()
{
int*p,i,a[10];
p=a;
for(i=0;i<10;i++)
scanf("%d",p++);
printf("\n");
for(i=0;i<10;i++)
printf("%d",*p++);
}
三、再论形参数组
前面在讲函数时曾提到,当被调函数的形参是数组时,形参数组元素值的怎么改变,实参数组的对应元素就怎么变。
从表面上看这似乎有悖于C语言中参数传递的方式——单向值传递。
通过sizeof运算符不难发现所谓的形参数组其实不是一个真正的数组,在编译时会被系统处理成指针变量。
【例9.8】形参数组本质上是指向数组元素的指针变量。
#include
voidadd_arrayelem(intb[],intn)
{
inti;
for(i=0;i } main() { inta[10],i; for(i=0;i<10;i++)a[i]=i+1; add_arrayelem(a,10); for(i=0;i<10;i++)printf("%d",a[i]); printf("\n"); } 说明: 形参数组“intb[]”在编译时被处理成int*b,在进行参数传递的时候,指针变量b就保存了实参数组a的起始地址,指向了a[0]。 由于下标运算和指针运算等价,“b[i]++”就被转换成(*(b+i))++。 形参数组之所以会改变时实参数组,其实还是指针变量在从中“捣怪”,所以在学习语法时,勿要被现象掩盖了本质。 四、函数返回多值 可以通过指针变量让函数返回多值,如下例所示: 【例9.9】定义一个void函数,且不用全局变量,求出一个数组中的最大元素值和数组的平均元素值。 #include doublefunction(int*a,intn,int*pmax,int*pmin) { inti; doublesum=a[0]; *pmax=*pmin=a[0]; for(i=1;i<10;i++) { if(a[i]>*pmax)*pmax=a[i]; elseif(a[i]<*pmin)*pmin=a[i]; sum=sum+a[i]; } returnsum/n; } main() { inta[10]; inti; doubleave; intmax,min; for(i=0;i<10;i++) scanf("%d",a+i); ave=function(a,10,&max,&min); printf("max=%d,min=%d,ave=%.2f\n",max,min,ave); } 9.3二维数组和指针 一、二维数组中的行地址和元素地址 【例9.10】行地址和元素地址。 #include main() { inta[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; inti,j; for(i=0;i<4;i++) { for(j=0;j<4;j++) printf("&a[%d][%d]=%#x",i,j,&a[i][j]); printf("\n"); } printf("a=%#x\n",a); printf("a[0]=%#x\n",a[0]); printf("a+1=%#x\n",a+1); printf("a[0]+1=%#x\n",a[0]+1); printf("a[0]+3=%#x\n",a[0]+3); } 说明: (1)从运行结果中不难发现,二维数组中存在两类不同的地址: 行地址和元素地址。 (2)二维数组名a是一个行地址,表示数组第0行的地址,所以a+i就是二维数组a第i行的行地址。 (3)而a[0]是一个元素地址,表示二维数组a中第0行第0个元素的地址,那么,a[0]+j表示a中第0行第j个元素的地址。 一般地,a[i]表示二维数组a中第i行第0个元素的地址,a[i]+j表示二维数组a中第i行第j个元素的地址,即a[i]+j==&a[i][j]。 (4)两类地址的互换。 第i行的行地址前使用指针运算符*,可转换成第i行第0个元素的地址。 例如*(a+i)==a[i],即*(a+i)+j==a[i]+j==&a[i][j]。 第i行第0个元素的地址前使用地址运算符&,可转换成第i行的行地址。 例如,&a[i]==&*(a+i)==a+i。 (4)对元素地址使用指针运算*就能间接访问该元素。 所以*(a[i]+j)==*(*(a+i)+j)==a[i][j]。 对于上述语法的验证代码如下: #include main() { inta[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; inti,j; for(i=0;i<4;i++) { for(j=0;j<4;j++) printf("a[%d][%d]=%-4d",i,j,a[i][j]); printf("\n"); } printf("\n"); for(i=0;i<4;i++) {for(j=0;j<4;j++)printf("&a[%d][%d]=%#x",i,j,&a[i][j]); printf("\n"); } printf("\n"); printf("*(a+1)+2=%#x\n",*(a+1)+2); printf("&a[1]=%#x\n",&a[1]); printf("&a[1]+2=%#x\n",&a[1]+2); printf("*(*(a+1)+2)=%d\n",*(*(a+1)+2)); printf("*(a[1]+2)=%d\n",*(a[1]+2)); } 二、指向二维数组元素的指针变量 定义一个与二维数组类型相同的指针变量,然后用该指针变量保存二维数组中第0行第0个元素的地址,再对该指针变量使用自增运算符,让它依次指向二维数组中的其他元素。 【例9.11】用指针变量访问二维数组中的元素。 #include main() { inta[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; int*p=*a;/*int*p=&a[0][0]*/ inti,j; for(i=0;i<4;i++) { for(j=0;j<4;j++) { printf("%-4d",*p); p++; } printf("\n"); } } 三、指向一维数组的指针变量 定义一个指向一维数组的指针变量,用来保存二维数组的行地址,其语法格式为: 数据类型(*指针变量名)[二维数组第2维长度],例如int(*p)[5]就是一个指向一维数组的指针变量。 【例9.12】指向一维数组的指针变量。 #include main() { inta[4][5]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; int*q; int(*p)[5]=a+2; inti,j; for(i=0;i<4;i++) { for(j=0;j<4;j++) printf("%-4d",*(*p+j)); p++; printf("\n"); } } 9.4指针数组和字符指针变量 一、指针数组 指针数组中的每个数组元素相当于一个指针变量,用于存储指针(地址),其语法格式为: 数组类型*p[数组长度],例如,int*p[4]。 【例9.13】指针数组编程示例。 #include main() { inta1[4]={1,2,3,4}; int*p[4]; inti; for(i=0;i<4;i++) p[i]=&a1[i];/*等价于p[i]=a1+i;*/ for(i=0;i<4;i++) printf("%d",*p[i]); printf("\n"); } 说明: (1)本例指针数组p中的4个元素分别用来存储一维数组a1中4个元素的地址,即p[0]的值是&a1[0](即p[0]指向了a1[0]),…,p[3]的值是&a1[3](即p[3]指向了a1[3])。 (2)定义指针数组时要注意区分指点向一维数组的指针变量,即int*p[4]≠int(*p)[4]。 (3)程序第5行中的*p[i]也可以写成*(*(p+i))。 二、字符指针 1.常量字符串的存储 可用一维字符数组存储单个字符串,其中字符数组名就表示所存储的串的起始地址。 #include main() { charstr[]="Computer"; printf("%#x\n",str); printf("%s\n",str);/*等价于printf("*%s\n",&str[0])*/ printf("*str=%c\n",*str); printf("*(str+5)=%c\n",*(str+5));/*输出字符't'*/ } 说明: (1)把串"Computer"赋给字符数组str,根据串中字符的个数可以确定str数组的长度为9(不要忘了字符串的结束符'\0')。 具体的存储情况如下: str[0]存储'C',str[1]存储'o',…,str[8]存储'\0'。 (2)数组名str就表示所存储的字符串"Computer"的起始地址,或者说串中第1个字符'C'的地址。 (3)用%s输出串的起始地址,实际打印的字符串本身。 2.字符指针变量 在C语言中,字符指针变量有两个作用: 一是用来保存某个字符变量的地址;二是用来 存储某个字符串的起始地址。 (1)用字符指针变量指向某个字符变量 #include main() { charch='a'; char*pc=&ch; printf("%c\n",*pc); } (2)用字符指针变量指向字符串 #include main() { charstr[9]="Hangzhou"; char*pstr=str; printf("%#x\n",str);printf("%#x\n",pstr); printf("%s\n",str);printf("%s\n",pstr); printf("*pstr=%c\n",*pstr); printf("*(pstr+4)=%c\n",*(pstr+4)); pstr=pstr+6; printf("*pstr=%c\n",*pstr); } 3.字符指针数组 #include main() { char*pstr[3]={"abc","defg","hijkl"}; printf("pstr[1]=%s",pstr[1]); printf("*(pstr[1]+3)=%c\n",*(pstr[1]+3)); } 10.5返回值为指针的函数和函数指针变量 一、返回值为指针的函数 函数可以返回整数、字符、实数等类型的数据,当然也可以返回指针数据(即地址),返回值为指针的函数的原型如下: 类型*函数名(形参列表); 例如: int*add(inta,intb); 说明: 函数add返回整型指针。 【例9.14】返回值为指针的函数。 #include int*add(int*pa,int*pb) {
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第9章 指针