第十章
C程序设计(第三版) http://ccf.tsinghua.edu.cn 2
主要内容
10.1地址和指针的概念
10.2变量的指针和指向变量的 指针变量
10.3数组与指针
10.4字符串与指针
10.5指向函数的指针
10.6返回指针值的函数
10.7指针数组和指向指针的指针
10.8有关指针的数据类型和指针运算的小结
C程序设计(第三版) http://ccf.tsinghua.edu.cn 3
10.1地址和指针的概念内存区的每一个字节有一个编号,这就是,地址,。
如果在程序中定义了一个变量,在对程序进行编译时,
系统就会给这个变量分配内存单元。
1,按变量地址存取变量值的方式称为,直接访问,方式
printf( ″%d ″,i);
scanf( ″%d ″,&i);
k=i+j;
C程序设计(第三版) http://ccf.tsinghua.edu.cn 4
C程序设计(第三版) http://ccf.tsinghua.edu.cn 5
2,另一种存取变量值的方式称为,间接访问,的方式。
即,将变量i的地址存放在另一个变量中。
在C语言中,指针是一种特殊的变量,它是存放地址的。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 6
一个变量的地址称为该变量的,指针,。
例如,地址 2000是变量i的指针。如果有一个变量专门用来存放另一变量的地址(即指针),则它称为,指针变量,。上述的 i_pointer就是一个指针变量。
指针和指针变量的定义:
C程序设计(第三版) http://ccf.tsinghua.edu.cn 7
10.2 变量的指针和指向变量的指针变量
10.2,1 定义一个指针变量定义指针变量的一般形式为基类型 *指针变量名;
C程序设计(第三版) http://ccf.tsinghua.edu.cn 8
下面都是合法的定义:
float *pointer_3;
char *pointer_4;
可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向一个该变量。
例如:
pointer_1=&i;
pointer_2=&j;
C程序设计(第三版) http://ccf.tsinghua.edu.cn 9
在定义指针变量时要注意两点:
(1)指针变量前面的,*”,表示该变量的类型为指针型变量。
例,float *pointer_1;
指针变量名是 pointer_1,而不是 * pointer_1 。
(2) 在定义指针变量时必须指定基类型。
需要特别注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中。下面的赋值是错误的 ∶
float a;
int * pointer_1;
pointer_1=&a;
C程序设计(第三版) http://ccf.tsinghua.edu.cn 10
10.2.2 指针变量的引用注意,指针变量中只能存放地址(指针),
不要将一个整数(或任何其他非地址类型的数据)
赋给一个指针变量。
例 10.1 通过指针变量访问整型变量
#include <stdio.h>
void main ( )
{ int a,b;
int *pointer_1,*pointer_2;
a=100;b=10;
pointer_1=&a; /*把变量a的地址赋给
pointer_1 */
C程序设计(第三版) http://ccf.tsinghua.edu.cn 11
pointer_2=&b; /*把变量b的地址赋给
pointer_2 */
printf( ″%d,%d \n ″,a,b);
printf( ″%d,%d \n ″,*pointer_1,*pointer_2);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 12
对“&”和,*”运算符说明:
如果已执行了语句 pointer_1=&a;
(1)& * pointer_1的含义是什么?
“&”和,*”两个运算符的优先级别相同,但按自右而左方向结合。因此,& * pointer_1与&a相同,即变量 a的地址。
如果有 pointer_2 =& * pointer_1 ;它的作用是将
&a(a的地址)赋给 pointer_2,如果 pointer_2原来指向b,经过重新赋值后它已不再指向b了,而指向了a。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 13
C程序设计(第三版) http://ccf.tsinghua.edu.cn 14
(2) *&a的含义是什么?
先进行&a运算,得a的地址,再进行 *运算。 *
&a和 *pointer_1的作用是一样的,它们都等价于变量a。即 *&a与a等价。
(3) ( *pointer_1)++相当于a++。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 15
例 10,2 输入a和b两个整数,按先大后小的顺序输出
a和b。
#include <stdio.h>
void main()
{ int *p 1,*p 2,*p,a,b;
scanf( ″%d,%d ″,&a,&b);
p 1=&a;p2=&b;
if(a<b)
{p=p1;p1=p2;p2=p;}
printf( ″a =%d,b =%d \n \n ″,a,b);
printf( ″max=%d,min=%d \n ″,*p 1,*p 2);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 16
运行情况如下:
5,9 ↙
a=5,b=9
max=9,min=5
当输入a=5,b=9时,由于a<b,
将p1和p2交换。交换前的情况见图
(a),交换后见图(b)。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 17
C程序设计(第三版) http://ccf.tsinghua.edu.cn 18
10.2,3 指针变量作为函数参数例 10,3 对输入的两个整数按大小顺序输出
#include <stdio.h>
void main()
{ void swap( int *p1,int *p2) ;
int a,b;
int *pointer_1,*pointer_2;
scanf( ″%d,%d ″,&a,&b);
pointer_1 =&a; pointer_2 =&b;
if(a<b= swap( pointer_1,pointer_2 );
printf( ″\n%d,%d \n ″,a,b);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 19
void swap( int *p1,int *p2)
{ int temp;
temp= *p 1;
*p1= *p2;
*p2= temp;

C程序设计(第三版) http://ccf.tsinghua.edu.cn 20
C程序设计(第三版) http://ccf.tsinghua.edu.cn 21
例 10.4 输入a、b、c 3个整数,按大小顺序输出。
#include <stdio.h>
void main()
{ void exchange( int *q 1,int *q 2,int *q 3) ;
int a,b,c,*p1,*p2,*p3;
scanf( ″%d,%d,%d ″,&a,&b,&c);
p1=&a;p2=&b;p3=&c;
exchange (p1,p2,p3);
printf( ″\n%d,%d,%d\n ″,a,b,c);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 22
void exchange( int *q1,int *q2,int *q3)
{ void swap( int *pt1,int *pt2) ;
if( *q1< *q2) swap(q1,q2);
if( *q1< *q3) swap(q1,q3);
if( *q2< *q3= swap(q2,q3);

void swap( int *pt1,int *pt2)
{ int temp;
temp= *pt1;
*pt1= *pt2;
*pt2= temp;

C程序设计(第三版) http://ccf.tsinghua.edu.cn 23
10.3 数组与指针一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓 数组元素的指针就是数组元素的地址 。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 24
定义一个指向数组元素的指针变量的方法,与以前介绍的指向变量的指针变量相同。
例如,int a[10];
(定义a为包含10个整型数据的数组 )
*p;
(定义p为指向整型变量的指针变量 )
应当注意,如果数组为int型,则指针变量的基类型亦应为int型。
10.3.1 指向数组元素的指针
C程序设计(第三版) http://ccf.tsinghua.edu.cn 25
对该指针变量赋值:
p=&a[0];
把a[0]元素的地址赋给指针变量p。也就是使p
指向a数组的第0号元素,如图:
C程序设计(第三版) http://ccf.tsinghua.edu.cn 26
10.3,2通过指针引用数组元素引用一个数组元素,可以用:
(1) 下标法,如a[i]形式;
(2) 指针法,如 *(a+i)或 *(p+i) 。
其中a是数组名,p是指向数组元素的指针变量,其初值p=a。
例 10.5 输出数组中的全部元素。
假设有一个a数组,整型,有10个元素。要输出各元素的值有三种方法:
C程序设计(第三版) http://ccf.tsinghua.edu.cn 27
(1)下标法。
#include <stdio.h>
void 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( ″%d ″,a[i]);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 28
(2) 通过数组名计算数组元素地址,找出元素的值。
#include <stdio.h>
void 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( ″%d ″,*(a+i));

C程序设计(第三版) http://ccf.tsinghua.edu.cn 29
(3) 用指针变量指向数组元素。
#include <stdio.h>
void main()
{ int a[10];
int *p,i;
for(i=0;i<10;i++)
scanf( ″%d ″,&a[i]);
printf( ″\n ″);
for(p=a;p<(a+10);p++)
printf( ″%d ″,*p);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 30
例 10.6 通过指针变量输出a数组的10个元素。
#include <stdio.h>
void 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++,p++ )
printf( ″%d ″,*p);

程序运行情况:
1 2 3 4 5 6 7 8 9 0↙
22153 234 0 0 30036 25202 11631 8259 8237 28483
显然输出的数值并不是a数组中各元素的值
C程序设计(第三版) http://ccf.tsinghua.edu.cn 31
#include <stdio.h>
void main()
{ int *p,i,a[10];
p=a;
for(i=0;i<10;i++)
scanf( ″%d ″,p++);
printg( ″\n ″);
p=a;
for(i=0;i<10;i++,p++ )
printf( ″%d ″,*p);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 32
10.3,3 用数组名作函数参数在第 8章 8.7节中介绍过可以用数组名作函数的参数如,void main()
{if( int arr[],int n) ;
int array[10];

f( array,10);


void f (int arr[ ],int n )
{

}
C程序设计(第三版) http://ccf.tsinghua.edu.cn 33
例 10.7 将数组a中n个整数按相反顺序存放。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 34
#include <stdio.h>
void main()
{ void inv( int x[ ],int n) ;
int i,a[10]={3,7,9,11,0,
6,7,5,4,2};
printf( ″The original array:\n ″);
for(i=0;i<10;i++)
printf ( ″%d,″,a[i]);
printf( ″\n ″);
inv (a,10);
printf( ″The array has been in verted,\n ″);
for(i=0;i<10;i++)
printf ( ″%d,″,a[i]);
printf ( ″\n ″);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 35
void inv( int x[ ],int n) /*形参 x是数组名 */
{ int temp,i,j,m=(n-1)/2;
for(i=0;i<=m;i++)

j=n-1-i;
temp=x[i];
x[i]=x[j];
x[j]= temp;

return;

运行情况如下:
The original array:
3,7,9,11,0,6,7,5,4,2
The array has been inverted:
2,4,5,7,6,0,11,9,7,3
C程序设计(第三版) http://ccf.tsinghua.edu.cn 36
C程序设计(第三版) http://ccf.tsinghua.edu.cn 37
#include <stdio.h>
void main()
{ void inv( int *x,int n) ;
int i,a[10]={3,7,9,11,0,
6,7,5,4,2};
printf( ″The original array:\n ″ );
for(i=0;i<10;i++)
printf ( ″%d,″,a[i]);
printf ( ″\n ″);
inv(a,10);
printf ( ″The array has been in verted,\n ″ );
for(i=0;i<10;i++)
printf ( ″%d,″,a[i]);
printf ( ″\n ″); }
对刚才的程序可以作一些改动。将函数 inv中的形参x改成指针变量。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 38
void inv( int *x,int n) /*形参 x为指针变量 */
{ int p,temp,*i,*j,m=(n-1)/2;
i=x;j=x+n-1;p=x+m;
for(;i<=p;i++,j--)
{t emp= *i; *i= *j; *j= temp;}
return;

C程序设计(第三版) http://ccf.tsinghua.edu.cn 39
如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
(1) 形参和实参都用数组名,如:
void main() void f ( int x [ ],int n)
{ int a[10]; {
… …
f (a,10); }

C程序设计(第三版) http://ccf.tsinghua.edu.cn 40
C程序设计(第三版) http://ccf.tsinghua.edu.cn 41
(2) 实参用数组名,形参用指针变量。如:
void main() void f ( int *x,int n)
{ int a[10]; {
… …
f (a,10); }

(3)实参形参都用指针变量。例如:
void main() void f( int *x,int n)
{ int a[10],*p=a; {
┇ ┇
f( p,10); }

C程序设计(第三版) http://ccf.tsinghua.edu.cn 42
C程序设计(第三版) http://ccf.tsinghua.edu.cn 43
(4) 实参为指针变量,形参为数组名。如:
void main() void f( int x[ ],int n)
{int a[10],*p=a; {
┇ ┇
f( p,10); }

C程序设计(第三版) http://ccf.tsinghua.edu.cn 44
#include <stdio.h>
void main()
{ void inv( int *x,int n) ;
int i,arr[10],*p=arr;
printf( ″The original array:\n ″);
for(i=0;i<10;i++,p++)
scanf( ″%d ″,p);
printf( ″\n ″);
p=arr;
inv(p,10); /* 实参为指针变量 */
printf( ″The array has been inverted,\n ″);
for(p=arr;p<arr+10;p++ )
printf( ″%d ″,*p);
printf( ″\n ″);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 45
void inv( int *x,int n)
{ int p,m,temp,*i,*j;
m=(n-1)/2;
i=x;j=x+n-1;p=x+m;
for(;i<=p;i++,j--)
{t emp= *i; *i= *j; *j= temp;}
return;

C程序设计(第三版) http://ccf.tsinghua.edu.cn 46
例 10,9 用选择法对10个整数按由大到小顺序排序。
#include <stdio.h>
void main()
{ void sort( int x[ ],int n) ;
int *p,i,a[ 10];
p=a;
for(i=0;i<10;i++)
scanf( ″%d ″,p++);
p=a;
sort(p,10);
for(p=a,i=0;i<10;i++)
{printf( ″%d ″,*p);p++;}

C程序设计(第三版) http://ccf.tsinghua.edu.cn 47
void sort( int x [ ],int n)
{ int i,j,k,t;
for(i=0;i<n-1;i++)
{k=i;
for(j=i+1;j<n;j++)
if(x[j]>x[k])
k=j;
if(k!=i)
{t=x[i];
x[i]=x[k];
x[k]=t;}


C程序设计(第三版) http://ccf.tsinghua.edu.cn 48
10.3.4 多维数组与指针用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。但在概念上和使用上,多维数组的指针比一维数组的指针要复杂一些。
1,多维数组元素的地址先回顾一下多维数组的性质,可以认为二维数组是“数组的数组”,例,
定义 int a[ 3][ 4] ={{1,3,5,7},{9,11,
13,15},{17,19,21,23} };
则二维数组 a是由 3个一维数组所组成的。设二维数组的首行的首地址为2000,则
C程序设计(第三版) http://ccf.tsinghua.edu.cn 49
表 示 形 式 含义 地 址
a 二维数组名,指向一维数组
a[0],即 0行首地址
2000
a[0],
*(a+0),
*a
0行 0列元素地址 2000
a+1,&a[ 1] 1行首地址 2008
a[ 1],*(a+1) 1行 0列元素 a[1][0]的地址 2008
A[1]+2,
*(a+1)+2,
&a[1][2]
1行 2列元素 a[1][2] 的地址 2012
*(a[1]+2),
*(*(a+1)+2),
a[1][2]
1行 2列元素 a[ 1][ 2]的值 元素值为 13
C程序设计(第三版) http://ccf.tsinghua.edu.cn 50
例 10.1 0 输出二维数组有关的值
#include <stdio.h>
# define FROMAT ″%d,%d\n ″
void main()
{ int a [3][4]={ 1,3,5,7,9,11,13,
15,17,19,21,23};
printf(FORMAT,a,*a);
printf(FORMAT,a [0],*(a+0));
printf(FORMAT,&a [0],&a [0][0]);
printf(FORMAT,a [1],a+1);
printf(FORMAT,&a [1][0],*(a +1) +0);
printf(FORMAT,a[2],*(a+2));
printf(FORMAT,&a[2],a+2);
printf(FORMAT,a[1][0],*( *(a+
1)+0));

C程序设计(第三版) http://ccf.tsinghua.edu.cn 51
某一次运行结果如下:
158,158 (0行首地址和 0行 0列元素地址 )
158,158 (0行 0列元素地址 )
158,158 (0行 0首地址和 0行 0列元素地址 )
166,166 (1行 0列元素地址和 1行首地址 )
166,166 (1行 0列元素地址 )
174,174 (2行 0列元素地址 )
174,174 (2行首地址 )
9,9 (1行 0列元素的值 )
C程序设计(第三版) http://ccf.tsinghua.edu.cn 52
2,指向多维数组元素的指针变量
(1) 指向数组元素的指针变量例 10.11 用指针变量输出二维数组元素的值
#include <stdio.h>
void main()
{ int a [3][4]={ 1,3,5,7,9,11,13,15,17,19,21,23};
int *p;
for(p=a[0];p<a[0]+12;p++)
{if((p-a[0])%4==0)
printf( ″\n ″);
printf( ″%4d ″,*p); }

运行结果如下:
1 3 5 7
9 11 13 15
19 21 23
C程序设计(第三版) http://ccf.tsinghua.edu.cn 53
(2) 指向由m个元素组成的一维数组的指针变量例 10.13 出二维数组任一行任一列元素的值
#include <stdio.h>
void main ( )
{ int a[3][4]={ 1,3,5,7,9,11,
13,15,17,19,21,23};
int ( *p)[4],i,j;
p=a;
scanf( ″ i=%d,j=%d ″,&i,&j);
printf( ″a[%d,%d]=%d\n ″,i,
j,*( *(p+i)+j));

运行情况如下:
i=1,j=2 ↙
a[1,2]=13
C程序设计(第三版) http://ccf.tsinghua.edu.cn 54
3,用指向数组的指针作函数参数例 10.13 有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。这个题目是很简单的。只是为了说明用指向数组的指针作函数参数而举的例子。用函数 average求总平均成绩,用函数
search找出并输出第i个学生的成绩。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 55
#include <sydio.h>
void main()
{ void average( float *p,int n);
void search( float (*p)[ 4],int n);
float score[ 3][ 4] ={{65,67,70,60},{80,
87,90,81},{90,99,100,98}};
average( *score,12); / *求 12个分数的平均分 */
search( score,2); / *求序号为2的学生的成绩 */

C程序设计(第三版) http://ccf.tsinghua.edu.cn 56
void average( float *p,int n)
{ float *p _end;
float sum=0,aver;
p_end=p+n-1;
for(;p<=p _end;p++)
sum= sum+( *p);
aver= sum/n;
printf( ″average=%5.2f\n ″,aver);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 57
void search( float (*p )[4],int n)
/ * p是指向具有 4个元素的一维数组的指针 */
{ int i;
printf( ″the score of No,%d are:\n ″,n);
for(i=0;i<4;i++ )
printf( ″% 5.2f ″,*( *(p+n)+i));

程序运行结果如下:
average=82.25
The score of No.2 are:
90,00 99,00 100,00 98,00
C程序设计(第三版) http://ccf.tsinghua.edu.cn 58
例 10.1 4 在上题基础上,查找有一门以上课程不及格的学生,打印出他们的全部课程的成绩。
#include <stdio.h>
void main()
{ void search( float (*p)[ 4],int n); /*函数声明 */
float score[3][4]={{65,57,70,60},{58,87,
90,81},{90,99,100,98}};
search( score,3);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 59
void search( float (*p)[ 4],int n)
{ int i,j,flag;
for(j=0;j<n;j++ )
{ flag=0;
for(i=0;i<4;i++ )
if( *( *(p+j)+i)<60 ) flag=1;
if(flag==1)
{ printf("No.%d fails,his scores are:\ n",j+1);
for(i=0;i<4;i++)
printf( ″%5,1f ″,*(*(p+j )+i ));
printf( ″\n ″);



程序运行结果如下:
No.1 fails,his scores are:
65,0 57,0 70,0 60,0
No.2 fails,his scores are:
58,0 87,0 90,0 81,0
C程序设计(第三版) http://ccf.tsinghua.edu.cn 60
10.4 字符串与指针
10.4.1字符串的表示形式例 10.1 5 定义一个字符数组,对它初始化,
然后输出该字符串
#include <stdio.h>
void main()
{ char string[]= ″I love China! ″;
printf( ″%s\n ″,string);

(1) 用字符数组存放一个字符串,然后输出该字符串。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 61
(2) 用字符指针指向一个字符串。
可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。
例 10.1 6 定义字符指针
#include <stdio.h>
void main()
{ char string= ″ I love China! ″;
printf( ″%s\n ″,string);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 62
例 10.1 7 将字符串a复制为字符串b。
#include <stdio.h>
void main()
{ char a [ ]= ″I am a boy,″,b [20];
int i;
for(i=0; *(a+i)!= ′\0 ′;i++)
*(b+i)= *(a+i);
*(b+i)= ′\0 ′;
printf( ″string a is,%s\n ″,a);
printf( ″string b is,″);
for(i=0;b[i]!= ′\0 ′;i++)
printf( ″%c ″,b[i]);
printf( ″\n ″);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 63
也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。
例 10.1 8 用指针变量来处理例 10.1 7问题。
#include <stdio.h>
void main()
{ char a [ ] =″I am a boy,″,b [20],*p1,*p2;
int i;
p1=a;p2=b;
for(; *p1!= ′\0 ′; p1++,p2++)
C程序设计(第三版) http://ccf.tsinghua.edu.cn 64
*p2= *p1;
*p2= ′\0 ′;
printf( ″string a is:%s\n ″,a);
printf( ″string b is,″);
for(i=0;b[i]!= ′\0 ′;i++)
printf( ″%c ″,b[i]);
printf( ″\n ″);

程序必须保证使p1和p2同步移动
C程序设计(第三版) http://ccf.tsinghua.edu.cn 65
C程序设计(第三版) http://ccf.tsinghua.edu.cn 66
10.4.2 字符指针作函数参数例 10.19 用函数调用实现字符串的复制
#include <stdio.h>
void main()
{ void copy_string( char from[ ],char to[ ]) ;
char a[ ]=″I am a teacher,″;
char b [ ]=″you are a student,″;
printf(“string a=%s\n string b=%s\n ″,
a,b );
printf(“copy string a to string b:\n,);
copy_string (a,b);
printf("\ nstring a=%s\ nstring b=%s\ n",a,b);

(1) 用字符数组作参数
C程序设计(第三版) http://ccf.tsinghua.edu.cn 67
void copy_string( char from[ ],char to[ ])
{ int i=0;
while( from[i]!= ′\0 ′)
{ to[i]= from[i];i++;}
to[i]= ′\0 ′;

程序运行结果如下,
string a= I am a teacher.
string b = you are a student.
copy string a to string b:
string a = I am a teacher.
stringb= I am a teacher.
C程序设计(第三版) http://ccf.tsinghua.edu.cn 68
(2) 形参用字符指针变量
#include <stdio.h>
void main()
{ void copy_string( char * from,char *to) ;
char *a= ″I am a teacher,″;
char *b= ″you are a student,″;
printf("string a=%s\n string b=%s\n ″,
a,b );
printf("copy string a to string b:\n ");
copy _string(a,b);
printf("\ nstring a=%s\ nstring b=%s\ n",a,b);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 69
void copy_string( char *from,char *to)
{ for(; *from!= ′\0 ′; from++,to++)
*to from;
*to= ′\0 ′;

(3) 对 copy string 函数还可作简化
1、将 copy_string函数改写为
void copy_string ( char *from,char *to)
{ while(( *to= *from)!= ′\0 ′)
{ to++; from++;}

C程序设计(第三版) http://ccf.tsinghua.edu.cn 70
copy_string函数的函数体还可改为

while(( *to++= *from++)!= ′\0 ′);

copy_string函数的函数体还可写成
{
while( *from!= ′\0 ′)
*to++= *from++;
*to= ′\0 ′;

C程序设计(第三版) http://ccf.tsinghua.edu.cn 71
上面的 while语句还可以进一步简化为下面的 while语句:
while( *to++= *from++);
它与下面语句等价:
while(( *to++= *from++)!= ′\0 ′);
将 *from赋给 *to,如果赋值后的 *to值等于 ′\0 ′则循环终止( ′\0 ′已赋给 *to)
函数体中 while语句也可以改用 for语句:
for(;( *to++= *from++)!=0;);

for(; *to++= *from++;);
C程序设计(第三版) http://ccf.tsinghua.edu.cn 72
也可用指针变量,函数 copy_string可写为
void copy_string ( char from[ ],char to[ ])
{ char *p1,*p2;
p1= from;p2=to;
while(( *p2++= *p1++)!= ′\0 ′);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 73
(1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第 1个字符的地址),决不是将字符串放到字符指针变量中。
(2)赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。
char str[14];
str= ″I love China! ″;
而对字符指针变量,可以采用下面方法赋值:
char *a;
a= ″I love China! ″;
10.4.3 对使用字符指针变量和字符数组的讨论字符数组和字符指针变量二者之间的区别:
C程序设计(第三版) http://ccf.tsinghua.edu.cn 74
(3)对字符指针变量赋初值:
char *a= ″I love China! ″;等价于
char *a;
a= ″I love Chian! ″;
而对数组的初始化:
char str[14]={ ″I love China! ″} ;
不能等价于
char str[14];
str[ ]= ″I love China! ″;
C程序设计(第三版) http://ccf.tsinghua.edu.cn 75
(4) 定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,
给指针变量分配内存单元,在其中可以放一个字符变量的地址。
例如,char str[10];
scanf( ″%s ″,str);
C程序设计(第三版) http://ccf.tsinghua.edu.cn 76
(5) 指针变量的值是可以改变的,例如:
例 10.2 0 改变指针变量的值
#include <stdio.h>
void main()
{ char *a= ″I love China! ″;
a=a+7;
printf( ″%s ″,a);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 77
#include <stdio.h>
void main()
{ char *a= ″I love C hina!″;
int i;
printf (,The sixth character is %c\ n",a[ 5] );
for(i=0;a[i]!= ′\0 ′;i++)
printf( ″%c ″,a[i]);

若定义了一个指针变量,并使它指向一个字符串,
就可以用下标形式引用指针变量所指的字符串中的字符。 例如,
C程序设计(第三版) http://ccf.tsinghua.edu.cn 78
10.5 指向函数的指针
10.5.1 用函数指针变量调用函数
用指针变量可以指向一个函数。
函数在编译时被分配给一个入口地址。这个函数的入口地址就称为 函数的指针 。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 79
#include <stdio.h>
void main()
{ int max( int,int);
int a,b,c;
scanf( ″%d,%d ″,&a,&b);
c=max(a,b);
printf( ″a=%d,b=%d,max=%d
″,a,b,c);

int max( int x,int y)
{ int z;
if(x>y)z=x;
else z=y;
return(z);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 80
将 main 函数改写为
#include <stdio.h>
void main()
{ int max( int,int);
int ( *p)();
int a,b,c;
p=max;
scanf( ″%d,%d ″,&a,&b);
c=( *p)(a,b);
printf( ″a=%d,b=%d,max=%
d ″,a,b,c);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 81
10.5.2 用指向函数的指针作函数参数函数指针变量常用的用途之一是把指针作为参数传递到其他函数。指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 82
实参函数名 f1 f2
↓ ↓
void sub( int (*x1)(int),int (*x2)(int,int))
{ int a,b,i,j;
a=( *x1)(i); / *调用f1函数 */
b=( *x2)(i,j); / *调用f2函数 */


C程序设计(第三版) http://ccf.tsinghua.edu.cn 83
例 10.2 3 设一个函数 process,在调用它的时候,每次实现不同的功能。输入a和b两个数,第一次调用
process时找出a和b中大者,第二次找出其中小者,
第三次求a与b之和。
#include <stdio.h>
void main()
{ int max( int,int) ; /* 函数声明 */
int min( int,int) ; /* 函数声明 */
int add( int,int); /* 函数声明 */
void process (int,int,int(*fun)(); /* 函数声明 */
int a,b;
printf( ″enter a and b,″);
scanf( ″%d,%d ″,&a,&b);
C程序设计(第三版) http://ccf.tsinghua.edu.cn 84
printf( ″max= ″);
process(a,b,max);
printf( ″min= ″);
process(a,b,min);
printf( ″sum= ″);
process(a,b,add);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 85
int max( int x,int y) /* 函数定义 */
{ int z;
if(x>y)z=x;
else z=y;
return(z);

int min( int x,int y) /* 函数定义 */
{ int z;
if(x<y)z=x;
else z=y;
return(z);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 86
int add( int x,int y) /* 函数定义 */
{ intz;
z=x+y;
return(z);

void process(int x,int y,int (*fun)(int,int))
{ int result;
result=( *fun)(x,y);
printf( ″%d\n ″,result);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 87
10.6 返回指针值的函数一个函数可以带回一个整型值、字符值、实型值等,
也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。
这种带回指针值的函数,一般定义形式为类型名 *函数名(参数表列) ;
例如:
int *a( int x,int y) ;
C程序设计(第三版) http://ccf.tsinghua.edu.cn 88
例 10.2 4 有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。
#include <stdio.h>
void main()
{ float *score[ ][ 4] ={{60,70,80,90},
{56,89,67,88},{34,78,90,66}};
float *search( float (*pointer)[ 4],int n);
float *p;
int i,m;
printf( ″enter the number of student,″);
scanf( ″%d ″,&m);
printf( ″The scores of No.%d are,\n ″,m);
C程序设计(第三版) http://ccf.tsinghua.edu.cn 89
p= search(score,m);
for(i=0;i<4;i++=
printf( ″%5.2f\t ″,*(p+i));

float * search( float (*pointer )[ 4],int n)
{ float *pt;
pt= *(pointer+n);
return(pt);

运行情况如下:
enter the number of student,1 ↙
The scores of No,1 are:
56.00 89.00 67.00 88.00
C程序设计(第三版) http://ccf.tsinghua.edu.cn 90
例 10.2 5 对上例中的学生,找出其中有不及格课程的学生及其学生号。
#include <stdio.h>
void main()
{float score[ ][ 4] ={{60,70,80,90},{56,
89,67,88},{34,78,90,66}};
float search( float (*pointer)[ 4]);
float *p;
int i,j;
C程序设计(第三版) http://ccf.tsinghua.edu.cn 91
for(i=0;i<3;i++)
{p= search( score +i);
if(p== *( score+i))
{ printf( ″No.%d scores,″,i);
for(j=0;j<4;j++ )
printf( ″% 5.2f ″,*(p+j));
printf( ″\n ″);}


C程序设计(第三版) http://ccf.tsinghua.edu.cn 92
10.7 指针数组和指向指针的指针
10.7.1 指针数组的概念一个数组,若其元素均为指针类型数据,称为 指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。
一维指针数组的定义形式为,
例如:
*p [4 ];
C程序设计(第三版) http://ccf.tsinghua.edu.cn 93
C程序设计(第三版) http://ccf.tsinghua.edu.cn 94
例 10.2 6 将若干字符串按字母顺序(由小到大)输出。
#include <stdio.h>
#include <string.h>
void main()
{ void sort( char *name[ ],int n);
void printf( char *name[ ],int n);
char *name[ ] ={"Follow me","BASIC","Great
Wall″,"FORTRAN","Computer design"};
int n=5;
sort(name,n);
print(name,n);

C程序设计(第三版) http://ccf.tsinghua.edu.cn 95
void sort( char *name[ ],int n)
{ char *temp;
int i,j,k;
for(i=0;i<n-1;i++=
{k=i;
for(j=i+1;j<n;j++=
if( strcmp( name[k ],name[j ]) >0)k =j;
if(k !=i)
temp=name[ i] ;
name[ i] =name[ k] ;
name[ k] =temp;}


C程序设计(第三版) http://ccf.tsinghua.edu.cn 96
void print( char *name[ ],int n)
{int i;
for(i=0;i<n;i++)
printf( ″%s\n ″,name[i]);
}
运行结果为:
BASIC
Computer design
FORTRAN
Follow me
Great Wall
C程序设计(第三版) http://ccf.tsinghua.edu.cn 97
10.7.2 指向指针的指针定义一个指向指针数据的指针变量,
**p;
p的前面有两个 *号。 *运算符的结合性是从右到左,
因此 **p相当于 *( *p),显然 *p是指针变量的定义形式。如果没有最前面的 *,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个
*号,表示指针变量p是指向一个字符指针变量的。
*p就是p所指向的另一个指针变量。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 98
例 10.2 7 使用指向指针的指针。
#include <stdio.h>
void main()
{ char *name[] ={"Follow me","BASIC","Great
Wall″,"FORTRAN","Computer design"};
char **p;
int i;
for(i=0;i<5;i++)
{p=name+i;
printf( ″%s\n ″,*p);


C程序设计(第三版) http://ccf.tsinghua.edu.cn 99
例 10.28 一个指针数组的元素指向整型数据的简单例子 。
#include <stdio.h>
void main()
{ int a[5]={1,3,5,7,9};
int *num[ 5] ={ &a[ 0],&a[ 1],
&a[ 2],&a[ 3],&a[ 4]};
int **p,i;
p=num;
for(i=0;i<5;i++=
{ printf( ″%d ″,**p);
p++;


C程序设计(第三版) http://ccf.tsinghua.edu.cn 100
10.7.3 指针数组作 main函数的形参指针数组的一个重要应用是作为 main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式,void main()括弧中是空的。
main函数可以有参数,例如:
void main( int argc,char *argv[ ])。
命令行的一般形式为 命令名 参数1 参数
2 …… 参数n
C程序设计(第三版) http://ccf.tsinghua.edu.cn 101
例如一个名为 file1的文件,它包含以下的 main函数,
void main( int argc,char *argv[ ])
{ while(argc>1)
{++ argv;
printf( ″%s\n ″,argv);
-- argc;


在 DOS命令状态下输入的命令行为
file1 China Beijing
则执行以上命令行将会输出以下信息:
China
Beijing
C程序设计(第三版) http://ccf.tsinghua.edu.cn 102
10.8有关指针的数据类型和指针运算的小结
10.8.1有关指针的数据类型的小结
C程序设计(第三版) http://ccf.tsinghua.edu.cn 103
定义 含义
int i; 定义整型变量i
*p; p为指向整型数据的指针变量
int a[ n] ; 定义整型数组a,它有n个元素
int *p[n]; 定义指针数组p,它由n个指向整型数据的指针元素组成
int ( *p)[n]; p为指向含n个元素的一维数组的指针变量
int f(); f为带回整型函数值的函数
int *p(); p为带回一个指针的函数,该指针指向整型数据
int ( *p)(); p为指向函数的指针,该函数返回一个整型值
int **p; p是一个指针变量,它指向一个指向整型数据的指针变量
C程序设计(第三版) http://ccf.tsinghua.edu.cn 104
10.8.2 指针运算小结
(1) 指针变量加(减)一个整数例如:p++、p--、p+i、p-i、p+=i、
p-=i等。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 105
(2) 指针变量赋值将一个变量地址赋给一个指针变量。如:
p=&a; (将变量a的地址赋给p)
p= array; (将数组array首元素地址赋给p)
p=& array[i];(将数组array第i个元素的地址赋给p)
p= max;(max为已定义的函数,将max的入口地址赋给p)
p1=p2;(p1和p2都是指针变量,将p2的值赋给p1)
C程序设计(第三版) http://ccf.tsinghua.edu.cn 106
(3) 指针变量可以有空值,即该指针变量不指向任何变量 。
(4) 两个指针变量可以相减如果两个指针变量都指向同一个数组中的元素,
则两个指针变量值之差是两个指针之间的元素个数 。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 107
(5) 两个指针变量比较若两个指针指向同一个数组的元素,则可以进行比较。指向前面的元素的指针变量“小于”
指向后面元素的指针变量。
C程序设计(第三版) http://ccf.tsinghua.edu.cn 108
10.8.3 void指针类型
ANSIC新标准增加了一种,void”指针类型,即可定义一个指针变量,但不指定它是指向哪一种类型数据的。 ANSIC标准规定用动态存储分配函数时返回 void指针,它可以用来指向一个抽象的类型的数据,在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。 例如,
*p1;
*p2;

p1=(char *)p2;
C程序设计(第三版) http://ccf.tsinghua.edu.cn 109
同样可以用(void *)p1将p1的值转换成
void *类型。如:
p2=(void *)p1;
也可以将一个函数定义为void *类型,如,
void *fun( char ch1,char ch2)
表示函数fun返回的是一个地址,它指向“空类型”,如需要引用此地址,也需要根据情况对之进行类型转换,如对该函数调用得到的地址要进行以下转换:
p1=(char *)fun(c h1,c h2);