C程序设计语言--指针与数组(二) 指针数组 数组指针 函数指针 指针函数
关于指针数组、数组指针、指针函数、函数指针、二级指针的解释
一、指针函数:
指针函数是指带指针的函数,即本质是一个函数。我们知道函数都有返回类型(如果不返回值,则为无值型),只不过指针函数返回类型是某一类型的指针。其定义格式如下所示:
返回类型标识符 *返回名称(形式参数表)
{ 函数体 }
返回类型可以是任何基本类型和复合类型。返回指针的函数的用途十分广泛。事实上,每一个函数,即使它不带有返回某种类型的指针,它本身都有一个入口地址,该地址相当于一个指针。比如函数返回一个整型值,实际上也相当于返回一个指针变量的值,不过这时的变量是函数本身而已,而整个函数相当于一个”变量”。例如下面一个返回指针函数的例子:
float *find(float (*pionter)[4], int n) /* 定义指针函数 */ { float *pt; pt = *(pionter + n); return(pt); } main() { static float score[][4] = { {60, 70, 80, 90}, {56, 89, 34, 45}, {34, 23, 56, 45}}; float *p; int i, m; printf("Enter the number to be found:"); scanf("%d", &m); printf("the score of NO.%d are:\n", m); p = find(score, m); for (i = 0; i < 4; i++) { printf("%5.2f\t", *(p + i)); } }
从这段代码中也可以发现,对于二维数组在函数参数的传递的过程中的样子,
学生学号从0号算起,函数find()被定义为指针函数,起形参pointer是指针指向包含4个元素的一维数组的指针变量。pointer + 1指向score的第一行。*(pointer + 1)指向第一行的第0个元素。pt是一个指针变量,它指向浮点型变量。main()函数中调用find()函数,将score数组的首地址传给pointer.
二、函数指针
“函数指针”是指向函数的指针变量,因而”函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上一致的。函数指针有两个用途:调用函数和做函数的参数。函数指针的说明方法为:
函数类型 (*指针变量名)(形参列表);
“函数类型”说明函数的返回类型,由于”()”的优先级高于”*“, 所以指针变量名外的括号必不可少,后面的”形参列表”表示指针变量指向的函数所带的参数列表。
例如:
int (*f)(int x);
double(*ptr)(double x);
在定义函数指针时请注意:
函数指针和它指向的函数的参数个数和类型都应该是—致的;
函数指针的类型和函数的返回值类型也必须是一致的。
函数指针的赋值
函数名和数组名一样代表了函数代码的首地址,因此在赋值时,直接将函数指针指向函数名就行了。
例如,
int func(int x); /* 声明一个函数 */ int (*f)(int x); /* 声明一个函数指针 */ f = func; /* 将func函数的首地址赋给指针f */
赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。
与其他指针变量相类似,如果指针变量pi是指向某整型变量i的指针,则*p等于它所指的变量i;如果pf是指向某浮点型变量f的指针,则*pf就等价于它所指的变量f。同样地,*f是指向函数func(x)的指针,则*f就代表它所指向的函数func。所以在执行了f = func;
之后,(*f)和func代表同一函数。
由于函数指针指向存储区中的某个函数,因此可以通过函数指针调用相应的函数。现在我们就讨论如何用函数指针调用函数,它应执行下面三步:
首先,要说明函数指针变量。
例如:int (*f)(int x);
其次,要对函数指针变量赋值。
例如: f = func;
(func(x)必须先要有定义)
最后,要用(*指针变量)(参数表);
调用函数。
例如:(*f)(x);
(x必须先赋值)
int max(int x, int y) { return(x > y ? x : y); } int main(void) { int a, b, c; int (*ptr)(int, int); scanf("%d,%d", &a, &b); ptr = max; c = (*ptr)(a, b); printf("a=%d,b=%d,max=%d", a, b, c); return 0; }
就相当于一个变量使用即可。
三、函数指针数组
关于函数指针数组的定义
分析:函数指针数组是一个其元素是函数指针的数组。那么也就是说,此数据结构是是一个数组,且其元素是一个指向函数入口地址的指针。
根据分析:首先说明是一个数组:数组名[]
其次,要说明其元素的数据类型指针:
*数组名[].
再次,要明确这每一个数组元素是指向函数入口地址的指针:函数返回值类型(*数组名[])().请注意,这里为什么要把”*数组名[]“用括号扩起来呢?因为圆括号和数组说明符的优先级是等同的(关于这几个的优先级在上篇文章已经介绍),如果不用圆括号把指针数组说明表达式扩起来,根据圆括号和方括号的结合方向,那么 *数组名[]() 说明的是什么呢?是元素返回值类型为指针的函数数组。有这样的函数数组吗?不知道。所以必须括起来,以保证数组的每一个元素是指针。
#include "stdio.h" int add1(int a1, int b1); int add2(int a2, int b2); int main(int argc, char *argv[]) { int numa1 = 1, numb1 = 2; int numa2 = 2, numb2 = 3; int (*op[2])(int a, int b); op[0] = add1; op[1] = add2; printf("%d %d\n", op[0](numa1, numb1), op[1](numa2, numb2)); getch(); } int add1(int a1, int b1) { return a1 + b1; } int add2(int a2, int b2) { return a2 + b2; }
最后再给几个猛料:
再给出常用的C变量的定义方式:
a) 一个整型数(An integer)
b) 一个指向整型数的指针(A pointer to an integer)
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)
d) 一个有10个整型数的数组(An array of 10 integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer)
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
当然还有更加复杂的组合,我觉得在使用过程中,这些已经够用了。
四、指针数组和数组指针
指针数组:array of pointers,即用于存储指针的数组,也就是数组元素都是指针
数组指针:a pointer to an array,即指向数组的指针
int* a[4] 指针数组(这是一个数组,只不过由多个指针组成的数组)
表示:数组a中的元素都为int型指针
元素表示:*a[i] *(a[i])是一样的,因为[]优先级高于*
int (*a)[4] 数组指针(这是一个指针,只不过这个指针指向了一个数组,数组中有4个元素)
表示:指向数组a的指针
元素表示:(*a)[i]
对于指针数组,在使用过程中,经常的定义:
typedef int* pInt;
pInt a[4];
举例说明一个:
#include <iostream>
using namespace std;
int main()
{
int c[4]={1,2,3,4};
int *a[4]; //指针数组
int (*b)[4]; //数组指针
b=&c; //注意这一行 体味
//将数组c中元素赋给数组a
for(int i=0;i<4;i++)
{
a[i]=&c[i];
}
//输出看下结果
cout<<*a[1]<<endl; //输出2就对
cout<<(*b)[2]<<endl; //输出3就对
return 0;
}
这也就是为啥在二维指针传递函数参数是必需是数组指针,因为数组指针是指向一个数组的指针。
注意:定义了数组指针,该指针指向这个数组的首地址,必须给指针指定一个地址,容易犯的错得就是,不给b地址,直接用(*b)[i]=c[i]给数组b中元素赋值,这时数组指针不知道指向哪里,调试时可能没错,但运行时肯定出现问题,使用指针时要注意这个问题。但为什么a就不用给他地址呢,a的元素是指针,实际上for循环内已经给数组a中元素指定地址了。但若在for循环内写*a[i]=c[i],这同样会出问题。总之一句话,定义了指针一定要知道指针指向哪里。
现在看看二级指针是不是指针数组!!!!
五、二级指针(转载)
#include <iostream.h>
void main()
{
int a[2][3];
int**p=a;
}
请问为什么是错误的?
其实这个数组与指针的问题,要写的话,一句话,两句话是将不清楚的。 首先数组和指针的概念你没分清楚,数组的本质你没搞清楚。这是导致问题出现的根源。 int x[5]; 这个定义里面,我们说定义了一个数组x,此数组有5个数组元素,元素的类型为int类型。首先要问的是,x到底为什么东西? 我知道,在谭浩强的书上面说x是数组名,x代表了数组第一个元素的首地址。没错,x确实是数组的名字,x的值也确实是第一个数组元素的地址值。注意这里我们说x代表的值与数组第一个元素的地址值相等,但是并不是说他们的类型是一样的。那么x的类型到底是什么呢? 有人说就是int * 类型。有如下语句可以做证: int *p=x; //这句话是正确的。 x的类型真是int *吗,我们说不是,因为下面的语句是不正确的: int a=10; x=&a; // int *类型的变量是可以接受值的。所以x不是int* 那么我们可以猜测x的类型是不是 int *const呢。也就是说x是一个地址值不可以改变的指针。这句话貌似有点正确。但是请大家看看下面的例子: int x[5]={0}; int a=sizeof(x); // a的值到底是多少?实际上这里a的值是5*4=20 我这里使用的编译器是VC++ 6.0 int类型数据占用4个字节空间,所以这里得到的是整个数组占用的字节数。 我们不是说x的类型是iint * const类型的吗,也就是x应该是一个指针类型,应该是4个字节的啊,为什么sizeof出来是整个数组占用的字节数呢。例如 sizeof(int *)这个的结果就是4。所以由此可以看出,x的类型并不是int*,也不是int * const。 int x[5];中的x到底是什么呢,我们说x是数组,此数组有5个元素,并且每个元素都是int类型。 我们有一个识别数据类型的规律例如: int x; //x类型为int int *x;//x类型为int * int **x;//x类型为int ** int (*x)[10];//x类型为int(*)[10]实际上是指向数组的指针 int (*x)(int ,int);//x的类型为int(*)(int,int)实际上是指向函数的指针 由此可以看出,一个符号是什么数据类型,我们只要在其定义的表达式中去掉符号本身,剩下的就是符号的类型了。照此推断,int x[5];中x的类型应该是 int [5]这个类型,可以看出此类型并不是int *类型。 那么int x[5];中的x可以这样赋值: int *p=x; 为什么呢,只能说这里面将x的类型隐式转换为了int *类型。所以这里是可以赋值的,因为进行了类型转换。 再请看下面的例子: void function(int x[5]) { cout<<sizeof(x)<<endl; //这里输出4 } 为什么会输出4,而不是4*5呢,可以看出上面的函数形参实际上类型是int*,并不是数组类型,所以我们在定义函数的时候,下面的都是与上面等价的: void function(int x[])//元素个数是多少可以省略 { cout<<sizeof(x)<<endl; //这里输出4 } void function(int *x) //直接写成指针变量也没错 { cout<<sizeof(x)<<endl; //这里输出4 } 他们都是等价的。 那么我们看一个类似的问题: int x[5]; int **p=&x; //为什么会报错? 因为类型不匹配。 p的类型是int **,而&x的类型却不是int **。 &x的类型实际上是int(*)[5],因为取的是x的地址,也就是说这个地址是数组的地址,并不是指向数组第一个元素的指针的指针(也就是二维指针),而是整个数组的地址。所以我们可以改成下面的: int (*p)[5]=&x;//这就对了。 指向数组的指针,和指向数组元素的指针有什么不同? 我们说对于一个指针变量,有几点是我们必须注意的,例如int *p;我们要注意的是,p的类型是int*,p占用的空间是4个字节,p指向的数据类型是int。p指向的数据类型占用4个字节。所以对于指针变量,我们要明白指针变量本身是占用空间的,本身是有类型的,其次指针变量所指向的空间是有类型的,是有空间的。 那么int *p; char *p1; 对于指针变量来说p,p1里面都放的是地址值,说白了就是一个数值,他们都占用4个字节的空间,但是他们的类型不一样,p里面的地址指向的是int类型的数据,p1指向的是char类型的数据,这主要体现在p++与p1++中他们在内存中移动的字节数是不一样的,我们假设int占4个字节,char占1个字节。那么对于p来说向前移动了4个字节,p1来说移动了一个字节。这就是他们的类型不同,导致运算过程的不同。 int x[5]; int (*p3)[5]; 此时p3指向数组x,那么p3++实际上向前移动了多少呢,可以算出移动了4*5个字节。也就是p3指向的是一个数组,是整个数组,所以p3移动的时候是将一个数组当做一个整体来看待的。所以向前移动了一整个数组的距离。 在看你的问题之前,我们来看一个类似的问题: int a[2][3]; int**p=&a; //这里我用&a来赋值行不行呢。是不行的。 这里为什么是错误的,原因就是因为&a的类型不是int**类型。所以类型不兼容,导致不能赋值,同时这两种类型是不可以相互转换的。 那么&a到底是一个什么样的类型呢。 我们说&a取的是整个数组的地址,那么&a自然就是指向整个数组的指针了。 int (*p)[2][3]=&a; 此时这样赋值才是正确的。如果我们要用a直接赋值,那该定义一个什么样的变量来接受它呢,首先要明白,数组名代表的地址类型是指向数组的第一个元素的指针,例如: int a[10]; int *p=a; 实际上这里与 int *p=&a[0];是等价的。因为指向a[0]的指针类型就是int*类型。 那么&a的是取数组的地址,其类型是指向数组的指针,而不是指向数组第一个元素的指针,这个是有区别的,他们的类型就不一样。 int(*p)[10]=&a; 所以说这里的a和&a绝对不是同一个东西,虽然本质上他们的地址值是一样的,但是他们的类型不一样。就决定他们代表不同的意义。 那么刚刚说了对于下面的例子: int a[2][3]; int (*p)[2][3]=&a;//我们可以定义这样的一个变量p来接受&a的值。 那么我们要接受a应该定义一个什么样的变量呢。a[2][3]是一个二维数组,可以这样看:a是一个数组,具有两个元素,分别为a[0],a[1]其中这两个元素的值a[0],a[1]他们的值又是一个具有3个元素的数组。此时我们可以将a[0],a[1]看成是数组名,那么a[0][0]就是数组a[0]的第0个元素了。对应关系如下: a[0] ----> a[0][0],a[0][1],a[0][2] a[1] ----> a[1][0],a[1][1],a[1][2] 那么a到底是什么,其实a数组有两个元素,a[0],a[1],那么a的值自然就是其第一个元素的地址了,也就是&a[0]了。这是一个什么类型? 我们知道如果我们将a[0]看成一个整体,例如我们用A来代替a[0],那么A[0],A[1]就相当于a[0][0],a[0][1] 。 此时A就是一个int类型的数组,&A,的类型实际上就是 int(*p)[3]这个类型。 所以下面的代码也是正确的: int a[2][3]; int(*p)[3]=a; //所以对于你的问题,可以这样子。。
但是下面的转化是合理的:
char *a[];
char **p=a;
http://www.cnblogs.com/dzry/archive/2011/05/12/2044835.html
其实第五部分讲述的还是很深刻的,不过这些内容在《C专家编程》上有详细的介绍。只不过没有总结,没有归纳,看了文章之后才想起来这些,总结!!!!!
还是拿些实例说明下关于指针的东西,这样记忆更加深刻!我又开始重新学习语言了!
还没有评论,来说两句吧...