第十章
§ 10.1地址和指针的概念为了说清楚什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。
内存区的每一个字节有一个编号,这就是,地址,。
如果在程序中定义了一个变量,在对程序进行编译时,
系统就会给这个变量分配内存单元。
1、按变量地址存取变量值的方式称为,直接访问,方式
printf( ″%d ″,i) ;
scanf( ″%d ″,&i);
k=i+j;
例如:
另一种存取变量值的方式称为,间接访问,的方式。
即,将变量i的地址存放在另一个变量中。
在C语言中,指针是一种特殊的变量,它是存放地址的。
假设我们定义了一个指针变量 i_pointer用来存放整型变量的地址,它被分配地址为 (3010),(3011)的两个字节。
可以通过语句,i_pointer =&i;
将i的地址 (2000)存放到 i_pointer中。这时,i_pointer
的值就是 (2000),即变量i所占用单元的起始地址。要存取变量i的值,可以采用间接方式:先找到存放“i
的地址”的变量 i_pointer,从中取出i的地址 (2000),
然后到 2000,2001字节取出i的值(3)。
一个变量的地址称为该变量的,指针,。
例如,地址 2000是变量i的指针。 如果有一个变量专门用来存放另一变量的地址(即指针),则它称为
,指针变量,。上述的 i_pointer就是一个指针变量。
指针和指针变量的定义:
指针变量的值(即指针变量中存放的值)是地址(即指针)。请区分“指针”和“指针变量”
这两个概念。
§ 10.2 变量的指针和指向变量的指针变量
10.2,1 定义一个指针变量定义指针变量的一般形式为基类型 *指针变量名;
下面都是合法的定义:
float *pointer_3; // pointer_3是指向 float型变量的指针变量
char *pointer_4; //pointer_4是指向字符型变量的指针变量可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向一个该变量。如:
pointer_1=&i;
pointer_2=&j;
在定义指针变量时要注意两点:
(1)指针变量前面的,*”,表示该变量的类型为指针型变量。
例,float *pointer_1;
指针变量名是 pointer_1,而不是 * pointer_1 。
(2) 在定义指针变量时必须指定基类型。
需要特别注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中。下面的赋值是错误的 ∶
float a;
int * pointer_1;
pointer_1=&a; /* 将 float型变量的地址放到指向整型变量的指针变量中,错误 */
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 */
pointer_2=&b; /*把变量b的地址赋给
pointer_2 */
printf( ″%d,%d \n ″,a,b);
printf( ″%d,%d \n ″,*pointer_1,*pointer_2);

对“&”和,*”运算符说明:
如果已执行了语句 pointer_1=&a;
(1)& * pointer_1 的含义是什么?“&”和,*”两个运算符的优先级别相同,但按自右而左方向结合,
因此先进行 * pointer_1 的运算,它就是变量a,再执行&运算。因此,& * pointer_1 与 &a 相同,即变量 a的地址。如果有 pointer_2 =& * pointer_1 ;
它的作用是将&a(a的地址)赋给 pointer_2,如果 pointer_2原来指向b,经过重新赋值后它已不再指向b了,而指向了a。
(2) *&a的含义是什么?先进行&a运算,得a的地址,再进行 *运算。即&a所指向的变量,也就是变量
a。 *&a和 *pointer_1的作用是一样的,它们都等价于变量a。即 *&a与a等价。
(3) ( *pointer_1)++相当于a++。注意括号是必要的,如果没有括号,就成为了 *pointer_1++,从附录可知,++和 *为同一优先级别,而结合方向为自右而左,因此它相当于 *(pointer_1++ )。由于 ++在
pointer_1的右侧,是“后加”,因此先对 pointer_1的原值进行 *运算,得到a的值,然后使 pointer_1的值改变,这样 pointer_1不再指向a了。
例 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);

运行情况如下:
5,9 ↙
a=5,b=9
max=9,min=5
当输入a=5,b=9时,由于a<b,
将p1和p2交换。交换前的情况见图
(a),交换后见图(b)。
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);

void swap( int *p1,int *p2)
{ int temp;
temp= *p 1;
*p1= *p2;
*p2= temp;

例 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);

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;

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

(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));

(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);

例 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数组中各元素的值解决这个问题的办法,只要在第二个for循环之前加一个赋值语句:
p=a;
#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);

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 )
{

}
f (int arr[ ],int n)
但在编译时是将 arr按指针变量处理的,相当于将函数 f的首部写成
f (int *arr,int n)
以上两种写法是等价的。
需要说明的是,C语言调用函数时虚实结合的方法都是采用,值传递,方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量 。
例 10.7 将数组a中n个整数按相反顺序存放
#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 ″);

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,
#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改成指针变量。
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;

归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
(1) 形参和实参都用数组名,如:
void main() void f ( int x [ ],int n)
{ int a[10]; {
… …
f (a,10); }

(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); }

(4) 实参为指针变量,形参为数组名。如:
void main() void f( int x[ ],int n)
{int a[10],*p=a; {
┇ ┇
f( p,10); }

#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 ″);

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;

例 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++;}

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;}


10.3,4 多维数组与指针用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。但在概念上和使用上,多维数组的指针比一维数组的指针要复杂一些。
1,多维数组元素的地址先回顾一下多维数组的性质,可以认为二维数组是“数组的数组”,例,
定义 int a[ 3][ 4] ={{1,3,5,7},{9,11,
13,15},{17,19,21,23} };
则二维数组 a是由 3个一维数组所组成的。设二维数组的首行的首地址为2000,则表 示 形 式 含义 地 址
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
例 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));

某一次运行结果如下:
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列元素的值 )
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
可将程序最后两个语句改为
printf( ″addr=%o,value=% 2d\n ″,p,*p);
在 TC++环境下某一次运行时输出如下:
addr=236,value=1
addr=240,value=3
addr=242,value=5
addr=244,value=7
addr=246,value=9
addr=250,value=11
addr=252,value=13
addr=254,value=15
addr=256,value=17
addr=260,value=19
addr=262,value=21
addr=264,value=23
(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
运行情况如下:
i=1,j=2 ↙ (本行为键盘输入)
a[1,2]=13
3,用指向数组的指针作函数参数例 10.13 有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。这个题目是很简单的。只是为了说明用指向数组的指针作函数参数而举的例子。用函数average求总平均成绩,用函数search找出并输出第i个学生的成绩。
#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的学生的成绩 */

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);

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
例 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);

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 fail s,his scores are:
65,0 57,0 70,0 60,0
No.2 fail s,his scores are ∶
58,0 87,0 90,0 81,0
§ 10.4 字符串与指针
10.4.1字符串的表示形式例 10.1 5 定义一个字符数组,对它初始化,
然后输出该字符串
#include <stdio.h>
void main()
{ char string[]= ″I love China! ″;
printf( ″%s\n ″,string);

(1) 用字符数组存放一个字符串,然后输出该字符串。
(2) 用字符指针指向一个字符串可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。
例 10.1 6 定义字符指针
#include <stdio.h>
void main()
{ char string= ″I love China! ″;
printf( ″%s\n ″,string);

例 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 ″);

也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。
例 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++)
*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同步移动。
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) 用字符数组作参数
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.
string b=I am a teacher.
(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\nstring 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);

void copy_string( char *from,char *to)
{ for(; *from!= ′\0 ′; from++,to++)
*
*to= ′\0 ′;

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

2,copy_string函数的函数体还可改为

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

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

4、上面的 while语句还可以进一步简化为下面的 while
语句:
while( *to++= *from++);
它与下面语句等价:
while(( *to++= *from++)!= ′\0 ′);
将 *from赋给 *to,如果赋值后的 *to值等于 ′
\0 ′,则循环终止( ′\0 ′已赋给 *to)
5、函数体中while语句也可以改用for语句:
for(;( *to++= *from++)!=0;);

for(; *to++= *from++;);
6、也可用指针变量,函数 copy_string可写为
void copy_string ( char from[ ],char to[ ])
*p1,*p2;
p1=from;p2=to;
while(( *p2++= *p1++)!= ′\0 ′);

10.4,3 对使用字符指针变量和字符数组的讨论虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点:
(1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第 1
个字符的地址),决不是将字符串放到字符指针变量中。
(2)赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。
char str[14];
str= ″I love China! ″;
而对字符指针变量,可以采用下面方法赋值:
char *a;
a= ″I love China! ″;
但注意赋给a的不是字符,而是字符串第一个元素的地址。
(3)对字符指针变量赋初值:
char *a= ″I love China! ″;等价于
char *a;
a= ″I love China! ″;
而对数组的初始化:
char str[14]={ ″I love China! ″} ;
不能等价于
char str[14];
str[ ]= ″I love China! ″;
(4) 如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,
则它并未具体指向一个确定的字符数据。
如,char str[10];
scanf( ″%s ″,str); 是可以的。
而常有人用下面的方法,目的是想输入一个字符串,虽然一般也能运行,但这种方法是危险的,
char *a;
scanf( ″%s ″,a);
应当这样:
*a,str[10];
a=str;
scanf( ″%s ″,a);
(5) 指针变量的值是可以改变的,如:
例 10.2 0 改变指针变量的值
#include <stdio.h>
void main()
{ char *a= ″I love China! ″;
a=a+7;
printf( ″%s ″,a);

需要说明,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。例 10.2 1
#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]);

10.5 指向函数的指针
10.5,1 用函数指针变量调用函数可以用指针变量指向整型变量、字符串、数组,
也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个 函数的入口地址就称为函数的指针 。
例 10.2 2 求a和b中的大者。先列出按一般方法的程序。
#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);

将 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);

10.5,2 用指向函数的指针作函数参数函数指针变量常用的用途之一是把指针作为参数传递到其他函数。前面介绍过,函数的参数可以是变量、
指向变量的指针变量、数组名、指向数组的指针变量等。现在介绍指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。它的原理可以简述如下:有一个函数
(假设函数名为 sub),它有两个形参( x1和 x2),
定义 x1和 x2为指向函数的指针变量。在调用函数 sub
时,实参为两个函数名f1和f2,给形参传递的是函数f1和f2的地址。这样在函数sub中就可以调用f1和f2函数了。
实参函数名 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函数 */


例 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);
printf( ″max= ″);
process(a,b,max);
printf( ″min= ″);
process(a,b,min);
printf( ″sum= ″);
process(a,b,add);

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);

int add( int x,int y) /* 函数定义 */
{int z;
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);

10.6 返回指针值的函数一个函数可以带回一个整型值、字符值、实型值等,
也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。
这种带回指针值的函数,一般定义形式为类型名 *函数名(参数表列) ;
例如:
int *a( int x,int y) ;
例 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);
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
例 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;
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 ″);}


10.7指针数组和指向指针的指针
10.7,1 指针数组的概念一个数组,若其元素均为指针类型数据,称为 指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式为例如:
*p [4 ];
例 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);

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;}


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
10.7.2 指向指针的指针怎样定义一个指向指针数据的指针变量呢? 如下:
**p;
p的前面有两个 *号。 *运算符的结合性是从右到左,
因此 **p相当于 *( *p),显然 *p是指针变量的定义形式。如果没有最前面的 *,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个
*号,表示指针变量p是指向一个字符指针变量的。
*p就是p所指向的另一个指针变量。
例 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);


例 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++;


10.7,3 指针数组作main函数的形参指针数组的一个重要应用是作为 main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式,void main()
括弧中是空的。实际上,main函数可以有参数,例如:
void main( int argc,char *argv[ ])
argc和 argv就是 main函数的形参。 main函数是由操作系统调用的。那么,main函数的形参的值从何处得到呢?
显然不可能在程序中得到。实际上实参是和命令一起给出的。也就是在一个命令行中包括命令名和需要传给 main函数的参数。命令行的一般形式为 命令名 参数
1 参数2 …… 参数n
如果有一个名为file1的文件,它包含以下的m
ain函数,
void main( int argc,char *argv[ ])
{while(argc>1)
{++argv;
printf( ″%s\n ″
--argc;


在 DOS命令状态下输入的命令行为
file1 China Beijing
则执行以上命令行将会输出以下信息:
China
Beijing
10.8有关指针的数据类型和指针运算的小结
10.8.1有关指针的数据类型的小结定义 含义
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是一个指针变量,它指向一个指向整型数据的指针变量
10.8.2 指针运算小结
(1) 指针变量加(减)一个整数例如:p++、p--、p+i、p-i、p+=i、
p-=i等。
(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)
(3) 指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示,p=NULL ;
(4) 两个指针变量可以相减如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数
(5) 两个指针变量比较若两个指针指向同一个数组的元素,则可以进行比较。
指向前面的元素的指针变量“小于”指向后面元素的指针变量。
10.8.3 void指针类型
ANSIC新标准增加了一种,void”指针类型,即可定义一个指针变量,但不指定它是指向哪一种类型数据的。
ANSIC标准规定用动态存储分配函数时返回 void指针,
它可以用来指向一个抽象的类型的数据,在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。例如,
*p1;
*p2;

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