先看一道经典的C语言题:用变量a给出下面的定义
a) 一个整型数(An integer)
b)一个指向整型数的指针( A pointer to an integer)
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r
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;
// b)一个指向整型数的指针
int *a;
// c)一个指向指针的的指针(二级指针),它指向的指针是指向一个整型数
int **a;
// d)一个有10个整型数的数组
int a[10];
// e) 一个有10个指针的数组,该指针是指向一个整型数的
int *a[10];
// f) 一个指向有10个整型数数组的指针
int (*a)[10];
// g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
int (*a)(int);
// h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
int (*a[10])(int);
如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。
当函数参数为指针,我们需要动态的改变指针指向的地址(即传入不同指针)时(不使用引用),当传入的参数是二级指针变量的地址,对一级指针进行修改,则相当于对原指针地址进行了修改,
1、二级指针作为参数传递,可以实现改变参数指针指向的位置。
int* b = new int(2);
void Func(int** num){
*num = b;
}
int main(){
int* a = new int(1);
Func(&a);
cout<<*a<<endl;
}
2、二级指针可用于内存分配。
void my_malloc(char **s) {
*s=(char*)malloc(100);
}
void main(){
char *p=NULL;
my_malloc(&p);
if(p)
free(p);
}
#include
int main(){
int p[3]={10,20,30};
int *p1[3];
int (*p2)[3];
p1[0]=&p[0];
p1[1]=&p[1];
p1[2]=&p[2];
printf("*p1[0] = %d, *p1[1] = %d, *p1[2] = %d\n", *p1[0], *p1[1], *p1[2]);
printf("p1[0] = %#X, p1[1] = %#X, p1[2] = %#X\n", p1[0], p1[1], p1[2]);
printf("&p1[0] = %#X, &p1[1] = %#X, &p1[2] = %#X\n", &p1[0], &p1[1], &p1[2]);
p2=&p;//p2=p;会出现 warning,但不影响结果
printf("(*p2)[0] = %d, (*p2)[1] = %d, (*p2)[2] = %d\n", (*p2)[0], (*p2)[1], (*p2)[2]);
printf("&((*p2)[0]) = %#X, &((*p2)[1]) = %#X, &((*p2)[2]) = %#X\n",
&((*p2)[0]), &((*p2)[0]), &((*p2)[0]));
printf("p = %#X, p1 = %#X, p2 = %#X\n", p, p1, p2);
printf("*p = %#X, *p1 = %#X, *p2 = %#X\n", *p, *p1, *p2);
printf("**p1 = %d, **p2 = %d\n", **p1, **p2);
printf("sizeof(p) = %d, sizeof(p1) = %d, sizeof(p2) = %d\n", sizeof(p),
sizeof(p1), sizeof(p2));
return 0;
}
/*输出
*p1[0] = 10, *p1[1] = 20, *p1[2] = 30
p1[0] = 0X61FE0C, p1[1] = 0X61FE10, p1[2] = 0X61FE14
&p1[0] = 0X61FDF0, &p1[1] = 0X61FDF8, &p1[2] = 0X61FE00
(*p2)[0] = 10, (*p2)[1] = 20, (*p2)[2] = 30
&((*p2)[0]) = 0X61FE0C, &((*p2)[1]) = 0X61FE0C, &((*p2)[2]) = 0X61FE0C
p = 0X61FE0C, p1 = 0X61FDF0, p2 = 0X61FE0C
*p = 0XA, *p1 = 0X61FE0C, *p2 = 0X61FE0C
**p1 = 10, **p2 = 10
sizeof(p) = 12, sizeof(p1) = 24, sizeof(p2) = 8
*/
指针数组
对于语句“intp1[3]”,因为“[]”的优先级要比“”要高,所以 p1 先与“[]”结合,构成一个数组的定义,数组名为 p1,而“int*”修饰的是数组的内容,即数组的每个元素。也就是说,该数组包含 3 个指向 int 类型数据的指针,因此,它是一个指针数组。
数组指针
对于语句“int(p2)[3]”,“()”的优先级比“[]”高,“”号和 p2 构成一个指针的定义,指针变量名为 p2,而 int 修饰的是数组的内容,即数组的每个元素。也就是说,p2 是一个指针,它指向一个包含3个 int 类型数据的数组。很显然,它是一个数组指针,数组在这里并没有名字,是个匿名数组。
数组指针与数组名的关系
p2=&p;//p2=p;会出现 warning,但不影响结果。(有些编译器直接报错)
在这里&p 是指整个数组的首地址,而 arr仅仅是数组首元素的首地址。赋值符号“=”号两边的数据类型不同(正常必须相同),则需要显示或隐式类型转换,因此发生警告可以运行。
函数指针: 一个指向函数的指针。一般用函数名表示。区别于返回值为指针的函数。
函数指针数组:元素为函数指针的数组。转移表。c语言中函数不可以定义为数组,只能通过定义函数指针来操作。
1、函数指针作为参数传递给另一个函数(实现回调函数)
回调函数:将一个函数指针作为参数传递给其它函数。后者将“回调”用户函数。
回调函数作用是为降低模块间的耦合度,针对不同的回调函数,可以用一个统一的函数接口调佣,是
实现软件的高内聚低耦合的一种方法。
函数 F1 调用函数 F2 的时候,函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针,在函数 F2 执行的过程中,函数F2 调用了函数 F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 F3 就是回调函数。
#include
int Callback_1(){// Callback Function 1
printf("Hello, this is Callback_1 \n");
return 0;
}
int Callback_2(){// Callback Function 2
printf("Hello, this is Callback_2 \n");
return 0;
}
int Callback_3(){// Callback Function 3
printf("Hello, this is Callback_3 \n");
return 0;
}
int Handle(int (*Callback)()){
printf("Entering Handle Function. \n");
Callback();
printf("Leaving Handle Function. \n");
}
int main(){
printf("--------------------------------------------------- \n");
Handle(Callback_1);
printf(" \n");
Handle(Callback_2);
printf(" \n");
Handle(Callback_3);
printf("--------------------------------------------------- \n");
return 0;
}
/*
---------------------------------------------------
Entering Handle Function.
Hello, this is Callback_1
Leaving Handle Function.
Entering Handle Function.
Hello, this is Callback_2
Leaving Handle Function.
Entering Handle Function.
Hello, this is Callback_3
Leaving Handle Function.
---------------------------------------------------
*/
2、函数指针数组作为转移表
为了使用switch语句,表示操作符的代码必须是整数。如果它们是从零开始连续的整数,我们可以使用转换表来实现相同的任务。转换表就是一个函数指针数组。这样方便把具体操作和选择操作的代码分开。
# include
# include
int Add(int x, int y){
return x + y;
}
int Sub(int x, int y){
return x - y;
}
int Mul(int x, int y){
return x * y;
}
int Div(int x, int y){
return x/y;
}
int main(){
int a=8, b=5;
int ret = 0;
int(*p[5])(int a, int b) = {Add,Sub,Mul,Div};//函数指针数组的使用
for(int i=0;i<4;i++){
ret = (*p[i])(a, b);
printf("ret=%d\n", ret);
}
// system("pause");
return 0;
}
/*
ret=13
ret=3
ret=40
ret=1
*/
指向函数指针数组的指针 :void (*(p)[10])(const char);
二维数组在概念上是二维的,有行和列,但在内存中所有的数组元素都是连续排列的。
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
/*
0X61FDE0 0X61FDE4 0X61FDE8 0X61FDEC 0X61FDF0 0X61FDF4 0X61FDF8 0X61FDFC 0X61FE00 0X61FE04 0X61FE08 0X61FE0C
0 1 2 3 4 5 6 7 8 9 10 11
*/
int (*p)[4]为数组指针,p 指向的数据类型是int [4],那么p+1就前进 4×4 = 16 个字节,p-1就后退 16 个字节,这正好是数组 a 所包含的每个一维数组的长度。也就是说,p+1会使得指针指向二维数组的下一行,p-1会使得指针指向数组的上一行。
int (*p)[4] = a;
数组名 a 在表达式中也会被转换为和 p 等价的指针!那么等价关系如下:
a+i == p+i
a[i] == p[i] == *(a+i) == *(p+i)
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == ((a+i)+j) == ((p+i)+j)
#include
int main(){
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
int(*p)[4];
int i,j;
p=a;
for(i=0; i<3; i++){
for(j=0; j<4; j++) printf("%2d ",*(*(p+i)+j));
printf("\n");
}
return 0;
}
/*
0 1 2 3
4 5 6 7
8 9 10 11
*/
https://www.cnblogs.com/suwen/p/5266227.html
http://c.biancheng.net/view/335.html
https://www.runoob.com/w3cnote/c-callback-function.html
https://blog.csdn.net/qq_29567701/article/details/83659742
http://c.biancheng.net/view/2022.html