《c语言数组和指针的学习.doc》由会员分享,可在线阅读,更多相关《c语言数组和指针的学习.doc(18页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、C语言数组与指针详解2008-08-11 02:01在语言中,指针和数组有着紧密的联系,其原因在于凡是由数组下标完成的操作皆可用指针来实现。在数组中我们已经知道,可以通过数组的下标唯一确定了某个数组元素在数组中的顺序和存储地址,这种访问方式也称为下标方式。例如: int a5 = 1, 2, 3, 4, 5, x, y; x=a2; /* 通过下标将数组a下标为2的第3个元素的值赋给x,x=3 */ y=a4; /* 通过下标将数组a下标为4的第5个元素的值赋给y,y=5 */ 由于每个数组元素相当于一个变量,因此指针变量既然可以指向一般的变量,同样也可以指向数组中的元素,也就是可以用指针方式
2、访问数组中的元素。 例10-6:分析程序的运行过程和结果。 #include main ( ) int a = 1, 2, 3, 4, 5 ; int x, y, *p; /* 指针变量p */ p = &a0; /* 指针p指向数组a的元素a0,等价于p=a */ x = *(p+2); /* 取指针p+2所指的内容,等价于x=a2 */ y = *(p+4); /* 取指针p+4所指的内容,等价于y=a4 */ printf (*p=%d, x=%d, y=%dn, *p, x, y); 语句p=&a0表示将数组a中元素a0的地址赋给指针变量p,则p就是指向数组首元素a0的指针变量,&a0
3、是取数组首元素的地址。 语言中规定,数组第1个(下标为0)元素的地址就是数组的首地址,同时C中还规定,数组名代表的就是数组的首地址,所以,该语句等价于p=a;。注意, 数组名代表的一个地址常量,是数组的首地址,它不同于指针变量。 对于指向数组首地址的指针p,p+i(或a+i)是数组元素ai的地址,*(p+i)(或*(a+i)就是ai的值,其关系如图10-5所示。 图10-5 指针操作与数组元素的关系 对数组元素的访问,下标方式和指针方式是等价的,但从语言系统内部处理机制上讲,指针方式效率高。需要注意的是:指针方式不如下标方式直观。下标方式可以直截了当地看出要访问的是数组中的哪个元素;而对于指向
4、数组的指针变量,进行运算以后,指针变量的值改变了,其当前指向的是哪一个数组元素不再是一目了然。 例10-7:分析程序。 main( ) int a = 1, 2, 3, 4, 5, 6; int *p; p = a; /* 指针p为数组的首地址 */ printf(%d, *p); printf( %dn, *(+p); /* 以下两个语句等价 */ printf(%d, *+p); printf( %dn, *(p-); /* *(p-)等价于*p- */ p += 3; printf(%d %dn, *p, *(a+3); 运行结果: 1 2 3 3 5 4 此例中指向数组a与指针变量p的
5、指向变化情况见图10-6。 注意,第2个printf语句中的*(+p),是先使指针p自增加,再取指针p值作*运算,它的含义等价于第3个printf语句中的*+p。而*(p-)是先取指针p值作* 运算,再使指针p自减减。 用指针方式实现对数组的访问是很方便的,可以使源程序更紧凑、更清晰。 10.3.2 指针基本运算 对于指针的运算有三种:指针与正整数的加减运算、两个指针的关系运算,以及两个指针的减法运算。 1. 指针与正整数的加减运算 当指针p指向数组中的元素时,n为一个正整数,则表达式: p+n 表示:指针p所指向当前元素之后的第n个元素。而表达式: p-n 表示:指针p所指向当前元素之前的第
6、n个元素。 最常见的指针加减运算为p+的含义是:指针加,指向数组中的下一个元素;p-的含义是:指针减,指向数组中的前一个元素。 指针与整数进行加减运算后,它们的相对关系如图10-7所示。 由于指针p所指的具体对象不同,所以对指针与整数进行加减运算时,C语言会根据所指的不同对象计算出不同的放大因子,以保证正确操作实际的运算对象。对于字符型,放大因子为1;对于整型,放大因子为2;对于长整型,放大因子为4;对于双精度浮点型,放大因子为 8。不同数据类型的放大因子等于一个该数据类型的变量所占用的内存单元数例10-8:编程将str1复制到str2中。 #include #include main( )
7、char str180, str280, *p1, *p2; printf(Enter string 1:); gets(str1); p1=str1; p2=str2; while ( (*p2=*p1) != 0 ) /* 指针p1的内容送到指针p2 */ p1+; p2+; /* 指针p1和p2分别向后移动1个字符 */ printf(String 2:); puts(str2); 程序中的关键是while语句,(*p2=*p1)!=0的含义是:先将指针p1的内容送到指针p2的内容中,即进行两个指针内容的赋值,然后再判断所赋值的字符是否是串结束标记0,如果不是串结束标记,则执行循环继续进
8、行字符复制;如果是串结束标记,则退出循环,完成串复制。 对于上面程序中的while循环,是可以进行优化的。优化后的循环可以如下: 优化一: while ( *p2 = *p1 ) p1+; p2+; 优化二: while ( *p2+ = *p1+ ) ; /* 循环体为空 */ 2. 两个指针的关系运算 只有当两个指针指向同一个数组中的元素时,才能进行关系运算。 当指针p和指针q指向同一数组中的元素时, 则: pq 当p所指的元素在q所指的元素之后时,表达式的值为1;反之为0。 p=q 当p和q指向同一元素时,表达式的值为1;反之为0。 p!=q 当p和q不指向同一元素时,表达式的值为1;反
9、之为0。 任何指针p与NULL进行p=NULL或p!=NULL运算均有意义,p=NULL的含义是当指针p为空时成立,p!=NULL的含义是当p不为空时成立。 不允许两个指向不同数组的指针进行比较,因为这样的判断没有任何实际的意义。 例10-9:编写程序将一个字符串反向。 #include main( ) char str50, *p, *s, c; printf(Enter string:); gets(str); p=s=str; /* 指针p和s指向str */ while ( *p ) p+; /* 找到串结束标记0 */ p-; /* 指针回退一个字符,保证反向后的字符串有串结束 标记
10、0,指针p指向字符串中的最后一个字符 */ while ( sp ) /* 当串前面的指针s(小于)串后面的指针p时,进行循环 */ c = *s; /* 交换两个指针所指向的字符 */ *s+ = *p; /* 串前面的指针s向后(+1)移动 */ *p- = c; /* 串后面的指针p向前(-1)移动 */ puts(str); 3. 两个指针的减法运算 只有当两个指针指向同一数组中的元素时,才能进行两个指针的减法运算,否则,没有意义。 当两个指针指向同一数组中的元素时,p-q表示指针p和q所指对象之间的元素数量。利用这一意义,可以求出一个字符串的长度。 例10-10:编写程序求字符串的长
11、度。 #include main( ) char str50, *p=str; printf(Enter string:); gets(str); while ( *p ) p+; /* 找到串结束标记0。退出循环时p指向0 */ printf(String length=%sn, p-str ); /* 指向同一字符数组的两个指针进行减法运算,求出串长 */ 10.3.3 通过指针引用二维数组中的元素 在C语言中,二维数组是按行优先的规律转换为一维线性存放在内存中的,因此,可以通过指针访问二维数组中的元素。 如果有:int aMN; 则将二维数组中的元素aij转换为一维线性地址的一般公式是:
12、 线性地址aiMj 其中:a为数组的首地址, M和N分别为二维数组行和列的元素个数。 若有:int a43, *p; p = &a00; 则二维数组a的数据元素在内存中存储顺序及地址关系如图10-8所示。 这里,a表示二维数组的首地址;a0表示0行元素的起始地址,a1表示1行元素的起始地址,a2和a3分别表示2行和3行元素的起始地址。 数组元素aij的存储地址是:&a00+i*n+j。 我们可以说:a和a0是数组元素a00的地址,也是0行的首地址。a+1和a1是数组元素a10的地址,也是1行的首地址。 由于a 是二维数组,经过两次下标运算 之后才能访问到数组元素。所以根据C语言的地址计算方法,
13、a要经过两次*操作后才能访问到数组元素。这样就有:*a是a0的内容,即数组元素a 00的地址。*a是数组元素a00。a0是数组元素a00的地址,*a0是素组元素a00。 例10-11:给定某年某月某日,将其转换成这一年的第几天并输出。 此题的算法很简单,若给定的月是i,则将1、2、3、i-1月的各月天数累加,再加上指定的日。但对于闰年,二月的天数29天,因此还要判定给定的年是否为闰年。为实现这一算法,需设置一张每月天数列表,给出每个月的天数,考虑闰年非闰年的情况,此表可设置成一个2行13列的二维数组,其中第1行对应的每列(设112列有效)元素是平年各月的天数,第2行对应的是闰年每月的天数。程序
14、中使用指针作为函数day_of_year的形式参数。 #include main( ) static int day_tab213= 0,31,28,31,30,31,30,31,31,30,31,30,31, 0,31,29,31,30,31,30,31,31,30,31,30,31 ; int y, m, d; scanf(%d%d%d, &y, &m, &d); printf(%dn, day_of_year(day_tab,y,m,d) ); /* 实参为二维数组名 */ day_of_year(day_tab,year,month,day) int *day_tab; /* 形式参数
15、为指针 */ int year, month, day; int i, j; i = (year%4=0&year%100!=0) | year%400=0; for ( j=1; j= ulAllocedLines)/ * 当前竖向空间已经不够了,通过realloc对其进行扩展。ulAllocedLines += 50; / 每次扩展50行。 ppText = realloc (ppText, ulAllocedLines * (char *);if (NULL = ppText)return; / 内存分配失败,返回 ppTextulCurrLines+ = p; / 横向“扩展”,指向不
16、定长字符串 问题:指针数组与数组指针与指向指针的指针指针和数组分别有如下的特征:指针:动态分配,初始空间小数组:索引方便,初始空间大下面使用高维数组来说明指针数组、数组指针、指向指针的指针各自的适合场合。 多维静态数组:各维均确定,适用于整体空间需求不大的场合,此结构可方便索引,例a1040。 数组指针:低维确定,高维需要动态生成的场合,例ax40。 指针数组:高维确定,低维需要动态生成的场合,例a10y。 指向指针的指针:高、低维均需要动态生成的场合,例axy。问题:数组名相关问题假设有一个整数数组a,a和&a的区别是什么?答案与分析:a = &a = &a0,数组名a不占用存储空间。需要引
17、用数组(非字符串)首地址的地方,我一般使用&a0,使用a容易和指针混淆,使用&a容易和非指针变量混淆。区别在于二者的类型。对数组a的直接引用将产生一个指向数组第一个元素的指针,而&a的结果则产生一个指向全部数组的指针。例如:int a2 = 1, 2;int *p = 0;p = a; /* p指向a0所在的地方 */x = *p; /* x = a0 = 1*/p = &a; /* 编译器会提示你错误,*/*显示整数指针与整数数组指针不一样 */问题:函数指针与指针函数请问:如下定义是什么意思:int *pF1();int (*pF2)();答案与分析:首先清楚它们的定义: 指针函数,返回一个指针的函数。 函数指针,指向一个函数的指针。可知: pF1是一个指针函数,它返回一个指向int型数据的指针。 pF2是一个函数指针,它指向一个参数为空的函数,这个函数返回一个整数。