引言:说到指针,学过C语言的同学想必都非常清楚类似于解引用(*),取地址(&)这些常用的符号,但是,有一些指针,如果不经常用到,很可能看到就晕,也会有畏惧之心,这篇文章将会向大家介绍指针的一些容易被遗忘的类型。
(*(void (*)())0)();
void (*signal(int , void(*)(int)))(int);//void(*)(int) signal(int,void(*)(int))//err
看到上面两段代码,是否会突然一慌呢,如果会的话,赶紧来学习一下吧~
1.数组指针和函数指针
为了让大家可以更加深刻的记住这些比较难以记住的指针,前面我讲涉及到的思路和大家分享一下,
所以我把数组指针和函数指针放一块咯~
1.1如何定义(善用类比的观察)
int* p;//这是指向整型的指针用int*,叫整型指针
char* p2;//这是指向字符的指针char*,叫字符指针
//·······
int** pp;//这是指向指针的指针,叫二级指针,存放的是一级指针的地址
int***ppp;//同理还可以有三级四级五级n级指针
//那么可以看看以下比较套娃式的指针,虽然有点抽象,但是也可以用以上类比的方式理解
int* p3[10];//这是什么指针?这不是指针,是数组,p3首先和[]结合,是一个数组,然后再是到int*,这个数组存放的是指向整型类型的指针
int(*p4)[10];//这是才是一个指针,p4先和*结合说明他是一个指针,再往外看到int[10],回想上面的,可以很轻松想到,这个指针指向的是一个数组的地址,而这个数组里有是个元素,并且是一个整型类型的数组----->这是一个数组指针
int abc(int a,int b);
int (*p5)(int,int);//指向函数地址的指针,函数指针
char* abc(int a,char b);
char* (*p6)(int,char);
//函数指针--可用于回调函数,通过传递函数的地址来调用函数
int(*p7[4])(int,int)//这个是函数指针数组---可用于转移表
那么现在,我相信大家对于开头的问题,即使有疑问也已经有所解答了吧~
(*(void (*)())0)();//(void (*)())0从最里面我们可以看到这是一个强转,把0强制转化为函数指针类型,然后作为函数指针参数传递
void (*signal(int , void(*)(int)))(int);//这个也是类似的我们其实可以看成void(*)(int) signal(int,void(*)(int))来帮助理解,void(*)(int) 是类型名,后面是我们要进行传参的内容,但是这种写法是错误的噢,
1.2有什么用呢?
1.2.1(数组指针可用于二维数组传参)
//在学习数组指针之前,我们进行二维数组的传参,可以需要依赖这种方式
#include <stdio.h>
void test(int a[3][5], int r, int c)//在这里我们需要将二维数组写在上面
{ int i = 0;
int j = 0;
for(i=0; i<r; i++)
{
for(j=0; j<c; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
//在学习数组指针之后,我们可以这样!
void test2(int (*p)[5], int r, int c)//而现在,我们可以将它写成指针的形式进行传参
{ int i = 0;
int j = 0;
for(i=0; i<r; i++)
{
for(j=0; j<c; j++)
{
printf("%d ", *(*(p+i)+j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};
test(arr, 3, 5);
test2(arr, 3, 5);//这两种调用方式将会是一样的结果噢
return 0;
}
1.2.1(函数指针的使用)
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int(*pf3)(int, int) = Add;//这里取出Add函数的地址,函数指针里存的就是Add函数的地址
int(*pf3)(int, int) = &Add;//这种和上面的结果是一样的,都是直接取出了函数的地址
printf("%d\n",Add(2,3));//--函数名调用
//----------------------------学习了函数指针后,我们可以用以下方式进行调用函数,在后续的回调函数的使用会非常重要
printf("%d\n", (*pf3)(2, 3));//--函数指针的调用
printf("%d\n", pf3(3, 5));//*可以省略,写不写都和上一行一致--函数指针的调用
return 0;
}
2.void*指针
可以接收任意指针的类型,但是不可以对他进行解引用和±整数的运算,多用于函数封装的时候,我们还不知道要给他传什么类型的参数时候使用
#include <stdio.h>
int main()
{
int a = 10;
void* p1 = &a;//接收是可以的
void* p2 = &a;
*p1 = 10;//这样会报错
*p2 = 0;
return 0;
}
3.const的使用
变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。但是如果我们希望⼀个变量加上⼀些限制,不能被随便修改,那我们应该怎么办呢,这就是const的功效!
#include <stdio.h>
void test1()
{
int n = 10;
int m = 20;
int *p = &n;
*p = 20;
p = &m;
}//此时正常情况下,我们是可以对*p和p进行赋值的
void test2()
{
int n = 10;
int m = 20;
const int* p = &n;
*p = 20
p = &m;
}//这种情况呢?是否赋值成功呢?
void test3()
{
int n = 10;
int m = 20;
int *const p = &n;
*p = 20;
p = &m;
}//这种情况又能否成功呢?
void test4()
{
int n = 10;
int m = 20;
int const * const p = &n;
*p = 20;
p = &m;
}//这个呢?
在运行过后我们可以发现,
test2中,我们不能对p里面的内容进行修改,但是我们可以修改其地址;
test3中,我们不能对p的地址进行修改,但是我们可以修改里面的值,此时const修饰的是指针;
test4中,我们很遗憾发现,我们无论是对于指针,还是里面的值,都没有权力去改变。
而这个就是const的作用,在很多库函数的封装都会频繁用到,也是自己写出好的稳定的代码的好帮手。
结论:const修饰指针变量的时候
const如果放在星号左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
const如果放在的星号右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变。
的稳定的代码的好帮手。
结论:const修饰指针变量的时候
const如果放在星号左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
const如果放在的星号右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变。