1、如果采取“间接存取”方式,要存取变量i的值,就必须先找到存放“i的地址”的变量p,从中取出i的地址(2000),然后到2000、2001字节取出i的值5,见图5-2: 图5-2 间接存取 由于通过地址能找到所需要的变量单元,我们可以说,地址指向该变量单元,因此,在C语言中,将地址形象化为指针。换句话说,一个变量的地址称之为该变量的“指针”,而指针变量是在一种专门存放其他变量在内存中的地址的特殊变量。指针变量的值是地址。5.1.2 指针变量的定义C语言规定所有变量必须遵循先定义后使用的规则。指针变量定义格式是:数据类型 *指针变量名;例如:int *p; /*p是指向int 型变量的指针变量*/
2、float *q; /*q是指向float 型变量的指针变量*/ char *a,b;/*其中a是指向char型变量的指针变量,而b是字符型一般变量*/ 那么,怎样使一个指针变量指向另外一个变量呢?可以用赋值语句使一个指针变量得到另外一个变量得地址,从而使它指向该变量。 int i; /*定义i为整型变量*/ int *p; /*定义p为指针变量*/ p=& /*将变量i的地址赋值给指针变量p*/ 注意 在定义指针变量时,要注意两点: 指针变量前面的“*”表示该变量的类型为指针变量。指针变量名是p,而不是*p,这是与定义整型或者字符型变量形式不同的。 在定义指针变量时必须指定基类型。并且指针变
3、量的类型是它指向的内存单元中存放数据的类型,而不是指针变量的值的类型。那么既然指针变量,是用来存放地址的,为什么还要指定它的类型呢?要知道不同数据在内存中所占的字节数是不同的(例如:整型占2个字节,字符型占1个字节),在本章的稍后将要介绍指针的移动和指针的运算,例如“使指针移动1个位置”,那么这个1代表什么呢?如果一个指针指向的是一个整型数据,那么“使指针移动1个位置”意味着移动2个字节,如果一个指针指向的是一个字符型数据,那么“使指针移动1个位置”意味着移动1个字节。因此必须指定指针变量的基类型,并且一个指针变量只能指向同一类型的变量。例如以下赋值是错误的:float a; /*定义a为fl
4、oat 型变量*/ /*定义p为基类型为int的指针变量*/a;/*错误!将float型变量的地址存放到指向整型变量的指针变量中,*/【例5-1】 通过指针变量访问整型变量main()int a,b; /*定义a,b为整型变量*/int *p1,*p2; /*定义p1,p2为指向整型变量的指针变量*/a=100; /*为变量a赋值为100*/b=10; /*为变量b赋值为10*/p1=& /*将a的地址赋给p1*/p2=&b; /*将b的地址赋给p2*/printf(%d,%dn,a,b); /* 输出a,b的值*/,*p1,*p2); /*输出p1,p2所指向的变量的值*/运行结果为:100
5、,105.1.3 指针变量的引用1.深入理解两个运算符:* 与 &设有定义语句:int *p,a; “*”是指针运算符。“*p”出现在定义语句和非定义语句中的含义是不一样的!在定义语句中,星号 * 声明其后的变量p为一指针(地址)变量;在非定义语句中“*p”表示指针变量p指向的地址单元内的值。 “&”是地址运算符,“&a”表示取某一普通变量a的地址。很显然,*(&a) 与 a 相当。 p=& 表示将a的地址赋给了指针变量 p,即p与&a指向了同一地址单元! 注意 “p=&”语句形式经常用到,使用时指针变量与一般变量的类型必须一致!例如本例中p,a均为整型变量,如果p,a的类型不一致那么就是错误
6、的!“p=a;”是非法的语句,号两边变量意义不同,左边为指针变量,右边为普通变量。 C规定,不能直接将一个常数赋给指针变量(除0以外,代表空指针)。【例5-2】输入a、b两个整数,使用指针变量按大小顺序输出这两个整数。main( ) int a,b,*p1,*p2,*p;scanf(%d,%d,p1,p2); /*等价于语句scanf(%d,%d, &a , &b);*/ if(*p1*p2) /*判断a,b整数的大小*/ p=p1;p1=p2;p2=p;printf(a=%d,b=%dn,a,b); printf(max=%d,min=%dn,*p1,*p2); 运行情况如下:输入: 6,8
7、输出:max=8,min=6 当输入6,8后,由于*p1 p1、p2 = p1。 指针与一个整型数据进行比较是没有意义的 。 不同类型指针变量之间比较是非法的。 零可以与任何类型指针进行=、!=的关系运算,用于判断指针是否为空指针 。例如:p1=0;作用是判断指针变量p1是否为空.5.2指针与数组从前面知道,一个数组包含有若干元素,数组各元素都在内存中占用存储单元,它们都有相应的地址,而这些存储单元是连续的区域,数组名代表这块空间的起始地址。而指针变量既然可以指向地址,当然也可以指向数组元素的地址。通常人们习惯将数组的首地址存放到一个指针变量中,然后通过指针加减运算,存取数组各元素。如定义语句
8、int x=1,2,3,4,5,*p=x;此语句中的“*p=x”,将x数组的首地址赋给了指针变量p,显然可以在定义语句中将它替换成“*p=&x0”形式。用指针来指向数组元素,比用纯数组的方式操作数组要方便得多,因为数组名不能运算,而指针是可以运算的。使用指针能使目标程序占内存少,运行速度快。5.2.1 指向一维数组的指针定义一个指向数组元素的指针的方法,与以前介绍的指向变量的指针变量定义方法相同。int a10; /*定义a为包含10个整型数据的数组*/ /*定义p为指向整型变量的指针变量*/a0; /*把元素a0的地址赋给指针变量p*/在C语言中规定数组名(不包含形参数组名,形参数组并不占有
9、实际的内存单元)代表数组中首地址(即数组中第一个元素的地址)。因此,下面两个语句等价:p=a; 注意 指针变量指向数组并不是指向整个数组,而是指向了数组中第一个元素。上述“p=a;”的作用是:“把数组的首地址赋值给p”,而不是“把数组a各元素的值赋值给p”。 那么要通过指针引用数组元素,应该如何实现呢? 按C语言规定:如果指针变量p已指向数组中的一个元素,那么p+1指向同一数组中的下一个元素,而不是简单的将p的值(地址)简单的加1。例如,数组元素是float型,每个元素占4个字节,那么p+1意味着使p的值(地址)加4个字节,以使它指向下一个元素;又例如数组元素是int型,每个元素占2个字节,那
10、么p+1意味着使p的值(地址)加2个字节,以使它指向下一个元素。也就是说,p+1所代表的地址实际上使p+1d,d代表一个数组元素所占的字节数。根据上述,引用一个数组元素可以用两种方法:(1) 下标法,如a0形式;(2) 指针法,如*(a+i)或者*(p+i)。其中a是数组名,p是指向数组元素的指针变量,初值为数组a的首地址。例如有语句如下:int a10,*p; 数组名是该数组的指针 a是数组的首地址(即a0的地址),是一个指针常量。a = &a0,a+1 = &a1, ,a+9 = &a9 数组元素的下标表示法:a0,a1, ,ai, ,a9 数组元素的指针表示法: *(a+0),*(a+1
11、), ,*(a+i), ,*(a+9) 指向一维数组元素的指针变量由于数组元素也是一个内存变量,所以此类指针变量的定义和使用与指向变量的指针变量相同。 例如有语句如下:p = a; /*相当于p = &*/此时p指向a0,下面用p表示数组元素 下标表示法:p0,p1, ,pi, ,p9 指针表示法: *(p+0),*(p+1), ,*(p+i), ,*(p+9) 提醒用指针变量引用数组元素,必须关注其当前值。如果指针变量p的初始值不一样,那么用p表示数组元素时,有一定的差异.p = p + 3;此时,指针变量p指向第四个数组元素a3,那么p0 、 *(p+0)等价于a3,而*(p-1)、p-1
12、等价于a2; *(p+1) 、p1等价于a4,依次类推.【例5-3】输出一维数组中的所有元素。int a=1,2,3,4,5,*p, i; /*将数组a的首地址赋值给指针变量p*/ for(i=0;i5;i+) n %d,%d,%d,%d,ai, *(a+i),pi, *(p+i) ); 程序分析与解释:程序中的printf()函数展示了对一维数组元素的四种等价表示形式。假如数组a的首地址是2000,那么p指向内存单元2000,则该数组在内存中存放形式及数组元素的表示形式如图5-4所示:【例5-5】 输入五个整数,使用指针变量将这五个数按从小到大排序后输出。 main( ) int a5,*p
13、p,*p,*q,t;for (p=a; pa+5;p+) /*输入5个整数,并且分别存放到数组a中*/ scanf(%d,p); for (p=a;a+4; p+) /*使指针变量p指向数组a*/pp=p; for (q=p+1; q*q) pp=q; if (pp!=p) /*如果本轮比较出的较小值不等于*p,那么交换值*/t=*p;*p=*pp;*pp=t; p+) printf(%d ,*p); /*输出数组的值,并且每输出一个空一格*/运行情况如下:输入:54 65 12 34 2输出:2 12 34 54 65该问题的解决方法,采取的是在上一章所学的选择排序法来进行排序的.5.2.2
14、 指向二维数组的指针1. 二维数组的地址 例如有定义语句:int a33; 二维数组名a是数组的首地址。 二维数组a包含三个行元素:a0、a1、a2 。 三个行元素的地址分别是:a、a+1、a+2。而a0、a1、a2也是地址量,是一维数组名,即*(a+0)、*(a+1)、*(a+2)是一维数组首个元素地址。如图5-5:2. 二维数组元素的地址 a0、a1、a2是一维数组名,所以ai+j是数组元素的地址。数组元素aij的地址可以表示为下列形式, &aij、ai+j 、*(a+i)+j如图5-6所示:3.二维数组元素的表示法数组元素可用下列形式表示:aij、*(ai+j)、*(*(a+i)+j)a
15、 是二维数组,根据C的地址计算方法,a经过两次 * 操作才能访问到数组元素。所以: * a 是 a0,* * a 才是 a00。 看一看,想一想 若a是二维数组名请理解下列表示方法的含义aa0,*(a+0),*a,&a00a+1,&a1a1+2,*(a+1)+2,&a12 a0是a00的地址,*a0是a00。4. 指向二维数组元素的指针变量【例5-6】用指向数组元素的指针变量输出数组元素,请注意数组元素表示方法。 main( ) int a34=0,1,2,3,10,11,12,13,20,21,22,23, i, j, *p; for (p=a0, i=0; i 3; i+) for (j=
16、0; jstrlen(str) | n=0 | m=0) printf(NULLn elsefor (p=str+m-1,i=0;n; i+) if(*p) ci=*p+; else break; /* 如读取到 0 则停止循环 */ ci= /* 在c数组中加上子串结束标志 */,c);5.3结构体在前面,已经介绍过了基本数据类型的变量(如整型、字符型、浮点型变量),也介绍了一种构造类型数据数组,其中数组中各元素属于同一种类型。但是只有这些数据类型是不够的,有时需要将不同数据类型组合成一个有机的整体,以便引用。这些组合在一个整体中的数据是互相联系的,例如:一个学生的信息有学号、姓名、性别、年
17、龄、住址、成绩等; 一本图书的信息有分类编号、书名、作者、出版社、出版日期、价格、库存量等。那么如何描述这些类型不同的相关数据呢?在C语言中允许用户自己指定这样一种数据结构,它是由若干个类型不同的(当然也可以相同)的数据项组合在一起,称为结构体(structure)。构成结构体的各个数据项称为结构体成员。 它相当于其他高级语言中的“记录”。如果要用C语言编写实用程序,结构的知识是不可缺少的!同时,结构还是C+等后续语言的基础。5.3.1 结构体的定义与引用1结构体定义的一般格式struct 结构体名数据类型1 成员名1; 数据类型2 成员名2; 数据类型n 成员名n; ; 不要忽略了最后的分号。 struct为关键字; 结构体名是用户定义的类型标识,用作结构体类型的标志。 结
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1