10 指针
10.1 地址指针的基本概念
一般把存储器中的一个字节称为一个内存单元,内存单元的编号叫做地址。
“指针”是指地址,是常量,“指针变量”是指取值为地址的变量。定义指针的目的是为了通
过指针去访问内存单元。
因为数组或函数都是连续存放的。通过访问指针变量取得了数组或函数的首地址,也就找
到了该数组或函数。
10.2 变量的指针和指向变量的指针变量
变量的指针就是变量的地址。存放变量地址的变量是指针变量。
为了表示指针变量和它所指向的变量之间的关系,在程序中用“*”符号表示“指向”,
例如,i_pointer代表指针变量,而*i_pointer是i_pointer所指向的变量。
10.2.1定义一个指针变量
对指针变量的定义包括三个内容:
(1) 、指针类型说明,即定义变量为一个指针变量;
(2) 、指针变量名;
(3) 、变量值(指针)所指向的变量的数据类型。
其一般形式为: 类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符
表示本指针变量所指向的变量的数据类型。
例如: int *p1;
表示p1是一个指针变量,它的值是某个整型变量的地址。
10.2.2指针变量的引用
指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。
未经赋值的指针变量不能使用,否则将造成系统混乱,甚至死机。指针变量的
赋值只能赋予地址, 决不能赋予任何其它数据。
两个有关的运算符:
1) 、&:取地址运算符。
2) 、*:指针运算符(或称“间接访问” 运算符)。
C语言中提供了地址运算符&来表示变量的地址。
其一般形式为:&变量名;
(1) 、指针变量初始化的方法
int a;
int *p=&a;
(2) 、赋值语句的方法
int a;
int *p;
p=&a;
例10.1
#include<stdio.h>
int main(void)
{
int a,b;
int *pointer_1,*pointer_2;
a=100;b=10;
pointer_1=&a;
pointer_2=&b;
printf("%d,%d\n",a,b);
printf("%d,%d\n",*pointer_1,*pointer_2);
}
对程序的说明:
1) 、在开头处虽然定义了两个指针变量pointer_1和pointer_2,担它们并未指向
任何一个整型变量。只是提供两个指针变量,规定它们可以指向整型变量。程
序第5、6行的作用就是使pointer_1指向a,pointer_2指向b。
2) 、最后一行的*pointer_1和*pointer_2就是变量a和b。最后两个printf函数作用
是相同的。
3) 、程序中有两处出现*pointer_1和*pointer_2,请区分它们的不同含义。
例10.2 输入a和b两个整数,按先大后小的顺序输出a和b。
#include<stdio.h>
int main(void)
{
int *p1,*p2,*p,a,b;
printf("input two number:");
scanf("%d%d",&a,&b);
p1=&a;p2=&b;
if(a<b)
{p=p1;p1=p2;p2=p;}
printf("\na=%d,b=%d\n",a,b);
printf("max=%d,min=%d\n",*p1,*p2);
}
函数的参数不仅可以是整型、实型、字符型等数据,还可以是指针类型。
它的作用是将一个变量的地址传送到另一个函数中。
例10.3 输入的两个整数按大小顺序输出
#include<stdio.h>
void swap(int *p1,int *p2)//把*p1和*p2交换一下
{
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
int main(void)
{
int a,b;
int *pointer_1,*pointer_2;
printf("input two numbers:");
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);
}
swap函数的形式参数pointer_1和pointer_2是变量a和b的地址,调用函数时,
int *p1=*pointer_1是a的值,int *p2=*pointer_2是b的值,通过if语句可以,
如果a<b,那么通过swap函数交换a和b的输出位置顺着上一题目的思路,
我们再做一个题
例10.5 输入a、b、c3个整数,按大小顺序输出。
题目分析:按照上一题的思路,我们先做一个交换2个变量值的函数,然后我
们进行判断,我们逐个对比,如果a<b,交换把b放在a前面,如果a<c,把c放
在a前面,如果b<c,把放在b前面。
#include<stdio.h>
void swap(int *pt1,int *pt2)
{
int temp;
temp=*pt1;
*pt1=*pt2;
*pt2=temp;
}
void exchange(int *q1,int *q2,int *q3)
{
if(*q1<*q2)swap(q1,q2);
if(*q1<*q3)swap(q1,q3);
if(*q2<*q3)swap(q2,q3);
}
int main(void)
{
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);
}
10.2.4指针变量几个问题的进一步说明
1、指针运算符
1) 、取地址运算符&:取地址运算符&是单目运算符,其结合性为自右至左,
其功能是取变量的地址。
2) 、取内容运算符*:取内容运算符*是单目运算符,其结合性为自右至左,
用来表示指针变量所指的变量
2.、指针变量的运算
1) 、赋值运算:指针变量的赋值运算有以下几种形式。
① 指针变量初始化赋值
② 把一个变量的地址赋予指向相同数据类型的指针变量。
例如: int a,*pa;
pa=&a; /*把整型变量a的地址赋予整型指针变量pa*/
③ 把一个指针变量的值赋予指向相同类型变量的另一个指针变量。
如: int a,*pa=&a,*pb;
pb=pa; /*把a的地址赋予指针变量pb*/
④ 把数组的首地址赋予指向数组的指针变量。
例如: int a[5],*pa;
pa=a; (数组名表示数组的首地址,故可赋予指向数组的指针变量pa)
也可写为: pa=&a[0]; /*数组第一个元素的地址也是整个数组的首地址, 也可赋予pa*/
当然也可采取初始化赋值的方法: int a[5],*pa=a;
⑤ 把字符串的首地址赋予指向字符类型的指针变量。
例如: char *pc;
pc="C Language";
或用初始化赋值的方法写为: char *pc="C Language";
⑥ 把函数的入口地址赋予指向函数的指针变量。
例如: int (*pf)();
pf=f; /*f为函数名*/
2)、 加减算术运算
对于指向数组的指针变量,可以加上或减去一个整数n。指向数组的指针加1,
则指向数组的下一个元素。
3) 、两个指针变量之间的运算:只有指向同一数组的两个指针变量之间才能
进行运算,否则运算毫无意义。
① 两指针变量相减
两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。
② 两指针变量进行关系运算:指向同一数组的两指针变量进行关系运算可表
示它们所指数组元素之间的关系。
对指针变量赋0值和不赋值是不同的。指针变量未赋值时,可以是任意值,
是不能使用的。否则将造成意外错误。而指针变量赋0值后,则可以使用,
只是它不指向具体的变量而已。
例10.7
#include<stdio.h>
int main(void)
{
int a=10,b=20,s,t,*pa,*pb; //说明pa,pb为整型指针变量
pa=&a; //给指针变量pa赋值,pa指向变量a
pb=&b; //给指针变量pb赋值,pb指向变量b
s=*pa+*pb; //求a+b之和 *pa是a的值,*pb是b的值
t=*pa**pb; //求a*b的积
printf("a=%d\nb=%d\na+b=%d\na*b=%d\n",a,b,a+b,a*b);
printf("s=%d\nt=%d\n",s,t);
}
例10.8
#include<stdio.h>
int main(void)
{
int a,b,c,*pmax,*pmin; //*pmax,*pmin为整型指针变量
printf("intput three number:\n");//输入提示
scanf("%d%d%d",&a,&b,&c); //输入三个数字
if(a>b)
{
pmax=&a;
pmin=&b;
}
else
{
pmax=&b;
pmin=&a;
}
if(c>*pmax) pmax=&c;
if(c<*pmin) pmin=&c;
printf("max=%d\nmin=%d\n",*pmax,*pmin);
}
10.3 数组指针和指向数组的指针变量
所谓数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。
10.3.1指向数组元素的指针
我们直接用一个例子来说明一下此问题。
int a[10]; //定义一个数组a
int *p; //定义p为指向整型变量的指针
我们来让p指向数组a
p=&a[0] 或者 p=a 或者int *p=&a[10] 或者 int *p=a
数组指针变量说明的一般形式为: 类型说明符 *指针变量名;
10.3.2通过指针引用数组元素
引用一个数组元素可以用:
1) 、下标法,即用a[i]形式访问数组元素。在前面介绍数组时都是采用这种方法。
2) 、指针法,即采用*(a+i)或*(p+i)形式,用间接访问的方法来访问数组元素,
其中a是数组名,p是指向数组的指针变量,其处值p=a。
如果p的初值为&a[0],则:
1)、 p+i和a+i就是a[i]的地址,或者说它们指向a数组的第i个元素。
2)、 *(p+i)或*(a+i)就是p+i或a+i所指向的数组元素,即a[i]。
例如,*(p+5)或*(a+5)就是a[5]。
3) 、指向数组的指针变量也可以带下标,如p[i]与*(p+i)等价。
例10.9 输出数组中的全部元素。(下标法)
#include<stdio.h>
int main(void)
{
int a[10],i;
for(i=0;i<10;i++)
{
a[i]=i;
}
for(i=0;i<5;i++)
{
printf("a[%d]=%d\n",i,a[i]);
}
}
例10.10 输出数组中的全部元素。(通过数组名计算元素的地址,找出元素的值)
#include<stdio.h>
int main(void)
{
int a[10],i;
for(i=0;i<10;i++)
*(a+i)=i;
for(i=0;i<10;i++)
printf("a[%d]=%d\n",i,*(a+i));
}
例10.11 输出数组中的全部元素。(用指针变量指向元素)
#include<stdio.h>
int main(void)
{
int a[10],i,*p;
p=a;
for(i=0;i<10;i++)
*(p+i)=i;
for(i=0;i<10;i++)
printf("a[%d]=%d\n",i,*(p+i));
}
#include<stdio.h>
int main(void)
{
int a[10],i,*p=a;
for(i=0;i<10;i++)
{
*p=i;
printf("a[%d]=%d\n",i++,*p++);
}
}
10.3.3数组名作函数参数
数组名就是数组的首地址,实参向形参传送数组名实际上就是传送数组的地址,
形参得到该地址后也指向同一数组
#include<stdio.h>
float aver(float *pa);
int main(void)
{
float sco[5],av,*sp;
int i;
sp=sco;
printf("\ninput 5 scores:\n");
for(i=0;i<5;i++)
scanf("%f",&sco[i]);
av=aver(sp);
printf("average score is %5.2f",av);
}
float aver(float *pa)
{
int i;
float av,s=0;
for(i=0;i<5;i++)
s=s+*pa++;
av=s/5;
return av;
}
算法为:将a[0]与a[n-1]对换,再a[1]与a[n-2] 对换……,直到将a[(n-1/2)]
与a[n-int((n-1)/2)]对换。今用循环处理此问题,设两个“位置指示变量”i和j,
i的初值为0,j的初值为n-1。将a[i]与a[j]交换,然后使i的值加1,j的值减1,
再将a[i]与a[j]交换,直到i=(n-1)/2为止
#include<stdio.h>
void inv(int *x,int n)
{
int *p,temp,*i,*j,m=(n-1)/2;
i=x;j=x+n-1;p=x+m;
for(;i<=p;i++,j--)
{temp=*i;*i=*j;*j=temp;}
return;
}
int main(void)
{
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 benn inverted:\n");
for(i=0;i<10;i++)
printf("%d",a[i]);
printf("\n");
}
例10.18 从10个数中找出其中最大值和最小值。
#include<stdio.h>
int max,min; //全局变量
void max_min_value(int array[],int n)
{
int *p,*array_end;
array_end=array+n;
max=min=*array;
for(p=array+1;p<array_end;p++)
if(*p>max)max=*p;
else if(*p<min)min=*p;
return;
}
int main(void)
{
int i,number[10];
printf("enter 10 integer numbers:\n");
for(i=0;i<10;i++)
scanf("%d",&number[i]);
max_min_value(number,10);
printf("\nmax=%d,min=%d\n",max,min);
}
说明:
1) 、在函数max_min_value中求出的最大值和最小值放在max和min中。
由于它们是全局,因此在主函数中可以直接使用。
2) 、函数max_min_value中的语句:max=min=*array; array是数组名,
它接收从实参传来的数组numuber的首地址。 *array相当于*(&array[0])。
上述语句与 max=min=array[0];等价。
3) 、在执行for循环时,p的初值为array+1,也就是使p指向array[1]。
以后每次执行p++,使p指向下一个元素。每次将*p和max与min比较。
将大者放入max,小者放min。
4) 、函数max_min_value的形参array可以改为指针变量类型。实参也可以不用
数组名,而用指针变量传递地址。