第八章 指针
8.1 指针变量的定义与引用
【学习目标】
(1)掌握指针的概念
(2)掌握指针变量的定义方法
(3)掌握指针变量的赋值方法及相关运算符的使用
(4)会区分指针变量的值和指针的地址值
(5)掌握指针变量的基本操作
实例34 指针变量的定义与引用——寻找变量在内存中的家
【实例任务】
已经定义了字符型、整型、实型三个变量,试定义三个指针变量,然后将几个变量在内存中的地址赋值给指针变量,输出本个变量的在内存中的地址值和变量的值。程序运行结果如图8-1所示。
图8-1 程序运行结果
【程序代码】
#include "stdio.h" main() { char c='a'; int n=10; float f=1.5; char *cp; /*定义一个指向字符型变量c的指针变量*/ int *np;/*定义一个指向整型变量a的指针变量*/ float *fp ;/*定义一个指向单精度类型变量n的指针变量*/ char **cpp; /*定义一个指向字符型指针变量cp的指针变量*/ int **npp; /*定义一个指向整型指针变量np的指针变量*/ float **fpp; /*定义一个指向实型指针变量fp的指针变量*/ /*让指针变量指向各个变量并输出值 */ cp=&c;/*取变量c的地址值给指针变量cp,也就是cp指向c*/ np=&n;/*np指向变量n*/ fp=&f;/*fp指向变量f*/ printf("变量的初值和指针变量的初值为:\n"); printf("变量c的值为:%c,它在内存中的地址为:0x%x\n",c,cp); /*c为字符型变量,内存中的地址输出值为十六进制整型*/ printf("变量n的值为:%d,它在内存中的地址为:%#x\n",n,np); /*c为字符型变量,内存中的地址输出值为十六进制整型*/ printf("变量f的值为:%f,它在内存中的地址为:0x%x\n",f,fp); /*c为字符型变量,内存中的地址输出值为十六进制整型*/ /*改变变量的值,看看指针变量的值是否改变*/ c='b'; n=20; f=2.5; printf("\n\n变量的初值和指针变量的初值为:\n"); printf("变量c的地址值为:%#x,它所存储的内容为:%c\n",cp,*cp); printf("变量n的地址值为:%#x,它所存储的内容为:%d\n",np,*np); printf("变量f的地址值为:%#x,它所存储的内容为:%f\n",fp,*fp); /*"*"是运算符,表示引用指针变量所内存单元中的内容*/ /*通过指针变量改变所指地址的值*/ *cp='c'; *np=30; *fp=3.5; printf("\n\n通过指针变量改变所指地址的内容\n"); printf("变量c的值为:%c\n",c); printf("变量n的值为:%d\n",n); printf("变量f的值为:%f\n",f); /*通过指向指针的指针变量引用变量的值*/ printf("\n\n通过指向指针的指针变量引用变量的内容\n"); cpp=&cp; npp=&np; fpp=&fp; printf("变量c的值为:%c\n",**cpp); printf("变量n的值为:%d\n",**npp); printf("变量f的值为:%f\n",**fpp); } |
【相关知识】
1.变量在内存中的地址
在C语言的程序设计中,指针是一种非常重要的数据类型,借助它可以表达非常复杂的数据结构,因而得到了广泛的应用,也是C语言中一个重要的知识点。
要了解指针,先要了解内存空间是如何进行存储和访问的。计算机内存管理时,是以字节为单位来编号的存储单元,这个编号就是这个单元在内存中的地址,类似于楼房中按序号编排的各家各户的门牌号码。
当一个变量被定义后,就在内存中为之分配一个大小合适的存储空间,这个空间的大小由变量的类型而定。一个char类型变量就占1个字节的存储空间,一个int类型的变量就占2个字节的存储空间,一个float类型的变量就占4个字节的存储空间,一个float类型的数组a[10]就占10×4个字节的空间。对于占多个字节的变量,存储空间的起始地址被当作是这个变量的地址。
实例中,定义语句“char c='a'; int n=10; float f=1.5;”定义的三个变量,假如地址分配如图8-2所示。
图8-2 地址分配
在这里,我们称变量c的地址为1000,变量n的地址为2000,变量f的地址为3000,这样就可以通过变量的地址实现对变量的访问。以前各章节,使用变量时都是通过变量名对变量内容进行存取,无需知道变量在内存中的存储地址,这种对存储单元的访问过程由系统自动完成对地址的查找,这种方式也称为“直接访问”。
学习本章后,可以在定义变量后,先将变量在内存的地址赋给指针变量,然后通过指针变量来访问变量的内存单元,这种访问方式也称为“间接访问”。实例中,定义的指针变量np,取整型变量n的地址赋值给np,则np指向了变量n,它们之间的关系如图8-3所示。
指针变量np 整型变量n
4000 2000 2001
图8-3 指针变量存放变量的起始地址
这样,我们再使用变量n的时候,就可通过指针变量p来引用。指针的知识在后续章节中被广泛使用,尤其链表的建立部分,使用指针是唯一选择。
2.指针的定义
指针变量用于存放变量的地址,所以定义类型要与所要存储变量的地址相一致,这对指针变量而言,称为指针变量的基类型。定义指针变量的基本形式是:
基类型名 *指针变量1,*指针变量2,…
如实例中的如下定义语句:
char *cp;
int *np;
float *fp;
这里,变量名cp、np、fp前的*标识出这三个变量。cp只能存放字符型变量的地址,np只能存放整型变量的地址,fp只能存放单精度型变量的地址。
另外,还有如下的定义形式:
int (*a)[5]; /*定义一个整型数组的指针变量,这个数组有5个数组元素*/
int (*f)( ); /*定义指向函数的指针变量,该函数返回一个整型值*/
int *(*f)( ); /*定义一个指向函数的指针变量,此函数返回一个地址值*/
还有下面两种情况,应区别看待,依据字符的优先级别,其定义的结果不属指针变量。
int *a[5] /*a是数组名,它有5个数组元素,每个数组元素都是指向整型的指针变量*/
int *f( ); /*f是一个函数名,它返回指向一个整型值的地址*/
3.指针变量的引用
C语言中,对指针变量的引用,可以通过取地址运算符“&”和间接访问运算符“*”来完成。有前面章节中,输入变量的值是使用scanf语句,就是使用&这个运算符将输入结果保存到系统指定的存储单元中。
如有如下程序段,我们可以灵活使用这两个运算符完成对变量的引用:
int n=10,*p,**np;
p=&n;
np=&p;
则输出变量n的值,可以用如下几种方法:
printf("直接输出n的值为:%d\n",n);
printf("使用n的地址值输出n的值为:%d\n",*(&n));
printf("引用指针变量p输出n的值为:%d\n",*p);
printf("引用指针变量np输出n的值为:%d\n",**np);
【课堂精练】
1. 通过指针变量,按由大到小输出两个整数。程序的运行结果如图8-4所示。
图8-4 程序运行结果
根据程序的运行结果,将程序补充完整并调试。
#include "stdio.h"
main()
{ int a,b,*p,*p1,*p2;
scanf("%d,%d",&a,&b);
________________;
________________;
if(a
{p=p1;___________;___________;}
printf("\n输入的a=%d,b=%d\n",a,b);
printf("排序后两个数为%d,%d",*p1,*p2);
getch(); }
2.通过指向指针的指针变量,改变整型变量和字符型变量中的值。
程序的运行结果如图8-5所示。
图8-5 程序的运行结果
根据程序的运行结果,请将如下程序补充完整并调试。
#include "stdio.h"
main()
{char c='a',*cp,**cpp;
int n=10,*np,**npp;
cp=&c;
__________________________;
__________________________;
npp=&np;
c=(*cp)+1;
c=(**cpp)+1;
*np=(*np)+10;
n=(**npp)+10;
printf("通过引用指针变量运算后,c的值为:%c\n",c);
printf("通过引用指针变量运算后,n的值为:%d\n",n);
getch(); }
8.2 一维数组与指针
【学习目标】
(1)掌握数组名中存放的值的内涵
(2)会区分数组元素的值和数组元素的地址值
(3)能通过数组名的运算实现指针的移动来引用数组元素
实例35 数组名的值和数组元素的地址——按序输出内存中各家的地址
【实例任务】
定义一个有6个数组元素的一维整型数组,用于存放6户人家的房号。利用数组名的值和数组元素的地址值,分别输出1-6号家庭的编号和在内存中的地址。程序的运行结果如图8-6所示。
图8-6 程序的运行结果
【程序代码】
#include "stdio.h" main() {int a[6]={1,2,3,4,5,6};/*为各个家庭编号*/ int i=0; printf("\n通过引用各个数组元素输出各个房间号的值\n"); while(i<< span="">=5) {printf("%d ",a[i]); i++;} printf("\n"); printf("通过数组的首地址来输出各个房间号的值\n"); for(i=0;i<=5;i++)< span=""> {printf("%d ",*(a+i));} printf("\n"); printf("输出整型数组各元素在内存中的十六进制地址\n"); for(i=0;i<=5;i++)< span=""> printf("0x%x ",&a[i]); getch(); } |
【相关知识】
1.一维数组名的值
一维数组被定义后,数组名就有了一个恒定的值,即这个数组在内存所占一串连续存储单元的首地址,也就是第一个数组元素的地址。
实例中,定义的数组a,它的值是数组元素a[0]在内存中的地址值,这个值不可改变,因此“a++;”或“a=&p;”等这样的语句都是不成立的。
2.数组元素的地址
虽然我们不能改变数组名的值,但是可以通过对数组名的运算,来得到不同数组元素的地址。如数组元素a[3]的地址值为表达式a+3的值。这种指向关系如图8-7所示。
图8-7 数组名的运算
可见,我们要想得到数组元素的地址,除直接引用取地址运算符名,还可以通过数组名得到。