1.有关指针的知识
地址指向该变量单元,将地址形象的称为指针。
存储单元的地址和存储单元的内容是有区别的。
对变量进行访问的方式有两种:直接访问和间接访问。间接访问是将一个变量的地址存放到另一个变量中,然后通过该变量访问原变量的地址,进而输出原变量。
有一个变量专门用来存放零一变量的地址(指针),则称它为“指针变量”。
指针变量就是地址变量,用来存放地址,指针变量的值为地址。
2.指针变量
(1)定义指针变量
定义指针变量的一般形式为:
类型名 *指针变量名
如:
int *p1,*p2; //左端的int是定义指针变量必须指定的基类型。
注:
指针变量前边的“*”表示该变量为指针型变量。p1,p2则是指针变量名。而地址是赋给指针变量p1,p2的,而不是赋给*p1,*p2的。
一个变量的指针的含义包括两个方面,一是以存储单元编号表示的纯地址(如编号为2000的字节),另一个是它指向的存储单元的数据类型(int,float等)。
(2)引用指针变量
①给指针变量赋值。如:
p=&a; //把a的地址赋给指针变量p
②引用指针变量指向的变量。如:
已执行“p=&a;”,
printf("%d",*p);
其作用是以整数形式输出指针变量p所指向的变量的值,即变量a的值。
③引用指针变量的值。如:
printf("%o",p);
作用是以八进制数的形式输出指针变量p的值,如果p指向a,就是输出了a的地址,即&a。
3.通过指针引用数组
(1)数组元素的指针
所谓数组元素的指针就是数组元素的地址。
引用数组元素的方法有下标法(如a[3]),也可以用指针法。使用指针法能使目标程序的质量更高。
在C语言中,数组名(不包括形参数组名)代表数组中首元素的地址。
下面两个式子等价:
p=&a[0];
p=a; //p=a是把a数组的首元素的地址赋给变量p
(2)在引用数组元素时指针的运算
可以进行以下运算:
加一个整数,如:p+1;
减一个整数,如:p-1;
自加运算,如:p++,++p;
自减运算,如:p--,--p。
两个指针相减,如:p1-p2(只有p1和p2都指向同一数组的元素时才有意义)。
(3)通过指针引用数组元素
引用一个数组元素,可以用以下两个方法:
①下标法。如:a[i];
②指针法。如:*(p+i)或*(a+i).其中a为数组名,p是指向数组元素的指针变量。
(4)用数组名做参数
可以用数组名做参数,如:
void fun(int s[],int n);
注:
实参数组名代表的是一个固定的地址,或者说是指针常量,形参数组名并不是一个固定的地址,而是按指针变量处理。
(5)通过指针引用多维数组
与二维数组有关的指针:
a //二维数组名,指向一维数组a[0]的地址,即0行起始地址
a[0],*(a+0),*a //0行0列的元素地址
a+1,*(a+1) //1行0列元素a[1][0]的地址
a+1,&a[1] //1行起始地址
a[1]+2,*(a+1)+2 //1行2列元素a[1][2]的地址
*(a[1]+2),*(*(a+1)+2),a[1][2] //1行2列的元素a[1][2]的值
4.通过指针引用字符串
(1)字符串的引用方式
可以通过数组名和下标引用字符串中的一个字符,也可以用数组名和格式声明“%s”输出字符串。如:
printf("%s",s);
printf("%c",s[2]);
注:
在C语言中,只有字符变量,没有字符串变量。
对于 char *string="I love china!";
等价于:
char *string; //定义一个char *类型变量
string="I love china!"; //把字符串第一个元素的地址赋予字符指针变量string
再用“%s”来输出这个字符串时,系统会先输出"I love china!"中的第一个字符,然后使string自动加1,使之指向下一个字符,在输出该字符,直到遇到字符串结束标志“\0”结束。
(2)字符指针做函数参数
如果想把一个字符串从一个函数传递到另一个函数,可以用地址传递的方式,即用字符数组名做参数,也可以用字符指针变量作参数。
(3)使用字符指针变量和字符数组的比较
①字符数组有若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的的是地址(字符串第一个字符的地址),绝不是将字符串放到字符指针变量中。
②赋值方式。可以对字符指针变量赋值,但不能对数组名赋值。如:
char *string;
string="I love china!"; //将字符串首元素地址赋给指针变量,合法。
这样是合法的;
char str[14];
str[0]='I'; //对字符数组元素赋值,合法
str="I love china!"; //数组名是地址,是常量,不能被赋值
③初始化的含义:
对字符指针变量赋值:
char *a="I love china!"; //定义字符指针变量a,并把字符串的第一个元素地址赋给a
等价于:
char *a;
a="I love china!";
而对数组的初始化:
char str[14]="I love china!"; //定义字符数组str,并把字符串赋给数组中的个元素
不等价于:
char str[14]; //定义字符数组str
str[]="I love china!"; //企图把字符串赋给数组中个元素,错误
数组可以在定义时对个元素赋初值,但不能用赋值语句对字符数组中的全部元素进行整体赋值。
④存储单元的内容。编译时为字符数组分配若干个存储单元,以存放个元素的值,而对字符指针变量,只分配一个存储单元。
⑤指针变量的值是可以改变的,而字符数组名各代表一个固定的值(数组首元素的地址),不能改变。
⑥字符数组中个元素值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可再取代的(不能对他们再赋值)。如:
char a[]="House"; //字符数组的初始化
char *b="House"; //字符指针变量b指向字符串常量的第一个字符
a[2]='r'; //合法,r取代a数组元素a[2]的原值u
b[2]='r'; //非法,字符串常量不能改变
⑦用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。如:
char *s;
s="a=%d,b=%f";
printf(s,a,b);
等价于:
printf("a=%d,b=%f",a,b);
因此只要改变指针变量s所指向的字符串,就可以改变输入输出格式。这种printf函数称为可变格式输出函数。
5.指向函数的指针
(1)什么是函数的指针
如果在程序中定义了一个函数,在编译时会把函数的源代码转换为可执行代码并分配一段存储空间,这段内存空间有一个起始地址,也称为函数的入口地址。每次调用函数时都从该地址入口开始执行此段函数代码。函数名代表函数的起始地址。调用函数时,从函数名得到函数的起始地址,并执行函数代码。
函数名就是函数的指针,代表函数的起始地址。
可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。如:
int (*p)(int,int);
定义p是一个指向函数的指针变量,它可以指向函数类型为整型且有两个整型参数的函数。
(2)定义和使用指向函数的指针变量
定义指向函数的指针变量的一般形式为:
类型名 (*指针变量名)(函数参数表列);
注:对指向函数的指针变量不能进行算数运算,如:p+n,p++等是无意义的。
(3)用指向函数的指针做函数参数
指向函数的指针变量的一个重要的用途是把函数的入口地址作为函数的参数传到其他函数。
指向函数的指针可以作为函数参数,把函数的入口地址传给形参,这样就能够在被调用的函数中使用实参参数。它的原理可以简述如下:有一个函数(假设函数名为fun),它有两个形参(x1,x2),定义x1,x2为指向函数的指针变量。在戴奥用函数fun时,实参为两个函数名f1,f2,给形参传递的是函数f1,f2的入口地址。这样在函数fun中就可以调用f1和f2函数了。如:
void fun(int (*x1)(int),int (*x2)(int,int)) //定义fun函数,形参是指向函数的指针变量
{
int a,b,i=3,j=5;
a=(*x1)(i); //调用f1函数,i是实参
b=(*x2)(i,j); //调用f2函数,i,j是实参
}
举一个例子:
有两个整数a和b,由用户输入1,2,3.如输入1,程序就给出a和b中的最大者,输入2,就给出a和b中的最小者,输入3,就求a和b的和。
代码如下:
#include<stdio.h>
int main()
{
int fun(int x,int y,int (*p)(int,int));
int max(int,int);
int min(int,int);
int add(int,int);
int a=34,b=-21,n;
scanf("%d",&n);
if(n==1) fun(a,b,max);
else if(n==2) fun(a,b,min);
else if(n==3) fun(a,b,add);
return 0;
}
int fun(int x,int y,int (*p)(int,int))
{
int s;
s=(*p)(x,y);
printf("%d",s);
}
int max(int x,int y)
{
int z;
if(x>y) z=x;
else z=y;
printf("max=");
return z;
}
int min(int x,int y)
{
int z;
if(x>y) z=y;
else z=x;
printf("min=");
return z;
}
int add(int x,int y)
{
int z;
z=x+y;
printf("sum=");
return z;
}
6.返回指针值的函数
定义返回指针值的函数的原型的一般形式为:
类型名 *函数名(参数表列);
7.指针数组和多重指针
一个数组,若其元素均为指针型数据,称为指针数组。也就是说,指针数组中的每一个元素都存放了一个地址,相当于一个指针变量。
定义一维指针数组的一般形式为:
类型名 *数组名[数组长度];
8.内存动态分配与指向它的指针变量
用malloc函数来开辟动态存储区:
函数原型: void *malloc(unsigned int size);
其作用是在内存的动态存储区内分配1个长度为size长度的连续空间。
用calloc函数来开辟动态存储区:
函数原型:void *calloc(unsigned n,unsigned size);
其作用是在内存中分配n个长度为size的连续空间。
用realloc函数来重新分配动态存储区:
函数原型:void *realloc(void *p,unsigned int size);
将已分配的动态存储空间重新新分配。
用free函数释放动态存储区:
函数原型:void free(void *p);
其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。