第十章
§ 10.1地址和指针的概念为了说清楚什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。
内存区的每一个字节有一个编号,这就是,地址,。如果在程序中定义了一个变量,在对程序进行编译时或程序运行时,系统就会给这个变量分配内存单元。
main()
{float x;
int y;

}
变量的两个物理意义
2000
2001
2002
2003
2004
2005

x
y
变量的内容变量的地址
main()
{int a,b,c;
a=5;
b=3;
c=a+b;

}
2000
2001
2002
2003
2004
2005
a
b
c
5
直接将整数 5存入变量 a 3
直接将整数 3存入变量 b 8
直接将变量 a,b的值取出,相加后存入变量 c
利用变量名存取数据的方式称为,直接存取
”方式。
另一种存取变量值的方式称为,间接访问,
的方式。即将变量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 定义一个指针变量定义指针变量的一般形式为基类型 *指针变量名;
指针变量用来指向另一个变量,
为了表示指针变量和它所指向的变量之间的联系,在程序中用
,*”符号表示,指向,,例如:
i_pointer代表指针变量,而
*i_pointer是 i_pointer所指向的变量,即 *i_pointer也代表一个变量,它和变量 i是同一回事。
1) i= 3;
2) *i_pointer=3;
这两个语句作用相同。
下面都是合法的定义:
float *pointer_3; // pointer_3是指向 float型变量的指针变量
char *pointer_4; //pointer_4是指向字符型变量的指针变量可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向一个该变量。如:
pointer_1=&i;
pointer_2=&j;
main()
{ int a,*p=&a;
float x,*q=&x;

}
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
a
p
x
q
2004
2000void main()
{int a,*p;
float x,*q;
p = &a;
q = &x;

}
指针变量定义的形式 指针变量初始化在定义指针变量时要注意两点:
(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 *p1,*p2,*p,a,b;
scanf(“%d,%d”,&a,&b);
p1=&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”,*p1,*p2);

运行情况如下:
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=*p1;
*p1=*p2;
*p2=temp;
}
例 10.4 输入 a,b,c 3个整数,按大小顺序输出
#include <stdio.h>
void main()
{ void exchange(int *q1,int *q2,int *q3);
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;
}
指针的加减运算补充:指针的基本运算
main()
{int a[]={10,20,30,40,50},*p1,*p2;
p1=p2=a;
printf(″p1 =%u,*p1=%d\n″,p1,*p1);
p2+=3;
printf(″p2 =%u,*p2=%d\n″,p2,*p2);
}
P1=404,*p1=10
P2=410,*p2=40
404
405
406
407
408
409
410
411
412
413
a[0]
a[1]
a[2]
a[3]
a[4]
30
20
10
40
50
p1
p2指针加减运算要点:
① 只有当指针变量 指向数组 时指针的加减运算才有意义。
② 指针变量可加减一个整型表达式。如:
p1++,p2+3,p2--,p2-2。
③ 指针的加减运算是以 基类型 为单位 (即
sizeof(类型) )的 。
④ 两个指针变量 不能 作 加 法运算,只有当两个指针变量指向同一数组时,进行指针变量相减才有实际意义。如,p2-p1。
当指针变量 p指向数组中的元素时,n为正整数:
p+n:指针变量 p所指向当前元素 之后 的第 n个元素;
p-n:指针变量 p所指向当前元素 之前 的第 n个元素 。
p++和 ++p:指针变量加 1,指向数组中的 下一 个元素;
p--和 --p:指针变量减 1,指向数组中的 前一 个元素 。
char m[8],*p;
p=&m[4];
p-2
p-1
p
p+1
p+2
p+3
p-4
p-3
m[0]
m[1]
m[2]
m[3]
m[4]
m[5]
m[6]
m[7]
指针的加减运算指针的关系运算 404
405
406
407
408
409
410
411
412
413
a[0]
a[1]
a[2]
a[3]
a[4]
30
20
10
40
50
p1
p2
① 指向同一数组的两个指针可以进行关系运算,表明它们所指向元素的相互位置关系 。
如,p2 > p1,p2 == p1。
② 指针与一个整型数据进行比较是没有意义的 。
③ 不同类型指针变量之间比较是非法的 。
④ NULL可以与任何类型指针进行 ==,!=
的关系运算,用于判断指针是否为空指针 。
§ 10.3 数组与指针一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓 数组元素的指针就是数组元素的地址 。
⑴ 数组名是该数组的指针
a是数组的首地址(即 a[0]的地址),是一个指针常量。
a = &a[0],a+1 = &a[1],…,a+9 = &a[9]
数组元素的下标表示法:
a[0],a[1],…,a[i],…,a[9]
数组元素的指针表示法:
*(a+0),*(a+1),…,*(a+i),…,*(a+9)
1,一维数组的指针例如,int a[10],*p;
a[0]
a[9]
p
a
10.3.1 指向数组元素的指针当 p指向 a[0]时,用 p表示数组元素下标法:
p[0],p[1],…,p[i],…,p[9]
指针法:
*(p+0),*(p+1),…,*(p+i),…,*(p+9)
a[0]
a[9]
p
1,一维数组的指针
⑵ 指向一维数组元素的指针变量数组元素也是一个内存变量,此类指针变量的定义和使用与指向变量的指针变量相同。例如,int a[10],*p;
p = a;(或 p = &a[0];)
a
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++);
printf(“\n”);
p=a;
for(i=0;i<10;i++,p++)
printf(“%d”,*p);
}
10.3.3 用数组名作函数参数在第 8章 8.7节中介绍过可以用数组名作函数的参数如,void main()
{ f(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;
}
}
运行情况如下:
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;}

归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下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.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
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.14 在上题基础上,查找有一门以上课程不及格的学生,打印出他们的全部课程的成绩。
#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.15 定义一个字符数组,对它初始化,
然后输出该字符串
#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.17 将字符串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.18 用指针变量来处理例 10.17问题。
#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.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.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.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);