C语言中函数指针*p **p 和数组不是很明白,可以解释下麽?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言中函数指针*p **p 和数组不是很明白,可以解释下麽?相关的知识,希望对你有一定的参考价值。

定义一个指向函数的指针用如下的形式,以上面的test()为例: int (*fp)(int a);//这里就定义了一个指向函数的指针 函数指针不能绝对不能指向不同类型,或者是带不同形参的函数,在定义函数指针的时候我们很容易犯如下的错误。 int *fp(int a);//这里是错误的,因为按照结合性和优先级来看就是先和()结合,然后变成了一个返回整形指针的函数了,而不是函数指针,这一点尤其需要注意! 下面我们来看一个具体的例子: #include <iostream> #include <string> using namespace std; int test(int a); void main(int argc,char* argv[]) cout<<test<<endl;//显示函数地址 int (*fp)(int a); fp=test;//将函数test的地址赋给函数学指针fp cout<<fp(5)<<"|"<<(*fp)(10)<<endl; //上面的输出fp(5),这是标准c++的写法,(*fp)(10)这是兼容c语言的标准写法,两种同意,但注意区分,避免写的程序产生移植性问题! cin.get(); int test(int a) return a; typedef定义可以简化函数指针的定义,在定义一个的时候感觉不出来,但定义多了就知道方便了,上面的代码改写成如下的形式: #include <iostream> #include <string> using namespace std; int test(int a); void main(int argc,char* argv[]) cout<<test<<endl; typedef int (*fp)(int a);//注意,这里不是生命函数指针,而是定义一个函数指针的类型,这个类型是自己定义的,类型名为fp fp fpi;//这里利用自己定义的类型名fp定义了一个fpi的函数指针! fpi=test; cout<<fpi(5)<<"|"<<(*fpi)(10)<<endl; cin.get(); int test(int a) return a; 考虑到指针运算的特殊性,可以用数组指针的方法表示数组元素。 例如,定义了一个长度为10的一维整型数组num[10],其第i个元素既可以用下标法表示为“num[i]”,亦可以用指针法表示为“*(num+i)”。 运用指针法的好处是占用内存小,程序运行速度快。 参考技术A *是取值的意思 &是取地址 **是二级指针 像 type a type *p=&a type *c=p *c是一级指针 取到的是p的值 **c是二级指针 取到的是p的这个地址里指向的地址的值,也就是p里放的上a的地址,最终于拿到a地址里的值本回答被提问者采纳

C语言—指针进阶

文章目录

指针

首先看一下指针最基本的用法

#include<stdio.h>
int main()
	char c = 'w';
	char* p = &c;
	printf("%c\\n", *p);

	*p = 'c';
	printf("%c\\n", *p);
	return 0;


看这段代码,这是对指针一个非常简单的应用,让指针p存放字符变量c的地址,通过对指针p解引用来访问变量c中存放的值。

指针数组和数组指针

首先搞清楚一个概念
指针数组指针 还是 数组??
答:是数组

数组指针数组 还是 指针??
答:是指针

要分清楚这个概念,其实只要关注这词最后的定语是什么,后面是数组,它就是一个数组,后面是指针,它就是一个指针。

下面来具体看一看,他们该如何定义和使用。

指针数组

指针数组这个概念比较好理解,我们已经学过整型数组,字符型数组。指针数组本质上就是一个数组,只不过,它的每个单元格里面存放的都是一个指针。
下面来看指针数组的定义

#include<stdio.h>
int main()
	char* ch[3] =  "hello", "world", "!" ;
	printf("%s ", ch[0]);
	printf("%s ", ch[1]);
	printf("%s ", ch[2]);
	return 0;

在这个指针数组中,有三个单元,分别存放了指向常量字符串"hello", “world”, "!"的指针,通过ch[数组下标]的方式,可以去调用其中的值。
下面看它的运行结果:

数组指针

我们说指针数组,是一个存放指针类型变量的数组。那数组指针又是什么含意呢?
定义:数组指针是一个指向整个数组的指针。
是的,你没有看错,它指向的是整个数组。
这里可能会有一个小误区,又可能会不理解什么叫做 指向整个数组,我们知道,数组名代表的只是数组首元素的地址,那我们该如何给这个数组指针赋上整个数组的地址呢? 而且一个数组有那么多的元素,那对这个指针解引用的时候,它会调用哪个元素的值呢?
下面我们来看实际代码:

比较 &数组名 和 数组名 之间的区别

	int num[5] =  1, 2, 3, 4, 5 ;
	printf("%p\\n", num);
	printf("%p\\n", &num);

我们首先定义一个一维数组,然后分别以地址的形式,打印num 和 &num 的址

我们发现,这两个值 ,居然是一样的,这是否表明 num 和 &num 是一个意思呢?
下面再来看:

	int num[5] =  1, 2, 3, 4, 5 ;
	printf("%p\\n", num);
	printf("%p\\n", &num);
	printf("------------\\n");
	printf("%p\\n", num + 1);
	printf("%p\\n", &num + 1);

在这里,我们保留了之前的代码,然后,输出num 和 &num 加1的结果,与之前进行对比


(注意:这里读者可能发现,num 和 &num 的值 与之前所得到的值不同,这是因为,我们在这里输出的是数组的地址,每次程序运行结束,都会销毁其中的数据,然后,下次运行时,再创建,所以系统为这个数组分配的地址可能与上次分配的不在一个地方,但是,num 和 &num 的值仍然相等,这个是不会变的。)

这里我们看到,num 和 &num 本来值是相同的,但在加1操作之后,居然指向了不同的地址,这是为什么?
为了了解这个问题,首先要清楚,地址加1代表了什么?是代表数值上的加1吗?显然不是,实际上地址加1,代表的是加上一个类型的大小。
明白了这个,就不难理解,num+1 为什么和 &num+1 的值不同了,因为,num代表的是数组首元素的地址,一个int型的元素,加1之后,加了四个字节,而&num代表的是整个数组的地址,&num+1,加的是整个数组的大小,也就是20个字节。所以我们看到,后面的两个数,之间差了16;
(注意:004FFB6C 和 004FFB7C之间差的是16,不是10,因为这是16进制表示的。)

明白了这个原理,我们就可以为我们的数组指针进行赋值,当然,赋的就是 (&数组名)这种形式了。
下面举一个例子:

#include<stdio.h>
int main()
	int num[5] =  1, 2, 3, 4, 5 ;
	int(*p)[5] = &num;
	
	printf("%p\\n", &num);
	printf("%p\\n", *p);
	
	return 0;

这里我们定义了一个数组指针p,让它接收 数组num 的地址,在赋值完之后,我们输出&num 和 *p 的值,看看变量p 是否成功接收。

我们看到,p已经接收了这个数组的地址。
接下来,我们分析,如果对整个数组的地址进行解引用的话,会出现那个元素的值?

#include<stdio.h>
int main()
	int num[5] =  1, 2, 3, 4, 5 ;
	int(*p)[5] = &num;
	
	printf("%p\\n", &num);
	printf("%p\\n", *p);
	printf("----------\\n");
	
	printf("%d\\n", **p);
	return 0;

这里我们打印 **p 来访问其中的值 发现给出的不是 数组中随机一个的值,也不是整个数组的值都打印一遍,而是打印了数组首个元素的值,所以,得出结论,对数组的地址解引用得到的是数组首个元素的值,这也不难理解,因为数组的地址,本来就等于数组首元素的地址(在值上相等,但意义不同)

下面来看一个关于数组指针的实际应用:

设计一个函数,打印二维数组的值

#include<stdio.h>

void print(int(*p)[3], int row, int col)

	for (int i = 0; i < row; i++)
	
		for (int j = 0; j < col; j++)
		
			printf("%d ", p[i][j]);
		
		printf("\\n");
	


int main()

	int num[2][3] =  1, 2, 3, 4, 5, 6 ;
	
	print(num, 2, 3);
	return 0;

在这串代码中,我们实现了一个打印二维数组的函数,向print函数中,传入数组num,因为是一个二维数组,而num数组名代表了数组首元素的地址,num代表了第一行数组的地址,所以我们可以用一个指向数组的指针来接收这个地址。在函数中,我们仍然使用二维数组的形式去使用它,可以这么去理解
【p[1][2]就相当于,* ( (p+1)+2 ) p指向的是第一行数组,加1之后指向了第二行,也就是数组的第1行,然后解引用,(p+1)就相当于一个指向第二行首元素的地址,给这个地址,加2,也就是第二行的第三个元素,也就是num[1][2]。】

函数指针

函数指针,顾名思义,就是指向一个函数的指针。
这里我们引用一下函数指针的官方定义:
如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。

从这段定义中,我们可以知道,函数在编译的时候,系统会为其分配空间,而函数指针,就是存放这段空间地址的一个指针变量。

下面来看一下,如何去定义一个函数指针:

#include<stdio.h>

void print(int(*p)[3], int row, int col)

	for (int i = 0; i < row; i++)
	
		for (int j = 0; j < col; j++)
		
			printf("%d ", p[i][j]);
		
		printf("\\n");
	

int main()

	int num[2][3] =  1, 2, 3, 4, 5, 6 ;
	void(*pfunction)(int(*)[3], int, int) = &print;

	(*pfunction)(num, 2, 3);
	return 0;


可以看到,这段代码同样能够实现打印二维数组,也就说明,我们通过对函数指针的调用,同样可以去使用这个函数的功能。
但是,我们在实际使用函数指针的时候,并不是只是这样定义一个指针然后直接调用,一般,函数指针是用在 回调函数 当中的。

回调函数

什么是回调函数呢?
回调函数 英文名(call back function)当一个函数的参数也是一个函数的时候,这个函数就被称为,回调函数,那现在我们知道,要想传递一个函数的地址,就一定要使用到函数指针进行传参。

下面来看一个例子:

#include<stdio.h>
void print(int a)

	printf("%d\\n", a);


void test(void (*pfunction)(int ))

	(*pfunction)(3);


int main()
	void(*pfunction)(int) = &print;
	
	test(pfunction);
	return 0;

这是一个简单的使用回调函数的例子,在这个例子中,我们的test函数中,并没有打印语句,打印语句在print函数中,所以我们首先定义了一个函数指针,让它指向函数print,然后调用test,向其中传递了函数print的地址,然后再test中调用print函数,从而达到打印数字3的目的。

这里有一个有意思的小问题,那就是,一个函数可以回调自身吗?也就是函数的参数,就是函数本身,这样的代码能实现吗?
答案是,不能。因为,函数再调用的时候,系统都会为函数分配一定的空间,如果函数回调自身的话,那么程序就会无休止的一直陷入函数调用中,系统一直为调用的函数分配空间,最终造成栈溢出,程序崩溃。

函数指针数组

函数指针数组与指针数组类似,本质上都是数组,其中的单元格存放的也都是地址,只不过函数指针数组当中存放的是各个函数的地址。
下面来看,函数指针数组该如何定义:

#include<stdio.h>
void add(int a, int b)

	printf("%d\\n", a + b);


void del(int a, int b)

	printf("%d\\n", a - b);

int main()

	void(*p[5])(int, int) =  &add ,&del;
	
	(*p[0])(1, 3);
	(*p[1])(3, 1);
	
	return 0;


在这段代码中,我们先定义了 add 和 del 两个函数,然后,在主函数中,我们没有直接调用它们,而是,通过定义了一个函数指针数组p,数组p的大小为5,其中存放的是指针,每个指针都是指向一个返回类型为 void ,并且有两个int型变量作为参数的函数。我们将函数add和del的地址放进去,然后通过访问数组的方法去访问它,看到仍然能够显示出正确的答案。

函数指针数组用途有它的局限性,也就是,放在函数指针数组当中的地址,对应的函数,必须有相同的返回类型,也必须有相同的参数,所以并不经常使用。

指向函数指针数组的指针

这种类型的指针使用频率更少,它也是一个指针,指向的是 存放函数指针的数组。
下面来看,借用上面的例子,如何定义一个指向函数指针数组 p 的指针:

#include<stdio.h>
void add(int a, int b)

	printf("%d\\n", a + b);


void del(int a, int b)

	printf("%d\\n", a - b);

int main()
	void(*p[5])(int, int) =  &add ,&del;
	(*p[0])(1, 3);
	(*p[1])(3, 1);
	printf("----------\\n");
	
	void(*(*pp)[5])(int, int) = &p;
	(**pp[0])(1, 3);
	
	return 0;


在这里,我们定义了指向函数指针数组p的指针pp,然后通过pp对函数进行调用,也是可以的。但是,可以看出,我们嵌套了这么多的指针和数组,实现的仍然是本来简单的函数 add 和del的功能,但是,我们失去了原本代码非常重要的 可读性和可维护性。所以,在实际操作中,我们不需要写出,如此复杂难懂的代码,只需完成相应的功能即可。

以上是关于C语言中函数指针*p **p 和数组不是很明白,可以解释下麽?的主要内容,如果未能解决你的问题,请参考以下文章

C语言data->time = c该怎么解释,我对->符号弄的不是很明白

C语言中的二维数组名是一个二重指针吗?

C语言中如何显示指针所指向的数

c语言如何给指针参数赋值为null?

c语言malloc函数

C语言如何定义指针指向字符型二维数组