第 10章 指针类型第 10章 指针类型
10.1 指针与指针变量
10.2 指针与数组
10.3 指针与字符串
10.4 指针与结构体
10.5 指针与链表
10.6 指针与函数
10.7 指针作基类型
10.8 程序设计举例第 10章 指针类型
10.1 指针与指针变量
10.1.1 指针变量的定义
1.
形式:
类型标识符 *变量标识符;
定义存放指定类型数据地址的指针变量。
第 10章 指针类型例如:
(1) int *p1,*p2,*p3;
定义指向整型数据的指针变量 p1,p2,p3。
(2) float *q1,*q2,*q3;
定义指向实型数据的指针变量 q1,q2,q3。
(3) char * r1,*r2,*r3;
定义指向字符型数据的指针变量 r1,r2,r3。
第 10章 指针类型
(4) struct date
{int year;
int month;
int day;
} *t1,*t2,*t3;
定义指向 struct date类型数据的指针变量 t1,t2,t3。
第 10章 指针类型说明:
(1) 指针变量可与普通变量混合定义,指针变量的定义与普通变量的定义用变量名前加,*” 区分 。 例如:
int i,*p; /*定义整型变量 i,指针变量 p*/
(2) 空指针,NULL”是一特殊的指针数据,表示空地址,相当于整型数据中的 0,字符数据中的空格 。
第 10章 指针类型
(3) 指针变量只能用于存放指定类型数据的地址 。
如以上定义的一些指针变量,p1,p2,p3只能存放整型数据的地址,q1,q2,q3只能存放实型数据的地址,
r1,r2,r3只能存放字符型数据的地址,t1,t2,t3只能存放 struct date型数据的地址 。
第 10章 指针类型
(4) 指针变量不能直接赋以具体地址值,不能从键盘输入值 。 指针变量通过间接赋以相关数据的地址,
或调用存储空间分配函数得到值 。 例如:
int i,j,k;
int*p1=&i,* p2=&j,* p3=&k; /* p1得到 i的地址,
p2得到 j的地址,p3得到 k的地址 */
第 10章 指针类型
(5) 指针类型隐含在指针变量的定义中 。 例如,在上面定义指针变量 p1,p2,p3时,实际上在背后隐含定义了一指向整型数据的指针类型,从形式上可以将 int*看成是指向整型数据的指针类型 。
与数组定义一样,通过 typedef可以将指针类型与指针变量分离 。 例如,上面定义的指针变量 p1,p2,p3,
可以改用如下形式:
typedef int *INTPOINT;
INTPOINT p1,p2,p3;
第 10章 指针类型
(6) 指针变量本身占有 2字节的存储空间 。
(7)“void *”指针类型定义的指针变量,不指向哪一种特定类型的数据,在实际使用时通过强制类型转换成指向特定类型的数据 。
第 10章 指针类型
2,
例如,对指针变量 p1,p2,p3,假定已有值,*p1、
*p2,*p3代表指针变量 p1,p2,p3所指向的数据,也就是 p1,p2,p3的值对应的存储单元里存放的数据,
称为指针变量所指向的变量,简称指针指向变量 。
如果指针变量 p1,p2,p3分别存放整型变量 i,j、
k的地址,则 p1指向 i,p2指向 j,p3指向 k。 图 10 - 1来直观反应指针变量与指针指向变量的关系 。
第 10章 指针类型图 10 - 1
第 10章 指针类型指针指向变量 *p1,*p2,*p3相当于整型变量 i,j,k。 例如:
int *p=&i;
scanf(,%d”,p) ; /*等价于 scanf(,%d”,&i) */
printf(,%d”,*p); /*等价于 printf(,%d”,i) */
第 10章 指针类型
10.1.2 指针的运算
1,引用运算
1) 取地址运算 ( &)
取地址运算,&”,对指针变量进行取地址运算,
可以得到指针变量本身的地址 。
第 10章 指针类型
2) 取内容运算 ( *)
取内容运算,*”,前称指针运算,用于获取地址数据对应存储单元的内容 。 取内容运算的优先级与取地址运算优先级相同,也为第 2级,结合性亦为右结合 。
对指针变量,进行取内容运算可以得到指针变量所指向的数据 。
取内容运算与取地址运算实质上是一对互逆运算 。
例如:
int a,*p=&a;
*( &a)就是 a,&( *p)就是 p; p指向 a,*p与 a等价。
第 10章 指针类型
2,算术运算
1) 加减运算加减运算常用于数组的处理 。 对指向一般数据的指针,
加减运算无实际意义 。 例如:
int a[ 10],*p=a,*x;
x=p+3; /*实际上是 p加上 3*2个字节赋给 x,x指向数组的第三个分量 */
对于不同基类型的指针,指针变量,加上,或,减去,
一个整数 n所移动的字节数是不同的 。 例如:
float a[ 10],*p=a,*x;
p=p+3; /*实际上是 p加上 3*4个字节赋给 x,x依然指向数组的第三个分量 */
第 10章 指针类型
2)
指针变量自增,自减运算具有上述运算的特点,但有前置后置,先用后用的考虑,务请小心 。 例如:
int a[ 10],*p=a,*x;
x=p++; /* x指向数组的第一个分量,p指向数组的第二个分量 */
x=++p; /* x,p均指向数组的第二个分量 */
*p++相当于 *( p+ +) 。 *( p++) 与 ( *p) ++ 含义不同,前者表示地址自增,后者表示当前所指向的数据自增 。
第 10章 指针类型
3)
指针相减得到两指针之间数据的个数,一般用于数组处理。
第 10章 指针类型
3,关系运算两指针的关系运算表示两指针的先后位置关系,
一般用于数组处理 。 除空指针外,不能进行指针与一般数值的关系运算 。
第 10章 指针类型
10.1.3 利用指针处理简单数据通过指向简单数据的指针变量来处理数据的步骤是:
(1) 定义以相应简单数据类型为基类型的指针变量 。
。
(2) 在指针变量与要处理的数据之间建立关联 。
只需将相应数据的地址赋给指针变量 。
(3) 使用指针所指向的变量来完成数据处理。
第 10章 指针类型例如,要利用指针处理 float数据 x:
(1) float *p;
(2) p=&x;
(3) *p即 x
第 10章 指针类型例 10-1 利用指针,求两个整数的和 。
/*程序 10 - 1,利用指针,求两个整数的和 */
main( )
{int i,j;
int *p,*q; /*定义指针变量 */
int sum;
p=&i; q=&j; /*建立关联 */
scanf( ″%d,%d″,p,q) ;
sum=*p+*q; /*使用 */
printf( ″%d,%d\n″,*p,*q) ;
printf(″和 =%d\n″,sum) ;
}
第 10章 指针类型例 10 – 2 指针运算示例 。
/*程序 10 - 2,指针运算 */
main( )
{char c=′a′;
char *p=&c;
int a1,a2;
int *p1,*p2;
a1=100; p1=&a1;
a2= (*p1)/3+7; p2=&a2;
printf( ″a1=%d,a2=%d,*p1=%d,*p2=%d\n″,
a1,a2,*p1,*p2) ;
a1=( *p1) ++; a2=*p2++;
第 10章 指针类型
printf( ″a1=%d,a2=%d,*p1=%d,*p2=%d\n″,
a1,a2,*p1,*p2) ;
printf( ″%c,%c\n″,c,*p) ;
}
运行结果:
a1=100,a2=40,*p1=100,*p2=40
a1=101,a2=40,*p1=101,*p2=随机值 ( p2指向
a2后一个数据单元 )
a,a
第 10章 指针类型
10.1.4 指针作函数参数例 10 – 3 将两个整数按从小到大的顺序输出。
先定义一个函数,用指针变量作参数,实现两个数的交换,然后在主函数中调用它,完成两个整数从小到大的顺序输出 。
/*程序 10 - 3,将两个整数顺序输出 */
void exchang( p1,p2) /*交换两个数 */
int *p1,*p2;
{int p;
p=*p1; *p1=*p2; *p2=p; /*结果通过 *p1,*p2带回 */
}
第 10章 指针类型
main(
{int a,b;
int *r,*s;
scanf( ″%d,%d″,&a,&b);
r=&a; s=&b;
if( a>b) exchang( r,s);
printf( ″%d,%d\n″,a,b);
}
输入数据,9,4
运行结果,4,9
第 10章 指针类型两点说明:
(1) 若在函数中交换指针变量的值,实参 r,s并不改变,指针参数亦是传值 。
int *p;
p=p1; p1=p2; p2=p;
不要希望如此完成处理 。
(2) 函数中交换值时不能使用无值的指针变量作临时变量 。 例如:
int *p;
*p=*p1; *p1=*p2; *p2=*p;
p无值,*p无意义。
第 10章 指针类型
10.2 指针与数组
10.2.1 指向一维数组的指针变量可以利用指向一维数组的指针变量,完成数组数据的操作处理,具体步骤如下:
(1) 定义与数组相同基类型的指针变量 。
即定义指向数组的指针变量 。
(2) 在指针变量与要处理的数组 ( 元素 ) 之间建立关联 。 只需将相应数组的首地址赋给指针变量 。
(3) 使用指针所指向的变量来完成数组元素(数组)
的操作处理。
第 10章 指针类型例如,要利用指针处理整型数组 a:
(1) int *p; /*定义指针变量 */
(2) p=a; 或 p=&a[ 0] ; /*建立关联 */
p+i是下标为 i 的数组的元素地址 。
(3) *p即 a[ 0],*( p+i) 即 a[ i] 。
*p++是 p当前指向的数组元素的下一个元素 。
如此得到处理数组的指针法。
第 10章 指针类型与指针法相类似的是处理数组的位移法,或称首地址法 。 通过数组的首地址计算出下标为 i的数组的元素地址 ( a+i),*( a+i) 即 a[ i] 。
指针法中 p是变量,用来存放数组元素的地址 。
位移法中 a是常量,代表数组的首地址 。
第 10章 指针类型例 10-4 分别用下标法,指针法,位移法输入,输出数组元素 。
方法一:
/*程序 10 — 4 — 1,下标法实现数组的输入,输出 */
main(
{ int a[ 10];
int i;
for ( i=0; i<10; i++
scanf ( ″%d″,&a[ i]);
printf( ″\n″);
for ( i=0; i<10; i++
printf( ″%3d″,a[ i]);
}
第 10章 指针类型方法二:
/*程序 10— 4 — 2,指针法实现数组的输入,输出 */
main( )
{ int a[ 10] ;
int i,*p; /*定义指针变量 */
p=a; /*建立关联 */
for ( i=0; i<10; i++)
scanf ( ″%d″,p++) ;
printf( ″\n″) ;
for ( p=a; p<a+10; p++) /*使用 */
printf( ″%3d″,*p) ;
}
第 10章 指针类型方法三:
/*程序 10— 4 — 3,位移法实现数组的输入,输出 */
main( )
{ int a[ 10] ;
int i,;
for ( i=0; i<10; i++)
scanf ( ″%d″,&a[ i]) ;
printf( ″\n″) ;
for ( i=0; i<10; i++)
printf( ″%3d″,*( a+i)) ;
}
第 10章 指针类型
10.2.2 数组作函数参数例 10 — 5 求 n个整数的最大值,最小值 。
求 n个数的最大值的一般方法我们已非常熟悉,在这里主要考察引入指针处理数组后,数组作函数参数的应用 。
假定不超过 100个数,数据输入输出在主函数中完成,求最大值,最小值用一个函数完成,n个数用参数传递,最大值,最小值使用全局变量 。
方法一,形参用指针,实参用数组名 。
程序如下:
第 10章 指针类型
/*程序 10 — 5 — 1,求 n个整数的最大值,最小值 */
# define L 100
int max,min;
main(
{int a[ L];
int n,i;
void max -min( );
printf( ″″);
scanf( ″%d″,&n);
printf( ″请输入 %d个数,″,n);
for( i=0; i<n; i++
scanf( ″%d″,&a[ i]);
max -min( a,n); /*调用函数 */
printf( ″最大值 =%4d,最小值 =%4d\n″,max,min);
}
第 10章 指针类型
void max -min( p,x) /*求最大值,最小值函数 */
int *p,x;
{int i;
max=min=*p; /*最大值,最小值初始化为第一个数据 */
for( i=1; i<x; i++) /*将第 i个数据分别与最大值,最小值比较 */
{if( *( p+i) >max) max=*( p+i);
if( *( p+i) <min) min=*( p+i);
}
}
运行结果:
请输入数的个数 n,6
请输入 6个数,32 54 7 88 13 49
最大值 = 88,最小值 =7
第 10章 指针类型方法二,形参用数组,实参用指针。
程序如下:
/*程序 10 — 5 — 2,求 n个整数的最大值,最小值 */
# define L 100
int max,min;
main(
{int a[ L],*p;
int n,i;
void max -min( );
printf( ″请输入数的个数 n,″);
scanf( ″%d″,&n);
printf( ″请输入 %d个数,″,n);
for( i=0; i<n; i++
scanf( ″%d″,&a[ i]);
第 10章 指针类型
p=a;
max -min( p,n); /*调用函数 */
printf( ″最大值 =%4d,最小值 =%4d\n″,max,min);
}
void max -min( b,x) /*求最大值,最小值函数 */
int b[],x;
{int i;
max=min=b[ 0]; /*最大值,最小值初始化为第一个数据 */
for( i=1; i<x; i++) /*将第 i个数据分别与最大值,最小值比较 */
{if( b[ i] >max) max=b[ i];
if( b[ i] <min) min=b[ i];
}
}
第 10章 指针类型方法三,形参用指针,实参用指针。
程序如下:
/*程序 10 — 5 — 3,求 n个整数的最大值,最小值 */
# define L 100
int max,min;
main(
{int a[ L],*p;
int n,i;
void max -min( );
printf( ″请输入数的个数 n,″);
scanf( ″%d″,&n);
printf( ″请输入 %d个数,″,n);
for( i=0; i<n; i++
scanf( ″%d″,&a[ i]);
第 10章 指针类型
p=a;
max-min( p,n); /*调用函数 */
printf( ″最大值 =%4d,最小值 =%4d\n″,max,min);
}
void max -min( q,x) /*求最大值,最小值函数 */
int *q,x;
{int i;
max=min=*q; /*最大值,最小值初始化为第一个数据 */
for( i=1; i<x; i++) /*将第 i个数据分别与最大值 \最小值比较 */
{if( *( q+i) >max) max=*( q+i);
if( *( q+i) <min) min=*( q+i);
}
}
第 10章 指针类型
10.2.3 指向二维数组的指针变量
1,二维数组的指针对于二维数组,相当于一张二维表格,存储按行按列存放 。
二维数组具有首地址,行地址,元素地址等相关指针 。 数组名代表首地址,称为二维数组的指针;行地址是二维数组中一行的首地址,二维数组中一行相当于一个一维数组;元素地址是二维数组的具体分量地址 。
第 10章 指针类型例如,对二维整型数组 a[ 4] [ 5],相当于下面的二维数据表:
第 0列 第 1列 第 2列 第 3列 第 4列第 0行,a[ 0] [ 0],a[ 0] [ 1],a[ 0] [ 2],a[ 0] [ 3],a[ 0] [ 4],
相当一维数组 a[ 0]
第 1行,a[ 1] [ 0],a[ 1] [ 1],a[ 1] [ 2],a[ 1] [ 3],a[ 1] [ 4],
相当一维数组 a[ 1]
第 2行,a[ 2] [ 0],a[ 2] [ 1],a[ 2] [ 2],a[ 2] [ 3],a[ 2] [ 4],
相当一维数组 a[ 2]
第 3行,a[ 3] [ 0],a[ 3] [ 1],a[ 3] [ 2],a[ 3] [ 3],a[ 3] [ 4],
相当一维数组 a[ 3]
第 10章 指针类型
(1) a代表整个二维数组的首地址,也就是第 0 行的首地址。也可用 a[ 0],&a[ 0][ 0]表示。
(2) a[ i] 代表第 i行的首地址 。
整个二维数组也相当于一个一维数组 a[ 0],a[ 1],
a[ 2],a[ 3],基于一维数组的处理方法,第 i行的首地址还可用 *( a+i),a+i,&a[ i],&a[ i] [ 0] 表示 。
请注意 *a表示 0 行首地址,而非 a[ 0] [ 0] 。
第 10章 指针类型
(3) a[ i] +j代表 a[ i][ j]的地址。 a[ i][ j]的地址还可用 *( a+i) +j表示。
请注意 a[ i][ j]的地址不能用( a+i) +j表示,因为此时实际表示的是第( i+j)行的地址。
a[ i] [ j] 相对 a[ 0] [ 0] 的绝对地址可用 a[ 0]
+i*5+j计算 。 如果每行有 m个元素,a[ i] [ j] 相对 a
[ 0] [ 0] 的绝对地址可用 a[ 0] +i*m+j计算 。
当然 &a[ i] [ j] 是我们早就知道的 a[ i] [ j] 的地址 。
第 10章 指针类型
(4) 基于上面的分析,引入指针后,二维数组的分量 a[ i] [ j] 可表示成:
a[ i] [ j] *( a[ i] +j) *( *( a+i) +j) *( a
[ 0] +i*m+j)
相应得到处理二维数组的多种形式位移法 。
第 10章 指针类型例 10 — 6 输出一指定数组。
程序如下:
/*程序 10— 6,输出一指定数组 */
main( )
{static int a[ 4] [ 5] ={1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20};
int i,j;
for( i=0; i<4; i++)
{for( j=0; i<5; j++)
第 10章 指针类型
printf( ″%6d″,*( a[ 0] +i*5+j)) ; /*或 printf( ″%6d″,*
( *( a+i) +j)) ; */
printf( ″\n″) ;
}
}
运行结果,1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
第 10章 指针类型
2,指向二维数组元素的指针变量利用指向二维数组元素的指针变量,可以完成二维数组数据的操作处理,这也就是处理二维数组的指针法 。
(1) 定义与数组相同基类型的指针变量 。
(2) 在指针变量与要处理的数组 ( 元素 ) 之间建立关联 。
(3) 使用指针所指向的变量来完成数组元素(数组)
的操作处理。
第 10章 指针类型例 10 — 7 输出同例 10 — 6指定数组。
/*程序 10 — 7,输出指定数组 */
main(
{static int a[ 4][ 5] ={1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20};
int *p;
for( p=a[ 0]; p<a[ 0] +20; p++
{if(( p-a[ 0]) %5= =0) printf( ″\n″); /*每行输出 5个元素 */
printf( ″%6d″,*p);
}
}
运行结果,1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
第 10章 指针类型
3.
一个二维数组相当于多个一维数组 。 通过指向整个一维数组的指针变量,也可以完成二维数组数据的操作处理,
这也是处理二维数组的指针法 。 例如:
int ( *p) [ 5] ;
定义了一个指向具有 5个元素的一维数组的指针变量 p,p
的增值以指向的一维数组为单位,一维数组的 5个元素可用 ( *p) [ 0],( *p) [ 1],( *p) [ 2],( *p)
[ 3],( *p) [ 4] 表示 。
a是上面定义的数组,如果 p=a,p+i指向 a数组的第 i行,
*( p+i) +j指向 a数组的第 i行第 j列元素 。
第 10章 指针类型例 10 — 8 输出上例二维数组中某行某列元素的值。
程序如下:
/*程序 10 — 8,输出上例二维数组中某行某列元素的值 */
main(
{static int a[ 4][ 5] ={1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20};
int ( *p)[ 5],i,j;
p=a;
scanf( ″%d,%d″,&i,&j);
printf( ″a[ %d][ %d] =%d″,i,j,*( *( p+i) +j));
输入数据,3,4
运行结果,a[ 3][ 4] =12
第 10章 指针类型
10.3 指针与字符串
10.3.1 字符串的指针表示例 10 — 9 字符串的指针表示。
main(
{char *s= ″computer″;
printf ( ″%s\n″,s);
}
运行结果,computer
第 10章 指针类型
(1) char *s= ″computer″;
等价于:
char *s;
s= ″computer″;
(2) printf ( ″%s\n″,s) ;
自动转换成:
for( s=″computer″; *s! =′\0′; s++)
printf ( ″%c″,*s) ;
(3) 可以用下标形式引用字符串中的字符 。 如所举例中,s[ 0] 表示 ′c′,s[ 5] 表示 ′t′。
(4) 用指针表示字符串比用数组表示字符串更常用。
第 10章 指针类型例 10 — 10 字符串的复制。
方法一,字符串用字符数组表示,字符数组用位移法处理。
程序如下:
/*程序 10 — 10 — 1,字符串的复制 */
main(
{char ss[ ] =″C LANGUAGE″,ts[ 20];
int i;
for( i=0; *( ss+i)! =′\0′; i++
*( ts+i) =*( ss+i);
*( ts+i) =′\0′;
printf( ″原字符串 ss=%s\n″,ss);
printf( ″目标字符串 ts=%s\n″,ts);
}
运行结果:
原字符串 ss=C LANGUAGE
目标字符串 ts=C LANGUAGE
第 10章 指针类型方法二,字符串用字符指针表示。
程序如下:
/*程序 10 — 10 — 2,字符串的复制 */
main(
{char ss[ ] =″C LANGUAGE″,ts[ 20];
char *ps,*pt;
ps=ss; pt=ts;
for(; *ps=′\0′; ps++,pt++
*pt=*ps;
*pt=′\0′;
printf( ″原字符串 ss=%s\n″,ps);
printf( ″目标字符串 ts=%s\n″,pt);
}
第 10章 指针类型
10.3.2 字符串(指针)作函数参数例 10 — 11 求字符串的长度函数。
int len( str) /*求字符串的长度函数 */
char str[ ];
{char *p=str;
int l=0;
while( *p! =′\0′
l++;
return( l);
}
第 10章 指针类型或直接用字符指针作函数参数,函数如下:
int len( ps) /*求字符串的长度函数 */
char *ps;
{int l=0;
while( *ps! =′\0′
l++;
return( l);
}
第 10章 指针类型例 10 — 12 用函数实现字符串的复制。
void copy( from,to) /*字符串复制函数 */
char *from,*to;
{for(; *from! =′\0′; from++,to++
*to=*from;
*to=′\0′;
}
第 10章 指针类型调用时对应实参可以是字符数组,也可以是字符类型的指针 。
for语句可改写成:
for( ; ( *to=*from) ! =′\0′; from++,to++) ;
或 for( ; ( *to++=*from++) ! =′\0′; ) ;
或 for( ; ( *to++=*from++) ; ) ;
也可改用 while语句:
while( *from! =′\0′)
{to++=from++; }
或
while( ( *to++=*from++) ! =′\0′) ;
或
while( *to++=*from++);
第 10章 指针类型
10.3.3 字符指针变量和字符数组的区别
(1) 字符数组由若干个元素组成,每个元素中存放字符串的一个字符,而字符指针变量中存放的是字符串的首地址 。
(2) 初始化方式不同 。 对字符数组初始化要用 static存储类别,在编译时进行 。 而对字符指针变量初始化不必加 static,在实际执行时进行 。
(3) 赋值方式不同 。 对字符数组不能整体赋值,只能转化成份量,对单个元素进行 。 而字符指针变量赋值可整体进行 。 例如:
char s[ 10] ;
s=″C++″; /*错,s是常量,怎能被赋值 */
第 10章 指针类型
(4) 在定义一个字符数组时,编译时即已分配内存单元,有确定的地址 。 而定义一个字符指针变量时,
给指针变量分配内存单元,但该指针变量具体指向哪个字符串,并不知道,即指针变量存放的地址不确定 。
例如:
char s[ 10] ;
char *p;
scanf( ″%s″,s) ; /*正确 */
scanf( ″%s″,p); /*非常危险,p的值动态 */
第 10章 指针类型
(5) 字符指针变量的值可以改变,字符数组名是一个常量,不能改变 。 例如,
main( )
{char *s=″china man″;
s+=6;
printf( ″%s″,s) ;
}
运行结果,man
第 10章 指针类型
10.4 指针与结构体
10.4.1 指向结构体数据的指针变量对使用指针来处理数据读者应有了一些体会,即先定义一以数据或元素类型为基类型的指针变量;其次在定义的指针变量与要处理的数据之间建立关联,
让指针变量指向要处理的数据;然后引用指针指向变量来完成数据的处理 。
第 10章 指针类型例 10 — 13 指向结构体变量的指针变量的应用示例 。
假设有一结构体,包含某人的姓名和年龄,用指向结构体变量的指针变量完成输出处理 。
程序如下:
/*程序 10 — 13,指针应用于结构体 */
main( )
{struct person
{char *name;
int age;
} someone;
第 10章 指针类型
struct person *p; /*定义结构体类型的指针变量 */
someone.name=″张三 ″; /*假定姓名为张三 */
someone.age=20;
p= &someone; /*建立关联,*p即 someone*/
printf( ″姓名 =%s,年龄 =%d\n″,( *p),name,( *p),age);
/*等价于 printf( ″姓名 =%s,年龄 =%d\n″,someone.name,
someone.age); */
}
运行结果,姓名 =张三,年龄 =20
第 10章 指针类型例 10 — 14 对上例,考虑三个人组成的结构体数组的处理。
程序如下:
/*程序 10 — 14,结构体数组的指针处理 */
struct person
{char *name;
int age;
};
第 10章 指针类型
struct person sp[ 3] ={ ″张三 ″,18,″李四 ″,19,″王五 ″,20};
main(
{struct person *p;
printf( ″ 姓名 年龄 \n″);
for ( p=sp,p<sp+3; p++);
printf( ″%6s %6d\n″,p-> name,p-> age );
}
运行结果:
姓名张三 18
李四 19
王五 20
第 10章 指针类型
10.4.2 指向结构体的指针作函数参数例 10 — 15 有 40个学生,每个学生包括学号,姓名及成绩,用函数输出成绩最高的学生数据 。
/*程序 10 — 15指向结构体的指针作函数参数例 */
struct student
{int num;
char *name;
float score;
}
void print( p) /*输出函数 */
struct student *p; /*结构体指针作函数参数 */
第 10章 指针类型
{printf ( ″%d %s %f \n″,p-> num,p-> name,p-> score); }
int maxi( p) /*求最高分学生序号函数 */
struct student *p;
{int i,t;
t=0;
for ( i=1; i<40; i++
if ( *( p+i),score>*( p+t),score
t=i;
return( t);
}
main(
{struct student stu[ 40];
int i,k;
for ( i=1; i<40; i++
scanf ( ″%d %s %f″,&stu[ i],num,stu[ i],name,&stu[ i],score);
k=maxi( stu); /*结构体数组作函数参数 */
print ( &stu[ k]); /*结构体指针作函数参数 */
}
第 10章 指针类型
10.5 指针与链表链表与数组不同,是一种动态地进行存储分配的数据结构,表中各元素在内存中可以不连续存放 。 要查找某一元素,必须先找到上一个元素,根据它提供的下一元素地址才能找到下一个元素 。 如果不知道第一个元素的地址,则整个链表无法访问 。 故一般在链表的开头设立头指针,存放第一个元素的内存地址 。 这样,整个链表就可以从头到尾访问 。 链表的访问只能从头到尾,故是一种顺序访问,而非随机访问 。
在结构体中,若只有一个指针成员域,则得到的链表为单链表;若有多个指针成员域,则得到的链表为多重链表 。
第 10章 指针类型
10.5.1 单链表的数据描述一般形式:
struct
{成员及类型说明;
struct 结构体名 *指针域;
}
第 10章 指针类型在单链表中,指针域只有一个,但非指针域个数不限制 。 为了描述方便,我们将非指针域个数也定为一个,并且按下面给定的类型来描述链表:
struct linklist
{int data;
struct linklist *next;
};
struct linklist *head;
第 10章 指针类型图 10 — 2
第 10章 指针类型
10.5.2 单链表的建立
(1) malloc( size)
在内存的动态存储区申请一个长度为 size字节的连续空间 。
(2) calloc( n,size)
在内存的动态存储区申请 n个长度为 size 字节的连续空间,函数返回值为分配空间的首地址 。 若此函数未被成功执行,函数返回值为 0。
(3) free( p)
释放由指针 p所指向的存储单元,而存储单元的大小是最近一次调用 malloc( ) 或 calloc( ) 函数时所申请的存储空间 。
第 10章 指针类型
1,头插法例 10 — 16 用头插法建立一些正整数链表。
/*程序 10 — 16,用头插法建立一些正整数链表 */
main(
{struct linklist
{int data; /*数据域 */
struct linklist *next; /*指针域 */
};
第 10章 指针类型
struct linklist *head,*p; /*head头指针,p申请单元指针 */
head=NULL; /*空链表 */
p=( struct linklist*) malloc( sizeof( struct linklist));
/*申请空间,并将指针强制转换成所指向的结构体类型 */
scanf( ″%d″,&p->data); /*得到数据 */
while( p->data>0
{p->next=head; /*建立连接 */
head=p; /*用头指针保存当前结点 */
p= ( struct linklist*) malloc( sizeof( struct linklist)); /*为下一个结点申请空间 */
scanf( ″%d″,&p->data); /*得到下一个结点数据 */
}
}
第 10章 指针类型
2,尾插法若将链表的左端固定,链表不断向右延伸,这种建立链表的方法称为尾插法 。 尾插法建立链表时,头指针固定不动,故必须设立一个搜索指针,向链表右边延伸,则整个算法中应设立三个链表指针,即头指针 head,搜索指针 p2,申请单元指针 p1。 尾插法最先得到的是头结点 。
/*程序 10 — 17,用尾插法建立一些正整数链表 */
main(
{struct linklist
{int data;
struct linklist *next;
};
第 10章 指针类型
struct linklist *head,*p1,*p2;
head=NULL; /*空链表 */
p1=( struct linklist*) malloc( sizeof( struct linklist)); /*申请空间 */
scanf( ″%d″,&p1->data); /*得到数据 */
while( p1->data>0
{if( head==NULL) head=p1;
else p2->next=p1; /*建立连接 */
p2=p1; /*保存当前结点 */
p1= ( struct linklist*) malloc( sizeof( struct linklist)); /*为下一个结点申请空间 */
scanf( ″%d″,&p1->data); /*得到下一个结点数据 */
}
p2->next=NULL; /*尾结点指针域为空指针 */
}
第 10章 指针类型
10.5.3 单链表的基本操作注意两点:
(1) 将链表传递进函数,只需将链表头指针传递进函数 。 函数的形参对应实参头指针 。
(2) 对链表的访问用条件循环控制,循环的条件是结点的指针域非空。
第 10章 指针类型
1,输出链表中所有结点
void print( struct linklist *head) /*输出链表所有结点 */
{struct linklist *p;
p=head; /*p指向链表第一个结点 */
while( p! =NULL
{printf( ″%d″,p->data);
p=p->next; /*p指向下一个结点 */
}
}
第 10章 指针类型
2,
只需将上述输出结点改成计数即可。
int count( struct linklist *head) /*统计链表中结点个数 */
{int n=0;
struct linklist *p;
p=head;
while( p! =NULL
{n++;
p=p->next;
}
return( n);
}
第 10章 指针类型
3,插入操作
void ins( struct linklist *head,int i,int x) /*插入结点 */
{int j;
struct linklist *p,*q;
p=head;
j=1;
while(( p! =NULL) &&( j<i)) /*找插入位置 */
{p=p->next;
j++;
}
q=( struct linklist *) malloc( sizeof( struct linklist)); /*产生插入结点 */
q->data=x;
q->next=p->next; /*q插入 p之后 */
p->next=q;
}
第 10章 指针类型
4,删除操作假设删除链表中第 i个结点,先找到第 i-1个结点和第 i
个结点,然后将第 i+1个结点链接在第 i-1个结点后,再释放第 i个结点所占空间,完成删除操作 。
第 10章 指针类型
void del( struct linklist *head,int i) /*删除结点 */
{int j;
struct linklist *p,*q;
p=head;
j=1;
while(( p! =NULL) &&( j<i)) /*找第 i-1个结点和第 i个结点指针 q,p*/
{q=p;
p=p->next;
j++;
}
if( p==NULL) printf( ″找不到结点! ″);
else
{q->next=p->next; /*删除第 i个结点 */
free( p);
}
}
第 10章 指针类型双链表有两个指针域,一个指针指向左边结点,一个指针指向右边结点,用头指针表示开始结点,用尾指针表示结尾结点 。 例如:
struct linklist
{int data;
struct linklist *llink,*rlink;
};
struct linklist *head,*rear;
第 10章 指针类型
10.6 指针与函数
10.6.1 指向函数的指针变量
1.
形式如下:
类型标识符 ( *变量标识符 ) ( ) ;
例如:
int ( *p)( );
第 10章 指针类型说明:
(1) 定义指向函数的指针变量,可以指向一类函数 。
(2) 定义指向函数的指针变量时,括号不能省略 。
形式 int *p( ) 定义的是指针函数头,返回值是指向整型数据的指针值,而不是指向函数的指针变量 。
(3) 对指向函数的指针变量 p,p+i,p++,p--等运算无意义 。
第 10章 指针类型
2.
定义了指向函数的指针变量,就可以在指针变量与特定函数之间建立关联,让指针变量指向特定函数 。
建立关联的方法为:
指针变量 =函数名;
说明:
(1) 指针变量只能指向定义时所指定的一类函数 。
(2) 一个指针变量可以先后指向多个不同的函数。
第 10章 指针类型
3,利用指针实现函数调用指针变量一旦指向某函数,利用指针所指向的变量可以实现函数调用 。
一般形式:
( *指针变量)(实参表);
第 10章 指针类型例 10 — 18 通过指针调用函数。
/*程序 10 — 18,通过指针调用函数,求两个数的最大值 */
float max( x,y) /*求两个数的最大值 */
float x,y;
{float z;
z=( x>=y)? x,y;
return( z);
}
main(
{float ( *p)( ); /*定义指向一类实型函数的指针变量 */
float a,b;
float m;
scanf( ″%d,%d″,&a,&b);
p=max; /*建立关联 */
m=( *p)( a,b); /*利用指针实现函数调用 */
printf( ″a=%d,b=%d,max=%d\n″,a,b,m);
}
第 10章 指针类型
10.6.2 指向函数的指针变量作函数参数例 10 — 19 编制程序,调用一个多功能函数,对于最大值函数参数,求两个数的最大值;对于最小值函数参数,求两个数的最小值 。
/*程序 10 — 19,指向函数的指针变量作函数参数 */
main(
{int max( ),min( ),fun( );
int a,b;
scanf( ″%d,%d″,&a,&b);
printf( ″最大值 =″);
fun( a,b,max);
printf( ″最小值 =″);
fun( a,b,min);
}
第 10章 指针类型
max( x,y) /*最大值函数 */
int x,y;
{if( x>y) return( x);
else return( y);
}
min( x,y) /*最小值函数 */
int x,y;
{if( x<y) return( x);
else return( y);
}
fun( x,y,p) /*多功能函数 */
int x,y;
int ( *p)( ); /*p参数为指向整型函数的指针变量 */
{int result;
result=( *p)( x,y); /*通过指针调用函数 */
printf( ″%d\n″,result);
}
输入数据,28,32
运行结果,最大值 =32 最小值 =28
第 10章 指针类型
10.6.3 指针函数例 10 — 20 设有若干学生,每个学生有 6门课程成绩 。 输入学生序号,输出其全部成绩,用指针函数实现 。
/*程序 10 — 20,指针函数示例 */
float *pointf( p,n) /*计算第 n个学生的成绩开始地址的指针函数 */
float ( *p)[ 6]; /*指向一个学生整个成绩的指针变量 */
int n;
{float *t;
t=*( p+n);
return( t);
}
第 10章 指针类型
main(
{static float score[ ][ 6] ={{70,85,78,80,90,99},{90,
79,63,70,56,68},{49,60,56,50,70,60}};
int m;
float *k;
int i;
printf( ″请输入学生序号,″);
scanf( ″%d″,&m);
k=pointf( score,m); /*调用函数计算序号 m的学生成绩地址 */
printf( ″序号为 %d的学生成绩如下,\n″,*( k+i));
for( i=0; i<4; i++
printf( ″%6.2f\t″,*( k+i));
}
第 10章 指针类型
10.7 指针作基类型
10.7.1 指针数组若数组每个元素的类型为指针类型,则称此数组为指针数组。
指针数组定义的一般形式:
类型标识符 *数组名[常量];
例如:
int *p[ 4];
第 10章 指针类型定义了一个整型指针数组 p,数组元素 p[ 0],p
[ 1],p[ 2],p[ 3] 均为指向整型数据的指针 。
在指针数组的定义中,,*” 和,[ ],形式上均为运算符,但,[ ],的优先级比,*” 的优先级高 。 如上面的例子,p先与 [ 4] 结合,形成 p[ 4],
这显然是数组形式;然后与,*” 结合,形成指针数组 。 注意不要写成 int( *p) [ 4],这是指向整个一维数组的指针变量 。
第 10章 指针类型例 10 — 21 将一些书名按字母顺序输出。
/*程序 10 — 21,将一些书名按字母顺序输出 */
main(
{static char *bookname[ 5] ={ ″pascal″,″clanguage″,″basic″,
″windo
w98″,″office97″;
void sort( );
void output( );
sort( bookname,5);
output( bookname,5);
}
void sort( sname,n) /*排序函数 */
char *sname[ ];
int n;
第 10章 指针类型
{char *tn;
for ( i=0; i<n-1; i++
{t=i;
for( j=i+1; j<n; j++
if( strcmp( sname[ t],sname[ j]) >0) t=i;
if( t! =i
{tn=sname[ i]; sname[ i] =sname[ t]; sname[ t] =tn; }
}
}
void output( oname,n) /*输出函数 */
char *oname[ ];
int n;
{int i;
for ( i=0; i<n-1; i++
printf( ″%10s″,oname[ i]);
}
第 10章 指针类型
10.7.2 多级指针若一个指针的基类型是另外一种指针,则称此为多级指针或指针的指针 。
多级指针的定义形式为:
类型标识符 **变量标识符;
相当于:
( 类型标识符 *) *变量标识符;
所定义的指针变量指向,类型标识符 *” 所描述的指针类型的数据 。 例如:
int *q[ 5],**p=q;
第 10章 指针类型例 10 — 22 将上例中的输出函数改用多级指针实现 。
void output( oname,n) /*用多级指针实现的输出函数 */
char *oname[ ] ;
int n;
{int i;
char **p;
p=oname;
for ( i=0; i<n-1; i++)
{printf( ″%10s″,*p) ;
p++;
}
}
第 10章 指针类型
10.7.3 指针数组作 main函数的形参
main( ) 函数带参数的形式为:
main( argc,argv)
int argc;
char *argv[ ] ;
main( ) 函数对应的实参由程序运行时的命令行参数给定 。 参数 argc为整型,对应命令行中参数个数,
包括执行文件名;参数 argv为字符型的指针数组,每个数组元素是一指向字符数据的指针,对应命令行中各参数的名字 。
第 10章 指针类型使用命令行参数将 C语言程序看作操作系统下的函数,main( ) 函数的参数是在系统与程序之间传递数据 。
命令行的一般形式为:
命令名 参数 1 参数 2 … 参数 n
假定程序运行时发出如下命令:
filename changsha beijing
则 mian( ) 函数中,argc=3,表示有三个命令行参数 ( 文件名也算一个 ),而 argv[ 0] 指向,file1”,argv
[ 1] 指向,changsha”,argv[ 2] 指向,beijing”。
第 10章 指针类型例 10 — 23 编写程序,实现 DOS的 ECHO命令 。
ECHO命令的功能是“参数回显”,不包括
,ECHO”。/*程序 10 — 23,参数回显程序 ECHO.C*/
main( argc,argv
int argc;
char *argv[ ];
{char **p=argv;
while( argc>1)
{p++;
printf( ′%s ",*p);
argc--;
}
}
假定以下面的命令行形式运行程序:
ECHO HU NAN COMPUTER COLLEGE
运行结果,HU NAN COMPUTER COLLEGE
第 10章 指针类型
10.8 程序设计举例有关指针的数据型,用实例说明如下:
int i; /*整型变量 i*/
int *p; /*指向整型数据的指针变量 p*/
int a[ N] ; /*整型数组 a*/
int *p[ N] ; /*指针数组 p,由 N个整型指针组成 */
int ( *p) [ N] ; /*指向整个一维整型数组的指针变量 p*/
int f( ) ; /*整型函数 f,返回值为整型数据 */
int *p( ) ; /*指针函数 p,返回值为指向整型数据的指针 */
int ( *p) ( ) ; /*指向整型函数的指针变量 p*/
int **p; /*二级指针变量 p,
第 10章 指针类型对于指针变量有两种方法进行初始化:
(1) 赋值初始化,注意不能赋具体的物理地址 。
(2) 动态分配,使用存储分配函数 calloc( ),
malloc( ) 申请空间 。
掌握指针的使用,是最终掌握 C语言程序设计关键 。
程序中经常通过在指针变量和要处理数据之间建立关联来处理数据,一般包括三个步骤:
(1) 定义以相关数据类型为基类型的指针变量 。
(2) 在指针变量与要处理的数据之间建立关联 。
(3) 使用指针所指向的变量来完成数据处理 。
有时也可以直接利用指针来处理数据。
第 10章 指针类型例 10 — 24 逆置数组的元素,即将数组的元素反序存放 。
数组的逆置只需将第一个元素与最后一个元素交换,
第二个元素与倒数第二个元素交换,其余依次类推,交换的次数为数组元素个数的一半 。
方法一,不用函数处理 。
设立两个指针,一个指针指向开头元素,一个指针指向末尾元素,交换两指针所指元素,然后第一个指针后移,第二个指针前移 。 重复上述步骤,直到两个指针相遇为止 。
第 10章 指针类型程序如下:
/*程序 10 — 24 — 1,用指针逆置数组的元素 */
main( )
{static int a[ 10] ={0,1,2,3,4,5,6,7,8,9};
int *i,*j;
int t;
i=a; j=a+9;
for(; i<j; i++,j--
{t=*i; *i=*j; *j=t; }
printf( ″逆置后数组,″);
for( i=a; i<a+10; i++
printf( ″%3d″,*i);
}
运行结果,9 8 7 6 5 4 3 2 1 0
第 10章 指针类型方法二,用函数处理,形参,实参均用指针。
/*程序 10 — 24 — 2,用函数逆置数组的元素 */
void change( x,n) /*逆置数组的元素的函数 */
int *x,n;
{int *i,*j;
int t;
i=x; j=x+n-1;
for(; i<j; i++,j— —
{t=*i; *i=*j; *j=t; }
return;
}
main()
{static int a[ 10] ={0,1,2,3,4,5,6,7,8,9};
int *p;
p=a;
change( p,10);
printf( ″逆置后数组,″);
for( p=a; p<a+10; p++
printf( ″%3d″,*p);
}
第 10章 指针类型例 10 — 25 用矩形法求下列函数的定积分,积分区间由键盘输入 。
(1) f1( x) =1+x2
(2) f2( x) =1+x2+x4
(3) f3( x) =
定义一个求积分的通用函数 inte,形式参数为 fun,a,b,
fun为指向函数的指针,对应被积函数,a,b对应积分区间 。
主函数中三次调用求积分的通用函数 inte,每次调用时分别以 f1,f2,f3作为实参函数名,并给出实际的积分区间,
求出相应的定积分 。
)cos (1
)s in(
x
x
第 10章 指针类型
/*程序 10 — 25,求定积分 */
# define N 100
main(
{float f1( ),f2( ),f3( );
float inte( );
float a,b;
float s1,s2,s3;
printf( ″请输入求积区间,″
scanf( ″%f,%f″,&a,&b);
s1=inte( f1,a,b);
s2=inte( f2,a,b);
s3=inte( f3,a,b);
printf( ″积分 1=%7.2f,积分 2=%7.2f,积分 3=%7.2f\n″,s1,s2,s3);
}
第 10章 指针类型
float f1( x) /*被积函数一 */
float x;
{float f;
f=1+x*x;
return( f);
}
float f2( x) /*被积函数二 */
float x;
{float f;
f=1+x*x+x*x*x*x;
return( f);
}
第 10章 指针类型
float f3( x) /*被积函数三 */
float x;
{float f;
f=sin( x) /( 1+cos( x));
return( f);
}
float inte( fun,a,b) /*矩形法求积分的通用函数 */
float ( *fun) ( ),a,b;
{int i;
float h,s;
h=( b-a) /N;
for( s=0,i=1; i<=N; i++)
s+=h*( *fun) ( a+i*h) ;
return( s) ;
}
第 10章 指针类型例 10 — 26 用链表实现若干学生的成绩处理 。
每个学生包括姓名与 8门课程成绩,求出学生人数,
每个学生的总成绩,平均成绩 。
学生数据用一链表存放,总成绩,平均成绩亦存放于链表,输入学生姓名为,x”,数据结束 。 建立成绩链表的同时求出学生人数 ( 链表结点数 ),每个学生的总成绩,平均成绩 。 建立成绩链表,输出成绩链表分别用函数完成 。
第 10章 指针类型
/*程序 10 — 26,用链表实现学生成绩处理 */
struct linkllist
{char *name;
float cj[ 8];
float tcj,avcj;
struct linklist *next;
};
int num;
main(
{struct linklist *creathead( );
void print( );
struct linklist *h;
h=creathead( ); /*调用 creathead( )函数建立成绩链表,返回头指针 */
printf( ″%d个学生成绩数据,\n″,num);
print( h); /*调用 print( )函数输出成绩链表 */
}
第 10章 指针类型
struct linklist *creathead( ) /*头插法建立成绩链表 */
{struct linklist *head,*p;
int i;
num=1;
p=( struct linklist *) malloc( sizeof( struct linklist));
scanf( ″%s″,p->name);
p->tcj=0;
for( i=0; i<8; i++
{scanf( ″%f″,&p->cj[ i]);
p->tcj+=p->cj[ i];
}
p->avcj=p->tcj/8;
head=NULL;
while ( strcmp( p->name,″x″)! =0
{p->next=head;
head=p;
num++;
第 10章 指针类型
p=( struct linklist *) malloc( sizeof( struct linklist));
scanf( ″%s″,p->name);
if( p->name! =x
for( i=0; i<8; i++
{scanf( ″%f″,&p->cj[ i]);
p->tcj+=p->cj[ i];
}
p->avcj=p->tcj/8;
}
return( head);
}
void print( struct linklist *head) /*输出成绩链表 */
{struct linklist *p;
第 10章 指针类型
p=head;
while( p! =NULL
{printf( ″%s″,p->name);
for( i=0; i<8; i++
printf( ″%8.2f″,p->cj[ i]); printf( ″%8.2f,%8.2f″,p-
>tcj,p->avcj),
p=p->next;
}
}
10.1 指针与指针变量
10.2 指针与数组
10.3 指针与字符串
10.4 指针与结构体
10.5 指针与链表
10.6 指针与函数
10.7 指针作基类型
10.8 程序设计举例第 10章 指针类型
10.1 指针与指针变量
10.1.1 指针变量的定义
1.
形式:
类型标识符 *变量标识符;
定义存放指定类型数据地址的指针变量。
第 10章 指针类型例如:
(1) int *p1,*p2,*p3;
定义指向整型数据的指针变量 p1,p2,p3。
(2) float *q1,*q2,*q3;
定义指向实型数据的指针变量 q1,q2,q3。
(3) char * r1,*r2,*r3;
定义指向字符型数据的指针变量 r1,r2,r3。
第 10章 指针类型
(4) struct date
{int year;
int month;
int day;
} *t1,*t2,*t3;
定义指向 struct date类型数据的指针变量 t1,t2,t3。
第 10章 指针类型说明:
(1) 指针变量可与普通变量混合定义,指针变量的定义与普通变量的定义用变量名前加,*” 区分 。 例如:
int i,*p; /*定义整型变量 i,指针变量 p*/
(2) 空指针,NULL”是一特殊的指针数据,表示空地址,相当于整型数据中的 0,字符数据中的空格 。
第 10章 指针类型
(3) 指针变量只能用于存放指定类型数据的地址 。
如以上定义的一些指针变量,p1,p2,p3只能存放整型数据的地址,q1,q2,q3只能存放实型数据的地址,
r1,r2,r3只能存放字符型数据的地址,t1,t2,t3只能存放 struct date型数据的地址 。
第 10章 指针类型
(4) 指针变量不能直接赋以具体地址值,不能从键盘输入值 。 指针变量通过间接赋以相关数据的地址,
或调用存储空间分配函数得到值 。 例如:
int i,j,k;
int*p1=&i,* p2=&j,* p3=&k; /* p1得到 i的地址,
p2得到 j的地址,p3得到 k的地址 */
第 10章 指针类型
(5) 指针类型隐含在指针变量的定义中 。 例如,在上面定义指针变量 p1,p2,p3时,实际上在背后隐含定义了一指向整型数据的指针类型,从形式上可以将 int*看成是指向整型数据的指针类型 。
与数组定义一样,通过 typedef可以将指针类型与指针变量分离 。 例如,上面定义的指针变量 p1,p2,p3,
可以改用如下形式:
typedef int *INTPOINT;
INTPOINT p1,p2,p3;
第 10章 指针类型
(6) 指针变量本身占有 2字节的存储空间 。
(7)“void *”指针类型定义的指针变量,不指向哪一种特定类型的数据,在实际使用时通过强制类型转换成指向特定类型的数据 。
第 10章 指针类型
2,
例如,对指针变量 p1,p2,p3,假定已有值,*p1、
*p2,*p3代表指针变量 p1,p2,p3所指向的数据,也就是 p1,p2,p3的值对应的存储单元里存放的数据,
称为指针变量所指向的变量,简称指针指向变量 。
如果指针变量 p1,p2,p3分别存放整型变量 i,j、
k的地址,则 p1指向 i,p2指向 j,p3指向 k。 图 10 - 1来直观反应指针变量与指针指向变量的关系 。
第 10章 指针类型图 10 - 1
第 10章 指针类型指针指向变量 *p1,*p2,*p3相当于整型变量 i,j,k。 例如:
int *p=&i;
scanf(,%d”,p) ; /*等价于 scanf(,%d”,&i) */
printf(,%d”,*p); /*等价于 printf(,%d”,i) */
第 10章 指针类型
10.1.2 指针的运算
1,引用运算
1) 取地址运算 ( &)
取地址运算,&”,对指针变量进行取地址运算,
可以得到指针变量本身的地址 。
第 10章 指针类型
2) 取内容运算 ( *)
取内容运算,*”,前称指针运算,用于获取地址数据对应存储单元的内容 。 取内容运算的优先级与取地址运算优先级相同,也为第 2级,结合性亦为右结合 。
对指针变量,进行取内容运算可以得到指针变量所指向的数据 。
取内容运算与取地址运算实质上是一对互逆运算 。
例如:
int a,*p=&a;
*( &a)就是 a,&( *p)就是 p; p指向 a,*p与 a等价。
第 10章 指针类型
2,算术运算
1) 加减运算加减运算常用于数组的处理 。 对指向一般数据的指针,
加减运算无实际意义 。 例如:
int a[ 10],*p=a,*x;
x=p+3; /*实际上是 p加上 3*2个字节赋给 x,x指向数组的第三个分量 */
对于不同基类型的指针,指针变量,加上,或,减去,
一个整数 n所移动的字节数是不同的 。 例如:
float a[ 10],*p=a,*x;
p=p+3; /*实际上是 p加上 3*4个字节赋给 x,x依然指向数组的第三个分量 */
第 10章 指针类型
2)
指针变量自增,自减运算具有上述运算的特点,但有前置后置,先用后用的考虑,务请小心 。 例如:
int a[ 10],*p=a,*x;
x=p++; /* x指向数组的第一个分量,p指向数组的第二个分量 */
x=++p; /* x,p均指向数组的第二个分量 */
*p++相当于 *( p+ +) 。 *( p++) 与 ( *p) ++ 含义不同,前者表示地址自增,后者表示当前所指向的数据自增 。
第 10章 指针类型
3)
指针相减得到两指针之间数据的个数,一般用于数组处理。
第 10章 指针类型
3,关系运算两指针的关系运算表示两指针的先后位置关系,
一般用于数组处理 。 除空指针外,不能进行指针与一般数值的关系运算 。
第 10章 指针类型
10.1.3 利用指针处理简单数据通过指向简单数据的指针变量来处理数据的步骤是:
(1) 定义以相应简单数据类型为基类型的指针变量 。
。
(2) 在指针变量与要处理的数据之间建立关联 。
只需将相应数据的地址赋给指针变量 。
(3) 使用指针所指向的变量来完成数据处理。
第 10章 指针类型例如,要利用指针处理 float数据 x:
(1) float *p;
(2) p=&x;
(3) *p即 x
第 10章 指针类型例 10-1 利用指针,求两个整数的和 。
/*程序 10 - 1,利用指针,求两个整数的和 */
main( )
{int i,j;
int *p,*q; /*定义指针变量 */
int sum;
p=&i; q=&j; /*建立关联 */
scanf( ″%d,%d″,p,q) ;
sum=*p+*q; /*使用 */
printf( ″%d,%d\n″,*p,*q) ;
printf(″和 =%d\n″,sum) ;
}
第 10章 指针类型例 10 – 2 指针运算示例 。
/*程序 10 - 2,指针运算 */
main( )
{char c=′a′;
char *p=&c;
int a1,a2;
int *p1,*p2;
a1=100; p1=&a1;
a2= (*p1)/3+7; p2=&a2;
printf( ″a1=%d,a2=%d,*p1=%d,*p2=%d\n″,
a1,a2,*p1,*p2) ;
a1=( *p1) ++; a2=*p2++;
第 10章 指针类型
printf( ″a1=%d,a2=%d,*p1=%d,*p2=%d\n″,
a1,a2,*p1,*p2) ;
printf( ″%c,%c\n″,c,*p) ;
}
运行结果:
a1=100,a2=40,*p1=100,*p2=40
a1=101,a2=40,*p1=101,*p2=随机值 ( p2指向
a2后一个数据单元 )
a,a
第 10章 指针类型
10.1.4 指针作函数参数例 10 – 3 将两个整数按从小到大的顺序输出。
先定义一个函数,用指针变量作参数,实现两个数的交换,然后在主函数中调用它,完成两个整数从小到大的顺序输出 。
/*程序 10 - 3,将两个整数顺序输出 */
void exchang( p1,p2) /*交换两个数 */
int *p1,*p2;
{int p;
p=*p1; *p1=*p2; *p2=p; /*结果通过 *p1,*p2带回 */
}
第 10章 指针类型
main(
{int a,b;
int *r,*s;
scanf( ″%d,%d″,&a,&b);
r=&a; s=&b;
if( a>b) exchang( r,s);
printf( ″%d,%d\n″,a,b);
}
输入数据,9,4
运行结果,4,9
第 10章 指针类型两点说明:
(1) 若在函数中交换指针变量的值,实参 r,s并不改变,指针参数亦是传值 。
int *p;
p=p1; p1=p2; p2=p;
不要希望如此完成处理 。
(2) 函数中交换值时不能使用无值的指针变量作临时变量 。 例如:
int *p;
*p=*p1; *p1=*p2; *p2=*p;
p无值,*p无意义。
第 10章 指针类型
10.2 指针与数组
10.2.1 指向一维数组的指针变量可以利用指向一维数组的指针变量,完成数组数据的操作处理,具体步骤如下:
(1) 定义与数组相同基类型的指针变量 。
即定义指向数组的指针变量 。
(2) 在指针变量与要处理的数组 ( 元素 ) 之间建立关联 。 只需将相应数组的首地址赋给指针变量 。
(3) 使用指针所指向的变量来完成数组元素(数组)
的操作处理。
第 10章 指针类型例如,要利用指针处理整型数组 a:
(1) int *p; /*定义指针变量 */
(2) p=a; 或 p=&a[ 0] ; /*建立关联 */
p+i是下标为 i 的数组的元素地址 。
(3) *p即 a[ 0],*( p+i) 即 a[ i] 。
*p++是 p当前指向的数组元素的下一个元素 。
如此得到处理数组的指针法。
第 10章 指针类型与指针法相类似的是处理数组的位移法,或称首地址法 。 通过数组的首地址计算出下标为 i的数组的元素地址 ( a+i),*( a+i) 即 a[ i] 。
指针法中 p是变量,用来存放数组元素的地址 。
位移法中 a是常量,代表数组的首地址 。
第 10章 指针类型例 10-4 分别用下标法,指针法,位移法输入,输出数组元素 。
方法一:
/*程序 10 — 4 — 1,下标法实现数组的输入,输出 */
main(
{ int a[ 10];
int i;
for ( i=0; i<10; i++
scanf ( ″%d″,&a[ i]);
printf( ″\n″);
for ( i=0; i<10; i++
printf( ″%3d″,a[ i]);
}
第 10章 指针类型方法二:
/*程序 10— 4 — 2,指针法实现数组的输入,输出 */
main( )
{ int a[ 10] ;
int i,*p; /*定义指针变量 */
p=a; /*建立关联 */
for ( i=0; i<10; i++)
scanf ( ″%d″,p++) ;
printf( ″\n″) ;
for ( p=a; p<a+10; p++) /*使用 */
printf( ″%3d″,*p) ;
}
第 10章 指针类型方法三:
/*程序 10— 4 — 3,位移法实现数组的输入,输出 */
main( )
{ int a[ 10] ;
int i,;
for ( i=0; i<10; i++)
scanf ( ″%d″,&a[ i]) ;
printf( ″\n″) ;
for ( i=0; i<10; i++)
printf( ″%3d″,*( a+i)) ;
}
第 10章 指针类型
10.2.2 数组作函数参数例 10 — 5 求 n个整数的最大值,最小值 。
求 n个数的最大值的一般方法我们已非常熟悉,在这里主要考察引入指针处理数组后,数组作函数参数的应用 。
假定不超过 100个数,数据输入输出在主函数中完成,求最大值,最小值用一个函数完成,n个数用参数传递,最大值,最小值使用全局变量 。
方法一,形参用指针,实参用数组名 。
程序如下:
第 10章 指针类型
/*程序 10 — 5 — 1,求 n个整数的最大值,最小值 */
# define L 100
int max,min;
main(
{int a[ L];
int n,i;
void max -min( );
printf( ″″);
scanf( ″%d″,&n);
printf( ″请输入 %d个数,″,n);
for( i=0; i<n; i++
scanf( ″%d″,&a[ i]);
max -min( a,n); /*调用函数 */
printf( ″最大值 =%4d,最小值 =%4d\n″,max,min);
}
第 10章 指针类型
void max -min( p,x) /*求最大值,最小值函数 */
int *p,x;
{int i;
max=min=*p; /*最大值,最小值初始化为第一个数据 */
for( i=1; i<x; i++) /*将第 i个数据分别与最大值,最小值比较 */
{if( *( p+i) >max) max=*( p+i);
if( *( p+i) <min) min=*( p+i);
}
}
运行结果:
请输入数的个数 n,6
请输入 6个数,32 54 7 88 13 49
最大值 = 88,最小值 =7
第 10章 指针类型方法二,形参用数组,实参用指针。
程序如下:
/*程序 10 — 5 — 2,求 n个整数的最大值,最小值 */
# define L 100
int max,min;
main(
{int a[ L],*p;
int n,i;
void max -min( );
printf( ″请输入数的个数 n,″);
scanf( ″%d″,&n);
printf( ″请输入 %d个数,″,n);
for( i=0; i<n; i++
scanf( ″%d″,&a[ i]);
第 10章 指针类型
p=a;
max -min( p,n); /*调用函数 */
printf( ″最大值 =%4d,最小值 =%4d\n″,max,min);
}
void max -min( b,x) /*求最大值,最小值函数 */
int b[],x;
{int i;
max=min=b[ 0]; /*最大值,最小值初始化为第一个数据 */
for( i=1; i<x; i++) /*将第 i个数据分别与最大值,最小值比较 */
{if( b[ i] >max) max=b[ i];
if( b[ i] <min) min=b[ i];
}
}
第 10章 指针类型方法三,形参用指针,实参用指针。
程序如下:
/*程序 10 — 5 — 3,求 n个整数的最大值,最小值 */
# define L 100
int max,min;
main(
{int a[ L],*p;
int n,i;
void max -min( );
printf( ″请输入数的个数 n,″);
scanf( ″%d″,&n);
printf( ″请输入 %d个数,″,n);
for( i=0; i<n; i++
scanf( ″%d″,&a[ i]);
第 10章 指针类型
p=a;
max-min( p,n); /*调用函数 */
printf( ″最大值 =%4d,最小值 =%4d\n″,max,min);
}
void max -min( q,x) /*求最大值,最小值函数 */
int *q,x;
{int i;
max=min=*q; /*最大值,最小值初始化为第一个数据 */
for( i=1; i<x; i++) /*将第 i个数据分别与最大值 \最小值比较 */
{if( *( q+i) >max) max=*( q+i);
if( *( q+i) <min) min=*( q+i);
}
}
第 10章 指针类型
10.2.3 指向二维数组的指针变量
1,二维数组的指针对于二维数组,相当于一张二维表格,存储按行按列存放 。
二维数组具有首地址,行地址,元素地址等相关指针 。 数组名代表首地址,称为二维数组的指针;行地址是二维数组中一行的首地址,二维数组中一行相当于一个一维数组;元素地址是二维数组的具体分量地址 。
第 10章 指针类型例如,对二维整型数组 a[ 4] [ 5],相当于下面的二维数据表:
第 0列 第 1列 第 2列 第 3列 第 4列第 0行,a[ 0] [ 0],a[ 0] [ 1],a[ 0] [ 2],a[ 0] [ 3],a[ 0] [ 4],
相当一维数组 a[ 0]
第 1行,a[ 1] [ 0],a[ 1] [ 1],a[ 1] [ 2],a[ 1] [ 3],a[ 1] [ 4],
相当一维数组 a[ 1]
第 2行,a[ 2] [ 0],a[ 2] [ 1],a[ 2] [ 2],a[ 2] [ 3],a[ 2] [ 4],
相当一维数组 a[ 2]
第 3行,a[ 3] [ 0],a[ 3] [ 1],a[ 3] [ 2],a[ 3] [ 3],a[ 3] [ 4],
相当一维数组 a[ 3]
第 10章 指针类型
(1) a代表整个二维数组的首地址,也就是第 0 行的首地址。也可用 a[ 0],&a[ 0][ 0]表示。
(2) a[ i] 代表第 i行的首地址 。
整个二维数组也相当于一个一维数组 a[ 0],a[ 1],
a[ 2],a[ 3],基于一维数组的处理方法,第 i行的首地址还可用 *( a+i),a+i,&a[ i],&a[ i] [ 0] 表示 。
请注意 *a表示 0 行首地址,而非 a[ 0] [ 0] 。
第 10章 指针类型
(3) a[ i] +j代表 a[ i][ j]的地址。 a[ i][ j]的地址还可用 *( a+i) +j表示。
请注意 a[ i][ j]的地址不能用( a+i) +j表示,因为此时实际表示的是第( i+j)行的地址。
a[ i] [ j] 相对 a[ 0] [ 0] 的绝对地址可用 a[ 0]
+i*5+j计算 。 如果每行有 m个元素,a[ i] [ j] 相对 a
[ 0] [ 0] 的绝对地址可用 a[ 0] +i*m+j计算 。
当然 &a[ i] [ j] 是我们早就知道的 a[ i] [ j] 的地址 。
第 10章 指针类型
(4) 基于上面的分析,引入指针后,二维数组的分量 a[ i] [ j] 可表示成:
a[ i] [ j] *( a[ i] +j) *( *( a+i) +j) *( a
[ 0] +i*m+j)
相应得到处理二维数组的多种形式位移法 。
第 10章 指针类型例 10 — 6 输出一指定数组。
程序如下:
/*程序 10— 6,输出一指定数组 */
main( )
{static int a[ 4] [ 5] ={1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20};
int i,j;
for( i=0; i<4; i++)
{for( j=0; i<5; j++)
第 10章 指针类型
printf( ″%6d″,*( a[ 0] +i*5+j)) ; /*或 printf( ″%6d″,*
( *( a+i) +j)) ; */
printf( ″\n″) ;
}
}
运行结果,1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
第 10章 指针类型
2,指向二维数组元素的指针变量利用指向二维数组元素的指针变量,可以完成二维数组数据的操作处理,这也就是处理二维数组的指针法 。
(1) 定义与数组相同基类型的指针变量 。
(2) 在指针变量与要处理的数组 ( 元素 ) 之间建立关联 。
(3) 使用指针所指向的变量来完成数组元素(数组)
的操作处理。
第 10章 指针类型例 10 — 7 输出同例 10 — 6指定数组。
/*程序 10 — 7,输出指定数组 */
main(
{static int a[ 4][ 5] ={1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20};
int *p;
for( p=a[ 0]; p<a[ 0] +20; p++
{if(( p-a[ 0]) %5= =0) printf( ″\n″); /*每行输出 5个元素 */
printf( ″%6d″,*p);
}
}
运行结果,1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
第 10章 指针类型
3.
一个二维数组相当于多个一维数组 。 通过指向整个一维数组的指针变量,也可以完成二维数组数据的操作处理,
这也是处理二维数组的指针法 。 例如:
int ( *p) [ 5] ;
定义了一个指向具有 5个元素的一维数组的指针变量 p,p
的增值以指向的一维数组为单位,一维数组的 5个元素可用 ( *p) [ 0],( *p) [ 1],( *p) [ 2],( *p)
[ 3],( *p) [ 4] 表示 。
a是上面定义的数组,如果 p=a,p+i指向 a数组的第 i行,
*( p+i) +j指向 a数组的第 i行第 j列元素 。
第 10章 指针类型例 10 — 8 输出上例二维数组中某行某列元素的值。
程序如下:
/*程序 10 — 8,输出上例二维数组中某行某列元素的值 */
main(
{static int a[ 4][ 5] ={1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20};
int ( *p)[ 5],i,j;
p=a;
scanf( ″%d,%d″,&i,&j);
printf( ″a[ %d][ %d] =%d″,i,j,*( *( p+i) +j));
输入数据,3,4
运行结果,a[ 3][ 4] =12
第 10章 指针类型
10.3 指针与字符串
10.3.1 字符串的指针表示例 10 — 9 字符串的指针表示。
main(
{char *s= ″computer″;
printf ( ″%s\n″,s);
}
运行结果,computer
第 10章 指针类型
(1) char *s= ″computer″;
等价于:
char *s;
s= ″computer″;
(2) printf ( ″%s\n″,s) ;
自动转换成:
for( s=″computer″; *s! =′\0′; s++)
printf ( ″%c″,*s) ;
(3) 可以用下标形式引用字符串中的字符 。 如所举例中,s[ 0] 表示 ′c′,s[ 5] 表示 ′t′。
(4) 用指针表示字符串比用数组表示字符串更常用。
第 10章 指针类型例 10 — 10 字符串的复制。
方法一,字符串用字符数组表示,字符数组用位移法处理。
程序如下:
/*程序 10 — 10 — 1,字符串的复制 */
main(
{char ss[ ] =″C LANGUAGE″,ts[ 20];
int i;
for( i=0; *( ss+i)! =′\0′; i++
*( ts+i) =*( ss+i);
*( ts+i) =′\0′;
printf( ″原字符串 ss=%s\n″,ss);
printf( ″目标字符串 ts=%s\n″,ts);
}
运行结果:
原字符串 ss=C LANGUAGE
目标字符串 ts=C LANGUAGE
第 10章 指针类型方法二,字符串用字符指针表示。
程序如下:
/*程序 10 — 10 — 2,字符串的复制 */
main(
{char ss[ ] =″C LANGUAGE″,ts[ 20];
char *ps,*pt;
ps=ss; pt=ts;
for(; *ps=′\0′; ps++,pt++
*pt=*ps;
*pt=′\0′;
printf( ″原字符串 ss=%s\n″,ps);
printf( ″目标字符串 ts=%s\n″,pt);
}
第 10章 指针类型
10.3.2 字符串(指针)作函数参数例 10 — 11 求字符串的长度函数。
int len( str) /*求字符串的长度函数 */
char str[ ];
{char *p=str;
int l=0;
while( *p! =′\0′
l++;
return( l);
}
第 10章 指针类型或直接用字符指针作函数参数,函数如下:
int len( ps) /*求字符串的长度函数 */
char *ps;
{int l=0;
while( *ps! =′\0′
l++;
return( l);
}
第 10章 指针类型例 10 — 12 用函数实现字符串的复制。
void copy( from,to) /*字符串复制函数 */
char *from,*to;
{for(; *from! =′\0′; from++,to++
*to=*from;
*to=′\0′;
}
第 10章 指针类型调用时对应实参可以是字符数组,也可以是字符类型的指针 。
for语句可改写成:
for( ; ( *to=*from) ! =′\0′; from++,to++) ;
或 for( ; ( *to++=*from++) ! =′\0′; ) ;
或 for( ; ( *to++=*from++) ; ) ;
也可改用 while语句:
while( *from! =′\0′)
{to++=from++; }
或
while( ( *to++=*from++) ! =′\0′) ;
或
while( *to++=*from++);
第 10章 指针类型
10.3.3 字符指针变量和字符数组的区别
(1) 字符数组由若干个元素组成,每个元素中存放字符串的一个字符,而字符指针变量中存放的是字符串的首地址 。
(2) 初始化方式不同 。 对字符数组初始化要用 static存储类别,在编译时进行 。 而对字符指针变量初始化不必加 static,在实际执行时进行 。
(3) 赋值方式不同 。 对字符数组不能整体赋值,只能转化成份量,对单个元素进行 。 而字符指针变量赋值可整体进行 。 例如:
char s[ 10] ;
s=″C++″; /*错,s是常量,怎能被赋值 */
第 10章 指针类型
(4) 在定义一个字符数组时,编译时即已分配内存单元,有确定的地址 。 而定义一个字符指针变量时,
给指针变量分配内存单元,但该指针变量具体指向哪个字符串,并不知道,即指针变量存放的地址不确定 。
例如:
char s[ 10] ;
char *p;
scanf( ″%s″,s) ; /*正确 */
scanf( ″%s″,p); /*非常危险,p的值动态 */
第 10章 指针类型
(5) 字符指针变量的值可以改变,字符数组名是一个常量,不能改变 。 例如,
main( )
{char *s=″china man″;
s+=6;
printf( ″%s″,s) ;
}
运行结果,man
第 10章 指针类型
10.4 指针与结构体
10.4.1 指向结构体数据的指针变量对使用指针来处理数据读者应有了一些体会,即先定义一以数据或元素类型为基类型的指针变量;其次在定义的指针变量与要处理的数据之间建立关联,
让指针变量指向要处理的数据;然后引用指针指向变量来完成数据的处理 。
第 10章 指针类型例 10 — 13 指向结构体变量的指针变量的应用示例 。
假设有一结构体,包含某人的姓名和年龄,用指向结构体变量的指针变量完成输出处理 。
程序如下:
/*程序 10 — 13,指针应用于结构体 */
main( )
{struct person
{char *name;
int age;
} someone;
第 10章 指针类型
struct person *p; /*定义结构体类型的指针变量 */
someone.name=″张三 ″; /*假定姓名为张三 */
someone.age=20;
p= &someone; /*建立关联,*p即 someone*/
printf( ″姓名 =%s,年龄 =%d\n″,( *p),name,( *p),age);
/*等价于 printf( ″姓名 =%s,年龄 =%d\n″,someone.name,
someone.age); */
}
运行结果,姓名 =张三,年龄 =20
第 10章 指针类型例 10 — 14 对上例,考虑三个人组成的结构体数组的处理。
程序如下:
/*程序 10 — 14,结构体数组的指针处理 */
struct person
{char *name;
int age;
};
第 10章 指针类型
struct person sp[ 3] ={ ″张三 ″,18,″李四 ″,19,″王五 ″,20};
main(
{struct person *p;
printf( ″ 姓名 年龄 \n″);
for ( p=sp,p<sp+3; p++);
printf( ″%6s %6d\n″,p-> name,p-> age );
}
运行结果:
姓名张三 18
李四 19
王五 20
第 10章 指针类型
10.4.2 指向结构体的指针作函数参数例 10 — 15 有 40个学生,每个学生包括学号,姓名及成绩,用函数输出成绩最高的学生数据 。
/*程序 10 — 15指向结构体的指针作函数参数例 */
struct student
{int num;
char *name;
float score;
}
void print( p) /*输出函数 */
struct student *p; /*结构体指针作函数参数 */
第 10章 指针类型
{printf ( ″%d %s %f \n″,p-> num,p-> name,p-> score); }
int maxi( p) /*求最高分学生序号函数 */
struct student *p;
{int i,t;
t=0;
for ( i=1; i<40; i++
if ( *( p+i),score>*( p+t),score
t=i;
return( t);
}
main(
{struct student stu[ 40];
int i,k;
for ( i=1; i<40; i++
scanf ( ″%d %s %f″,&stu[ i],num,stu[ i],name,&stu[ i],score);
k=maxi( stu); /*结构体数组作函数参数 */
print ( &stu[ k]); /*结构体指针作函数参数 */
}
第 10章 指针类型
10.5 指针与链表链表与数组不同,是一种动态地进行存储分配的数据结构,表中各元素在内存中可以不连续存放 。 要查找某一元素,必须先找到上一个元素,根据它提供的下一元素地址才能找到下一个元素 。 如果不知道第一个元素的地址,则整个链表无法访问 。 故一般在链表的开头设立头指针,存放第一个元素的内存地址 。 这样,整个链表就可以从头到尾访问 。 链表的访问只能从头到尾,故是一种顺序访问,而非随机访问 。
在结构体中,若只有一个指针成员域,则得到的链表为单链表;若有多个指针成员域,则得到的链表为多重链表 。
第 10章 指针类型
10.5.1 单链表的数据描述一般形式:
struct
{成员及类型说明;
struct 结构体名 *指针域;
}
第 10章 指针类型在单链表中,指针域只有一个,但非指针域个数不限制 。 为了描述方便,我们将非指针域个数也定为一个,并且按下面给定的类型来描述链表:
struct linklist
{int data;
struct linklist *next;
};
struct linklist *head;
第 10章 指针类型图 10 — 2
第 10章 指针类型
10.5.2 单链表的建立
(1) malloc( size)
在内存的动态存储区申请一个长度为 size字节的连续空间 。
(2) calloc( n,size)
在内存的动态存储区申请 n个长度为 size 字节的连续空间,函数返回值为分配空间的首地址 。 若此函数未被成功执行,函数返回值为 0。
(3) free( p)
释放由指针 p所指向的存储单元,而存储单元的大小是最近一次调用 malloc( ) 或 calloc( ) 函数时所申请的存储空间 。
第 10章 指针类型
1,头插法例 10 — 16 用头插法建立一些正整数链表。
/*程序 10 — 16,用头插法建立一些正整数链表 */
main(
{struct linklist
{int data; /*数据域 */
struct linklist *next; /*指针域 */
};
第 10章 指针类型
struct linklist *head,*p; /*head头指针,p申请单元指针 */
head=NULL; /*空链表 */
p=( struct linklist*) malloc( sizeof( struct linklist));
/*申请空间,并将指针强制转换成所指向的结构体类型 */
scanf( ″%d″,&p->data); /*得到数据 */
while( p->data>0
{p->next=head; /*建立连接 */
head=p; /*用头指针保存当前结点 */
p= ( struct linklist*) malloc( sizeof( struct linklist)); /*为下一个结点申请空间 */
scanf( ″%d″,&p->data); /*得到下一个结点数据 */
}
}
第 10章 指针类型
2,尾插法若将链表的左端固定,链表不断向右延伸,这种建立链表的方法称为尾插法 。 尾插法建立链表时,头指针固定不动,故必须设立一个搜索指针,向链表右边延伸,则整个算法中应设立三个链表指针,即头指针 head,搜索指针 p2,申请单元指针 p1。 尾插法最先得到的是头结点 。
/*程序 10 — 17,用尾插法建立一些正整数链表 */
main(
{struct linklist
{int data;
struct linklist *next;
};
第 10章 指针类型
struct linklist *head,*p1,*p2;
head=NULL; /*空链表 */
p1=( struct linklist*) malloc( sizeof( struct linklist)); /*申请空间 */
scanf( ″%d″,&p1->data); /*得到数据 */
while( p1->data>0
{if( head==NULL) head=p1;
else p2->next=p1; /*建立连接 */
p2=p1; /*保存当前结点 */
p1= ( struct linklist*) malloc( sizeof( struct linklist)); /*为下一个结点申请空间 */
scanf( ″%d″,&p1->data); /*得到下一个结点数据 */
}
p2->next=NULL; /*尾结点指针域为空指针 */
}
第 10章 指针类型
10.5.3 单链表的基本操作注意两点:
(1) 将链表传递进函数,只需将链表头指针传递进函数 。 函数的形参对应实参头指针 。
(2) 对链表的访问用条件循环控制,循环的条件是结点的指针域非空。
第 10章 指针类型
1,输出链表中所有结点
void print( struct linklist *head) /*输出链表所有结点 */
{struct linklist *p;
p=head; /*p指向链表第一个结点 */
while( p! =NULL
{printf( ″%d″,p->data);
p=p->next; /*p指向下一个结点 */
}
}
第 10章 指针类型
2,
只需将上述输出结点改成计数即可。
int count( struct linklist *head) /*统计链表中结点个数 */
{int n=0;
struct linklist *p;
p=head;
while( p! =NULL
{n++;
p=p->next;
}
return( n);
}
第 10章 指针类型
3,插入操作
void ins( struct linklist *head,int i,int x) /*插入结点 */
{int j;
struct linklist *p,*q;
p=head;
j=1;
while(( p! =NULL) &&( j<i)) /*找插入位置 */
{p=p->next;
j++;
}
q=( struct linklist *) malloc( sizeof( struct linklist)); /*产生插入结点 */
q->data=x;
q->next=p->next; /*q插入 p之后 */
p->next=q;
}
第 10章 指针类型
4,删除操作假设删除链表中第 i个结点,先找到第 i-1个结点和第 i
个结点,然后将第 i+1个结点链接在第 i-1个结点后,再释放第 i个结点所占空间,完成删除操作 。
第 10章 指针类型
void del( struct linklist *head,int i) /*删除结点 */
{int j;
struct linklist *p,*q;
p=head;
j=1;
while(( p! =NULL) &&( j<i)) /*找第 i-1个结点和第 i个结点指针 q,p*/
{q=p;
p=p->next;
j++;
}
if( p==NULL) printf( ″找不到结点! ″);
else
{q->next=p->next; /*删除第 i个结点 */
free( p);
}
}
第 10章 指针类型双链表有两个指针域,一个指针指向左边结点,一个指针指向右边结点,用头指针表示开始结点,用尾指针表示结尾结点 。 例如:
struct linklist
{int data;
struct linklist *llink,*rlink;
};
struct linklist *head,*rear;
第 10章 指针类型
10.6 指针与函数
10.6.1 指向函数的指针变量
1.
形式如下:
类型标识符 ( *变量标识符 ) ( ) ;
例如:
int ( *p)( );
第 10章 指针类型说明:
(1) 定义指向函数的指针变量,可以指向一类函数 。
(2) 定义指向函数的指针变量时,括号不能省略 。
形式 int *p( ) 定义的是指针函数头,返回值是指向整型数据的指针值,而不是指向函数的指针变量 。
(3) 对指向函数的指针变量 p,p+i,p++,p--等运算无意义 。
第 10章 指针类型
2.
定义了指向函数的指针变量,就可以在指针变量与特定函数之间建立关联,让指针变量指向特定函数 。
建立关联的方法为:
指针变量 =函数名;
说明:
(1) 指针变量只能指向定义时所指定的一类函数 。
(2) 一个指针变量可以先后指向多个不同的函数。
第 10章 指针类型
3,利用指针实现函数调用指针变量一旦指向某函数,利用指针所指向的变量可以实现函数调用 。
一般形式:
( *指针变量)(实参表);
第 10章 指针类型例 10 — 18 通过指针调用函数。
/*程序 10 — 18,通过指针调用函数,求两个数的最大值 */
float max( x,y) /*求两个数的最大值 */
float x,y;
{float z;
z=( x>=y)? x,y;
return( z);
}
main(
{float ( *p)( ); /*定义指向一类实型函数的指针变量 */
float a,b;
float m;
scanf( ″%d,%d″,&a,&b);
p=max; /*建立关联 */
m=( *p)( a,b); /*利用指针实现函数调用 */
printf( ″a=%d,b=%d,max=%d\n″,a,b,m);
}
第 10章 指针类型
10.6.2 指向函数的指针变量作函数参数例 10 — 19 编制程序,调用一个多功能函数,对于最大值函数参数,求两个数的最大值;对于最小值函数参数,求两个数的最小值 。
/*程序 10 — 19,指向函数的指针变量作函数参数 */
main(
{int max( ),min( ),fun( );
int a,b;
scanf( ″%d,%d″,&a,&b);
printf( ″最大值 =″);
fun( a,b,max);
printf( ″最小值 =″);
fun( a,b,min);
}
第 10章 指针类型
max( x,y) /*最大值函数 */
int x,y;
{if( x>y) return( x);
else return( y);
}
min( x,y) /*最小值函数 */
int x,y;
{if( x<y) return( x);
else return( y);
}
fun( x,y,p) /*多功能函数 */
int x,y;
int ( *p)( ); /*p参数为指向整型函数的指针变量 */
{int result;
result=( *p)( x,y); /*通过指针调用函数 */
printf( ″%d\n″,result);
}
输入数据,28,32
运行结果,最大值 =32 最小值 =28
第 10章 指针类型
10.6.3 指针函数例 10 — 20 设有若干学生,每个学生有 6门课程成绩 。 输入学生序号,输出其全部成绩,用指针函数实现 。
/*程序 10 — 20,指针函数示例 */
float *pointf( p,n) /*计算第 n个学生的成绩开始地址的指针函数 */
float ( *p)[ 6]; /*指向一个学生整个成绩的指针变量 */
int n;
{float *t;
t=*( p+n);
return( t);
}
第 10章 指针类型
main(
{static float score[ ][ 6] ={{70,85,78,80,90,99},{90,
79,63,70,56,68},{49,60,56,50,70,60}};
int m;
float *k;
int i;
printf( ″请输入学生序号,″);
scanf( ″%d″,&m);
k=pointf( score,m); /*调用函数计算序号 m的学生成绩地址 */
printf( ″序号为 %d的学生成绩如下,\n″,*( k+i));
for( i=0; i<4; i++
printf( ″%6.2f\t″,*( k+i));
}
第 10章 指针类型
10.7 指针作基类型
10.7.1 指针数组若数组每个元素的类型为指针类型,则称此数组为指针数组。
指针数组定义的一般形式:
类型标识符 *数组名[常量];
例如:
int *p[ 4];
第 10章 指针类型定义了一个整型指针数组 p,数组元素 p[ 0],p
[ 1],p[ 2],p[ 3] 均为指向整型数据的指针 。
在指针数组的定义中,,*” 和,[ ],形式上均为运算符,但,[ ],的优先级比,*” 的优先级高 。 如上面的例子,p先与 [ 4] 结合,形成 p[ 4],
这显然是数组形式;然后与,*” 结合,形成指针数组 。 注意不要写成 int( *p) [ 4],这是指向整个一维数组的指针变量 。
第 10章 指针类型例 10 — 21 将一些书名按字母顺序输出。
/*程序 10 — 21,将一些书名按字母顺序输出 */
main(
{static char *bookname[ 5] ={ ″pascal″,″clanguage″,″basic″,
″windo
w98″,″office97″;
void sort( );
void output( );
sort( bookname,5);
output( bookname,5);
}
void sort( sname,n) /*排序函数 */
char *sname[ ];
int n;
第 10章 指针类型
{char *tn;
for ( i=0; i<n-1; i++
{t=i;
for( j=i+1; j<n; j++
if( strcmp( sname[ t],sname[ j]) >0) t=i;
if( t! =i
{tn=sname[ i]; sname[ i] =sname[ t]; sname[ t] =tn; }
}
}
void output( oname,n) /*输出函数 */
char *oname[ ];
int n;
{int i;
for ( i=0; i<n-1; i++
printf( ″%10s″,oname[ i]);
}
第 10章 指针类型
10.7.2 多级指针若一个指针的基类型是另外一种指针,则称此为多级指针或指针的指针 。
多级指针的定义形式为:
类型标识符 **变量标识符;
相当于:
( 类型标识符 *) *变量标识符;
所定义的指针变量指向,类型标识符 *” 所描述的指针类型的数据 。 例如:
int *q[ 5],**p=q;
第 10章 指针类型例 10 — 22 将上例中的输出函数改用多级指针实现 。
void output( oname,n) /*用多级指针实现的输出函数 */
char *oname[ ] ;
int n;
{int i;
char **p;
p=oname;
for ( i=0; i<n-1; i++)
{printf( ″%10s″,*p) ;
p++;
}
}
第 10章 指针类型
10.7.3 指针数组作 main函数的形参
main( ) 函数带参数的形式为:
main( argc,argv)
int argc;
char *argv[ ] ;
main( ) 函数对应的实参由程序运行时的命令行参数给定 。 参数 argc为整型,对应命令行中参数个数,
包括执行文件名;参数 argv为字符型的指针数组,每个数组元素是一指向字符数据的指针,对应命令行中各参数的名字 。
第 10章 指针类型使用命令行参数将 C语言程序看作操作系统下的函数,main( ) 函数的参数是在系统与程序之间传递数据 。
命令行的一般形式为:
命令名 参数 1 参数 2 … 参数 n
假定程序运行时发出如下命令:
filename changsha beijing
则 mian( ) 函数中,argc=3,表示有三个命令行参数 ( 文件名也算一个 ),而 argv[ 0] 指向,file1”,argv
[ 1] 指向,changsha”,argv[ 2] 指向,beijing”。
第 10章 指针类型例 10 — 23 编写程序,实现 DOS的 ECHO命令 。
ECHO命令的功能是“参数回显”,不包括
,ECHO”。/*程序 10 — 23,参数回显程序 ECHO.C*/
main( argc,argv
int argc;
char *argv[ ];
{char **p=argv;
while( argc>1)
{p++;
printf( ′%s ",*p);
argc--;
}
}
假定以下面的命令行形式运行程序:
ECHO HU NAN COMPUTER COLLEGE
运行结果,HU NAN COMPUTER COLLEGE
第 10章 指针类型
10.8 程序设计举例有关指针的数据型,用实例说明如下:
int i; /*整型变量 i*/
int *p; /*指向整型数据的指针变量 p*/
int a[ N] ; /*整型数组 a*/
int *p[ N] ; /*指针数组 p,由 N个整型指针组成 */
int ( *p) [ N] ; /*指向整个一维整型数组的指针变量 p*/
int f( ) ; /*整型函数 f,返回值为整型数据 */
int *p( ) ; /*指针函数 p,返回值为指向整型数据的指针 */
int ( *p) ( ) ; /*指向整型函数的指针变量 p*/
int **p; /*二级指针变量 p,
第 10章 指针类型对于指针变量有两种方法进行初始化:
(1) 赋值初始化,注意不能赋具体的物理地址 。
(2) 动态分配,使用存储分配函数 calloc( ),
malloc( ) 申请空间 。
掌握指针的使用,是最终掌握 C语言程序设计关键 。
程序中经常通过在指针变量和要处理数据之间建立关联来处理数据,一般包括三个步骤:
(1) 定义以相关数据类型为基类型的指针变量 。
(2) 在指针变量与要处理的数据之间建立关联 。
(3) 使用指针所指向的变量来完成数据处理 。
有时也可以直接利用指针来处理数据。
第 10章 指针类型例 10 — 24 逆置数组的元素,即将数组的元素反序存放 。
数组的逆置只需将第一个元素与最后一个元素交换,
第二个元素与倒数第二个元素交换,其余依次类推,交换的次数为数组元素个数的一半 。
方法一,不用函数处理 。
设立两个指针,一个指针指向开头元素,一个指针指向末尾元素,交换两指针所指元素,然后第一个指针后移,第二个指针前移 。 重复上述步骤,直到两个指针相遇为止 。
第 10章 指针类型程序如下:
/*程序 10 — 24 — 1,用指针逆置数组的元素 */
main( )
{static int a[ 10] ={0,1,2,3,4,5,6,7,8,9};
int *i,*j;
int t;
i=a; j=a+9;
for(; i<j; i++,j--
{t=*i; *i=*j; *j=t; }
printf( ″逆置后数组,″);
for( i=a; i<a+10; i++
printf( ″%3d″,*i);
}
运行结果,9 8 7 6 5 4 3 2 1 0
第 10章 指针类型方法二,用函数处理,形参,实参均用指针。
/*程序 10 — 24 — 2,用函数逆置数组的元素 */
void change( x,n) /*逆置数组的元素的函数 */
int *x,n;
{int *i,*j;
int t;
i=x; j=x+n-1;
for(; i<j; i++,j— —
{t=*i; *i=*j; *j=t; }
return;
}
main()
{static int a[ 10] ={0,1,2,3,4,5,6,7,8,9};
int *p;
p=a;
change( p,10);
printf( ″逆置后数组,″);
for( p=a; p<a+10; p++
printf( ″%3d″,*p);
}
第 10章 指针类型例 10 — 25 用矩形法求下列函数的定积分,积分区间由键盘输入 。
(1) f1( x) =1+x2
(2) f2( x) =1+x2+x4
(3) f3( x) =
定义一个求积分的通用函数 inte,形式参数为 fun,a,b,
fun为指向函数的指针,对应被积函数,a,b对应积分区间 。
主函数中三次调用求积分的通用函数 inte,每次调用时分别以 f1,f2,f3作为实参函数名,并给出实际的积分区间,
求出相应的定积分 。
)cos (1
)s in(
x
x
第 10章 指针类型
/*程序 10 — 25,求定积分 */
# define N 100
main(
{float f1( ),f2( ),f3( );
float inte( );
float a,b;
float s1,s2,s3;
printf( ″请输入求积区间,″
scanf( ″%f,%f″,&a,&b);
s1=inte( f1,a,b);
s2=inte( f2,a,b);
s3=inte( f3,a,b);
printf( ″积分 1=%7.2f,积分 2=%7.2f,积分 3=%7.2f\n″,s1,s2,s3);
}
第 10章 指针类型
float f1( x) /*被积函数一 */
float x;
{float f;
f=1+x*x;
return( f);
}
float f2( x) /*被积函数二 */
float x;
{float f;
f=1+x*x+x*x*x*x;
return( f);
}
第 10章 指针类型
float f3( x) /*被积函数三 */
float x;
{float f;
f=sin( x) /( 1+cos( x));
return( f);
}
float inte( fun,a,b) /*矩形法求积分的通用函数 */
float ( *fun) ( ),a,b;
{int i;
float h,s;
h=( b-a) /N;
for( s=0,i=1; i<=N; i++)
s+=h*( *fun) ( a+i*h) ;
return( s) ;
}
第 10章 指针类型例 10 — 26 用链表实现若干学生的成绩处理 。
每个学生包括姓名与 8门课程成绩,求出学生人数,
每个学生的总成绩,平均成绩 。
学生数据用一链表存放,总成绩,平均成绩亦存放于链表,输入学生姓名为,x”,数据结束 。 建立成绩链表的同时求出学生人数 ( 链表结点数 ),每个学生的总成绩,平均成绩 。 建立成绩链表,输出成绩链表分别用函数完成 。
第 10章 指针类型
/*程序 10 — 26,用链表实现学生成绩处理 */
struct linkllist
{char *name;
float cj[ 8];
float tcj,avcj;
struct linklist *next;
};
int num;
main(
{struct linklist *creathead( );
void print( );
struct linklist *h;
h=creathead( ); /*调用 creathead( )函数建立成绩链表,返回头指针 */
printf( ″%d个学生成绩数据,\n″,num);
print( h); /*调用 print( )函数输出成绩链表 */
}
第 10章 指针类型
struct linklist *creathead( ) /*头插法建立成绩链表 */
{struct linklist *head,*p;
int i;
num=1;
p=( struct linklist *) malloc( sizeof( struct linklist));
scanf( ″%s″,p->name);
p->tcj=0;
for( i=0; i<8; i++
{scanf( ″%f″,&p->cj[ i]);
p->tcj+=p->cj[ i];
}
p->avcj=p->tcj/8;
head=NULL;
while ( strcmp( p->name,″x″)! =0
{p->next=head;
head=p;
num++;
第 10章 指针类型
p=( struct linklist *) malloc( sizeof( struct linklist));
scanf( ″%s″,p->name);
if( p->name! =x
for( i=0; i<8; i++
{scanf( ″%f″,&p->cj[ i]);
p->tcj+=p->cj[ i];
}
p->avcj=p->tcj/8;
}
return( head);
}
void print( struct linklist *head) /*输出成绩链表 */
{struct linklist *p;
第 10章 指针类型
p=head;
while( p! =NULL
{printf( ″%s″,p->name);
for( i=0; i<8; i++
printf( ″%8.2f″,p->cj[ i]); printf( ″%8.2f,%8.2f″,p-
>tcj,p->avcj),
p=p->next;
}
}