3.4 指针
所谓指针就是指内存中的地址,它可能是变量的地址,也可能是函数的入口地址。如果指针变量存储的地址是变量的地址,则称该指针为变量的指针(或变量指针);如果指针变量存储的地址是函数的入口地址,则称该指针为函数的指针(或函数指针)。
1.变量指针和指向变量的指针变量
指针变量与变量指针的含义不同:指针变量也简称指针,是指一个变量,且该变量是指针类型的;而变量指针虽然也是指一个变量,且该变量也是指针类型的,但它存放的是另一个变量的地址。
1)指针变量的定义在C语言中,所有的变量在使用之前必须定义,以确定其类型。指针变量也一样,由于它是用来专门存放地址的,所以必须将它定义为“指针类型”。指针定义的一般形式如下:
类型标识符 *指针变量名
其中,类型标识符就是本指针变量所指向的变量的数据类型;“*”表示这是一个指针变量;指针变量名就是指针变量的名称。例如:
int *ap1 //定义整型指针变量ap1 char *ap2,*ap3 //定义了两个字符型指针变量ap2和ap3 float *ap4 //定义了实数型指针变量ap4
在定义指针变量时要注意以下几点。
(1)指针变量名前的“*”表示该变量为指针变量,上例中的指针变量名为ap1、ap2、ap3、ap4,而不是*ap1、*ap2、*ap3、*ap4,这与定义变量有所不同。
(2)一个指针变量只能指向同一个类型的变量,上例中的ap1只能指向整型变量,不能指向字符型或实数型指针变量。
2)指针变量的引用指针变量在使用之前也要先定义说明,然后赋予具体的值。指针变量只能赋予地址,而不能赋予任何其他数据,否则将引起错误。在C语言中,变量的地址由编译系统分配,用户不知道变量的具体地址。
有两个有关的运算符:“&”和“*”,其中,“&”为取地址运算符;“*”为指针运算符(或称“间接访问”运算符)。
在C语言中,指针变量的引用是通过取地址运算符“&”来实现的。使用取地址运算符“&”和赋值运算符“=”就可以使一个指针变量指向一个变量。
例如:给指针变量p所对应的内存地址单元中装入变量x所对应的内存单元地址,可使用以下程序段实现:
int x; //定义整型变量x int *p=&x; //指针变量声明时初始化
还可以采用以下程序段实现:
int x; //定义整型变量x int *p; //定义整型指针变量p p=&x; //用赋值语句对指针赋值
2.数组指针和指向数组的指针变量
指针既然可以指向变量,当然也可以指向数组。所谓数组指针是指数组的起始地址,数组元素的指针是数组元素的地址。若有一个变量用来存放一个数组的起始地址(指针),则称它为指向数组的指针变量。
1)指向数组元素的指针变量的定义与赋值定义一个指向数组元素的指针变量的方法与1中指针变量的定义相同。例如:
int x[6]; //定义含有6个整型数据的数组 int *p; //定义指向整型数据的指针p p=&x[0]; //对指针p赋值,此时数组x[5]的第1个元素x[0]的地址就赋给了指针变量p p=x; //对指针p赋值,此种引用的方法与“p=&x[0];”的作用完全相同,但形式上更简单
在C语言中,数组名代表数组的首地址,也就是第0号元素的地址。因此,语句“P=&x[0];”和“P=x;”是等价的。还可以在定义指针变量时赋给初值:
int *p=&x[0]; //或者int *p=x;
等价于:
int *p; p=&x[0];
2)通过指针引用数组元素如果p指向一个一维数组x[6],并且已给p赋予了一个初值&x[0],则可以使用以下3种方法引用数组元素。
(1)下标法:C语言规定,如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素。P+i和x+i等价于x[i],或者说它们都指向x数组的第i个元素。
(2)地址法:*(p+i)和*(x+i)等价于x[i]。实际上,编译器会将数组元素x[i]处理成*(x+i),即按数组的首地址加上相对位移量得到要找元素的地址,然后找出该单元中的内容。(3)指针法:用间接访问的方法来访问数组元素,指向数组的指针变量也可以带下标,如p[i]与*(p+i)等价。
3)关于指针变量的运算如果先使指针变量p指向数组x[](即p=x;),则可得出以下几个结论。
(1)p++(或p+=1):该操作将使指针变量p指向下一个数组元素,即x[1]。若再执行x=*p,则将取出x[1]的值,将其赋给变量x。
(2)*p++:由于++与*运算符优先级相同,而结合方向为自右向左,所以*p++等价于*(p++),其作用是先得到p指向的变量的值(即*p),然后再执行p自加运算。
(3)*p++与*++p作用不同:*p++是先取*p值,然后使p自加1;而*++p是先使p自加1,再取*p值。如果p的初值为&x[0],则执行a=*p++后,a的值为x[0]的值;而执行*++p后,a的值等于x[1]的值。
(4)(*p)++:(*p)++表示p所指向的元素值加1。注意,是元素值加1,而不是指针变量值加1。如果指针变量p指向&x[0],且x[0]=4,则(*p)++等价于(a[0])++。此时,x[0]的值增为5。
(5)如果p当前指向数组中第i个元素,则存在以下3种关系:
(p--)与x[i--]等价,相当于先执行*p,然后再使p自减;
(++p)与x[++i]等价,相当于先执行自加,然后再执行*p运算;
(--p)与x[--i]等价,相当于先执行自减,然后再执行*p运算。
3.字符串指针和指向字符串的指针变量
1)字符串指针和指向字符串的指针变量的表示形式在C语言中有两种方法实现一个字符串的运算:一种是使用字符数组;另一种是使用字符串指针。例如:
char a[]={‘C’,‘o’,‘d’,‘e’,‘V’,‘i’,‘s’,‘i’,‘o’,‘n’,‘A’,‘V’,‘R’ ,‘\0’}; //使用字符数组定义 char *b=“CodeVisionAVR”; //使用字符串指针定义
字符串指针变量的定义说明与指向字符变量的指针变量说明是相同的。在上述程序段中,a[]是一个字符数组,字符数组是以“\0”常量结尾的;b是指向字符串的指针变量,它没有定义字符数组。由于C语言对字符串常量是按字符数组处理的,所以实际在使用字符串指针时,C编译器也在内存中开辟了一个字符数组用来存放字符串常量。
2)使用字符串指针变量与字符数组的区别用字符数组和字符串指针变量都可实现字符串的存储和运算。但两者是有区别的。在使用时应注意以下几个问题。
(1)字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身存放在以该首地址为首的一块连续的内存空间中并以‘\0’作为串的结束。字符数组是由若干个数组元素组成的,它可用来存放整个字符串。
(2)定义一个字符数组时,在编译中已分配内存单元,有确定的地址。而定义一个字符串指针变量时,会给指针变量分配内存单元,但并不知道该指针变量具体指向哪个字符串,即指针变量存放的地址不确定。
(3)赋值方式不同。对字符数组不能整体赋值,只能将其转化成分量后,对单个元素进行赋值。而字符串指针变量可整体进行赋值,直接将其指向字符串首地址即可。
(4)字符串指针变量的值在程序运行过程中可以改变,而字符数组名是一个常量,不能改变。