前言
指针是C语言中广泛使用的一种数据类型。运用指针编程是C语言最主要的风格之一。里用指针变量可以表示各种数据结构;能方便地使用数组和字符串;并能像汇编语言一样处理内存地址,从而编写出精炼且高效的程序。指针极大地丰富了C语言的功能。学习指针是学习C语言重要的一环,能否正确理解和使用指针是我们是否掌握C语言的一个标志。同时C语言中最困难的一部分,在学习中除了要正确理解基本概念,还必须要多编程,上机调试。
一、指针的内涵
在计算机中,所有的数据都是存放在存储器中。一般把存储器中的1字节称为1个内存单位,不同的数据类型所占用的内存单元不等。为了能正确地访问这些单元,必须把每个内存单元编号。根据一个内存单元的编号即可准确找到该内存单元。内存单元的编号也叫地址,通常也把这个地址成为指针。内存单元的指针和内存单元的内容是两个不同的概念。对于1个单元内存来说,单元的地址即指针。因此,1个指针变量的值就是某个内存单元的地址或称为某内存单元的指针,如下:
在图中,设有字符变量c,其内容即变量值‘K'(ASKII码为十进制数75),变量c占用了102A号内存地址(地址用十六进制数表示,即变量的地址为102A)。设有指针变量p,其变量值为102A,即变量p中存放了变量c的地址。这种情况我们称为p指向变量c,或者p是指向变量c的指针。
严格来说,一个指针是一个地址,是一个常量。一个指针变量可以被赋予不同的指针值,是变量。常把指针变量简称为指针。为了避免混淆,我们约定:“指针”指地址,是常量;“指针变量”指取值为地址的变量。定义指针的目的是通过指针去访问内存单元。
在C语言中,一种数据结构类型或数据结构往往看都占有一组连续的内存单元。如下:
假设有定义"int x=20,y=15;",即定义了两个int类型的变量x和y。若int类型占4个字节,则系统给变量x分配编号为1000,1001,1002,1003共4个字节的内存单元来存放他的地址。其中,内存编号1000是变量x的首地址,通常是把这个编号称为变量x的指针。同理,变量y占用了1004到1007号内存单位,变量y的首地址1004被称为y的指针。因此,用“地址”这个概念并不能很好的描述一种数据结构,而“指针”虽然实际上也是一个地址,但他却是一个数据类型或数据结构的首地址。
在上图中,若指针变量p的值是1000,即p中存放了指针x的指针,则称p是指向x的指针。为了表示指针变量和它所指向的变量之间的关系,在程序中用“*”符号表示“指向”。例如,p代表指针变量,而*p和p所指向的变量,也就是变量x,所以*p和x是等价的。若需要访问变量x,则可通过以下两种方式进行访问。
(1)直接访问,即通过变量名来访问。例如:x=3;
(2)间接访问,即通过变量的指针来访问。例如:*p=x;*p=3;
第二个语句的含义是将3赋给指针变量p所指向的变量。
既然指针变量的值是一个地址,那么这个地址不仅可以是变量的地址,也可以是其他数据结构的地址,如数组的地址、构造类型(结构体、共用体等)的地址。另外,程序在计算机中也是要存储的,每个函数都是一段程序,也都有对应的地址。这些数据类型或数据结构的地址也可以保存在指针变量中。
那么在一个指针变量中存放一个数组或一个函数的首地址有何意义呢?
答:因为数组或函数都是连续存放的。通过访问指针变量取得了一个数组或函数的首地址,也就找到了该数组和函数。这样一来,凡是出现数组、函数的地方,都可以用一个指针变量来表示,只要该指针变量被赋予数组或函数的首地址即可。这样做会使程序的概念十分清楚,程序本身也精炼、高效。
二、指针访问基类型数据
1.指针变量的定义
指针变量同普通变量一样,使用之前要定义说明。定义指针的一般形式如下:
类型说明符 *变量名;
其中,*表示这是一个指针变量,变量名及定义的指针变量名,类型说明符表示该指针变量去所指向的变量的数据类型。例如:
int *p1; //*p1是指向整型的指针变量
float *p2 //*p2是指向浮点型的指针变量
char *p3 //*p3是指向字符型的指针变量
应该注意的是,指针变量的只是其他变量的值,也就是内存单元的编号,而所有的内存编号都是unsigned int类型的,即所有指针变量存储的都是unsigned int类型的地址数据。要用指针变量间接访问其指向的变量他,则必须指明其指向的内存单元的大小,因此,定义指针变量是的类型是指针变量要指向变量的类型。另外,一个指针变量只能指向同一类型的变量。
2.指针变量的初始化和引用
指针变量同普通变量一样,使用之前不仅要定义说明,而且必须被赋予具体的值。未经复制的指针变量不能使用,否则将导致系统混乱,甚至死机。在C语言中,变量的地址是由编译系统分配的,对用户完全透明,用户不知道变量的具体地址。接下来,我们来了解如何取得变量的地址?又如何通过指针变量间接访问其指向的变量?
(1)&:取地址运算符,单目运算符,用于变量前,作用为获取变量的地址。其一般形式如下:
&变量名;
如&a表示变量a的地址,&b表示变量b的地址。变量本身必须预先说明。
(2)*:指针运算符(或称“间接访问”运算符),单目运算符,用于指针变量前,作用为间接访问指针变量所指向的变量。其一般形式如下:
*指针变量名;
设有指向整型变量的指针变量p,如要把整形变量a的地址赋予p,可以有以下两种方式。
(1)指针变量初始化的方法。
int a;
int *p=&a;
(2)赋值语句的方法。
int a;
int *p;
p=&a;
此时指针变量p指向整型变量a,以后我们便可以通过指针变量p间接访问变量a,如:
*p=5;
等价于
a=5;
指针变量可出现在表达式中,设有定义:
int a, b,*pa=&a;
指针变量pa指向变量a,则*pa可出现在a能出现的任何地方。例如:
b=*pa+5; //表示把a的值加5后赋给b
b=++*pa; //表示把a的值加1后赋给b,++*pa相当于++(*pa)
b=*pa++; //相当于b=*pa;pa++
b=(*pa)++; //相当于b=*pa;a++
不允许把一个整数赋予指针变量,故下面的赋值是错误的:
int *p;
p=1000;
给指针变量赋值时,前面不能再加“*”说明符,如写为“*p=&a;”,也是错误的。
需要注意的是,指针运算符*和指针变量说明中的指针说明符*不是一回事。在指针变量说明中,“*”是类型说明符,表示其后的变量是指针类型。表达式中出现的“*”则是一个运算符,用以表示相乘。
另外,指针变量和一般变量一样,存放在他们之中的指是可以改变的,也就是说可以改变他们的指向。如下面这个例子。
Example:输入a和b两个整数,按先大后小的顺序输出a和b。
【程序代码】
#include<stdio.h>
int main()
{
int *p1,*p2,*p,a,b;
printf("Please enter a,b:");
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);
return 0;
}
【运行结果】
Please enter a,b:5 8
a=5,b=8
max=5,min=8
通过这个例子可以看到,用指针变量访问它所指向的一个变量是以间接访问的形式进行的。
虽然没有直接对变量进行改变,但是运用这种办法能给程序员带来灵活性。
3.指针变量做函数参数
函数的参数不仅可以是整型、实型、字符型的数据,还可以是指针类型的。它的作用是讲一个变量的地址由主调函数传送到被调函数,从而达到在被调函数中间接访问中调函数中的变量的目的。尤其是当被调函数中有多个计算结果需要传递到主调函数时,使用指针可以很方便达到这一目的。我们可以通过观察几个例子来看一下。
Example:输入的两个整数按先大后小的顺序输出,用函数处理。
【程序代码】
#include<stdio.h>
void swap1(int x,int y)
{
int t;
t=x;x=y;y=t;
}
int main()
{
int a,b;
printf("Please enter a,b:");
scanf("%d%d",&a,&b);
if(a>b)
{
swap1(a,b);
}
printf("max=%d,min=%d\n",a,b);
return 0;
}
【运行结果】
Please enter a,b:5 8
max=5,min=8
--------------------------------
Process exited after 5.146 seconds with return value 0
Press ANY key to exit...
这段代码只进行了值传递,形参改变,并没有改变实参的值,所以并没有达到题目的要求。
那么我们需要用指针进行修改。如下
【程序代码】
#include<stdio.h>
void swap2(int *px,int *py)
{
int t;
t=*px;*px=*py;*py=t;
}
int main()
{
int a,b;
printf("Please enter a,b:");
scanf("%d%d",&a,&b);
if(a<b)
{
swap2(&a,&b);
}
printf("max=%d,min=%d\n",a,b);
return 0;
}
【运行结果】
Please enter a,b:
5 8
max=8,min=5
--------------------------------
Process exited after 3.866 seconds with return value 0
Press ANY key to exit...
这段代码实现了那个题目要求,是由于这里面运用到了指针,通过找到地址改变里面的参数。
注意:不要企图通过改变指针形参的值而使指针实参的值改变。
Example:及输入两个整数按大小顺序输出,用函数处理。
【程序代码】
#include<stdio.h>
void swap3(int *px,int *py)
{
int *pt;
pt=px;px=py;py=pt;
}
int main()
{
int a,b,*pa=&a,*pb=&b;
printf("Please enter a,b:");
scanf("%d%d",&a,&b);
if(a<b)
{
swap3(pa,pb);
}
printf("max=%d,min=%d\n",*pa,*pb);
return 0;
}
【运行结果】
Please enter a,b:5 8
max=5,min=8
--------------------------------
Process exited after 2.268 seconds with return value 0
Press ANY key to exit...
这是由于形参变量和实参变量占用的是不同的内存单元,在函数的调用中发生的数据传送是单向的,即只能把实参的值传给形参,而不能把形参的值传给实参。因此在函数的调用过程中,形参的值发生任何改变,而实参的值不会改变。
因此,要通过函数调用来改变主调函数中某个变量的值,通常需按如下步骤实现。
(1)在主调函数中,将该变量的地址或指向该变量的指针作为实参。
(2)在被调函数中,用指针类型形参接受该变量的地址。
(3)在被调函数中,改变形参所指向变量的值。