今天分享一个自己总结的关于进阶指针的知识点:
1.数组指针
数组指针和指针数组是两个挺绕口的名词,但是两个的意思截然不同,一个是指针,一个是数组。今天主要讲数组指针。
举例如下:
int (*p) [5]
这是一个数组指针,因为*首先和p结合代表他是个指针标量,然后指向了一个大小为5的整形数组。
注: [ ] 的优先级高于 *
对于一个int型数组arr[5]来说,arr和&arr分别代表什么?
下面可以通过一个实例来理解:
int a[5] = {0};
printf("%p\n",a);
printf("%p\n", &a);
printf("a+1 = %p\n ", a + 1);
printf("&a+1 = %p\n",&a+1);
这里可以看到a和&a的值一样,但两者所表示的意义截然不同,a表示数组首元素的地址,&a表示数组的地址,所以也就导致后面a+1和&a+1的值不一样,a+1表示跳过数组的一个元素的结果,而&a+1表示跳过整个数组的结果。
下面举一个数组指针的使用案例:
#include<stdio.h>
#include<stdlib.h>
void print_arr(int(*arr)[5], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
print_arr(arr, 3, 5);
system("pause");
return 0;
}
这个例子中可以看到在调用print_arr()函数时,实参为arr,3,5;同时print_arr函数的形参为int(*arr)[5],3,5;为什么要用int(*arr)[5]来接收arr呢?因为arr代表二维数组首元素的地址,二维数组可以看成是每个元素是一个一维数组的数组;多维数组都可以化为一维数组,所以二维数组的首元素是二维数组的第一行,这里传递的arr就相当于第一行的地址,所以可以拿一个数组指针来接收。
2.函数指针
函数指针当然就是形象化出的指向函数的指针,举例如下:
void(*p) ( ) 这里可以看到*首先和p构成一个指针,然后指向一个返回值为void,接收参数为空的函数。( )在这里就起到了调用的作用!
阅读一行代码:
void ( * signal (int ,void ( * ) (int) ) ) (int) ;
看到这里我想不少人有点懵了,这个该怎么分析,最好就是使用typedef来简化这行代码,能让分析变得简单点,化简如下:
typedef void (*pfun) (int) ;
pfun signal (int,pfun);
化简成这样我相信大家就能看明白了。
3.函数指针数组
函数指针数组首先要明确他是一个数组,每个元素是一个函数指针。
举例如下:
int (*arr[5]) () ;
arr首先和[]结合代表arr是一个数组,数组的内容是int (*) ( ),每个元素就是一个函数指针。
函数指针数组的用途:转移表
举例如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int add(int a,int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a*b;
}
int div1(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = {0,add,sub,mul,div1}; //这里定义了一个函数指针数组,每个数组元素都是一个指向函数的指针
while (input)
{
printf("*********************\n");是一个
printf("**1.add 2.sub**\n");
printf("**3.mul 4.div1**\n");
printf("*********************\n");
printf("请选择:");
scanf("%d",&input);
if (input >= 1 && input <= 4)
{
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y); //此处输入input,x,y的值通过相应的指针来调用相应的函数达到计算效果
}
else
printf("输入有误!");
printf("ret = %d\n",ret);
}
system("pause");
return 0;
}
可以看到上面的代码量比一般的思维写出的代码要少不少,一般最容易想到的就是switch,case来实现这个功能了,但是代码量较上面这种方法来说要多一些。
4.指向函数指针数组的指针
这句话看起来很绕口,但首先要明白他是一个指针,指向一个函数指针数组,举例如下:
void (*pfun) (char*) = test; //这是一个函数指针
void (*pfun[5]) (char*); //这是一个函数指针数组
void (*(*p)[5]) ( char*) = &pfun; //这是一个指向函数指针数组的指针
5.回调函数
回调函数算是很重要的一个知识点,因为他是函数指针的一种应用。
下面关于回调函数的应用举例请查看我的另一篇文章。
https://blog.csdn.net/Python_programer/article/details/88548816
指针校招笔试常考题
int a[] = {1,2,3,4};
printf("%d\n", sizeof(a)); //16个字节因为是计算整个数组大小
printf("%d\n", sizeof(a+0)); //4个字节因为数组名+数字隐式转化为指针,是指针就是四个字节
printf("%d\n", sizeof(*a)); //4个字节因为数组名代表首元素地址,解引用就是代表首元素的大小
printf("%d\n", sizeof(a+1)); //4个字节原因同第二个
printf("%d\n", sizeof(a[1])); //4个字节因为是数组第二个元素的大小
printf("%d\n", sizeof(&a)); //4个字节因为&a代表数组指针,是指针就是四个字节
printf("%d\n", sizeof(*&a)); //16个字节因为先&a代表数组指针,再解引用就是代表整个数组的大小
printf("%d\n", sizeof(&a+1)); //4个字节因为&a代表数组指针再+1还是指针,仍然是四个字节
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
printf("%d\n", sizeof(arr)); //6个字节因为是求整个数组大小
printf("%d\n", sizeof(arr + 0)); //4个字节因为数组名+数字隐式转化为指针,是指针就是四个字节
printf("%d\n", sizeof(*arr)); //1个字节因为是数组首元素大小
printf("%d\n", sizeof(arr[1])); //1个字节因为是数组第二个元素的大小
printf("%d\n", sizeof(&arr)); //4个字节因为&arr表示数组指针
printf("%d\n", sizeof(&arr + 1)); //4个字节同上
printf("%d\n", sizeof(&arr[0] + 1)); //4个字节因为&a[0]表示指针
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
printf("%d\n", strlen(arr)); //这几个都是未定义行为,因为比如第一个strlen函数求的是字符串长度遇到'\0'就会停止,而arr是字符数组没有'\0',所以就会一直计算下去知道'\0'为止,而再往后就会造成数组越界,访问非法内存,是未定义行为!
printf("%d\n", strlen(arr + 0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr + 1));
printf("%d\n", strlen(&arr[0] + 1));
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); //7个字节因为上面这个arr是字符串带有'\0',所以数组总大小是7
printf("%d\n", sizeof(arr + 0)); //4个字节因为数组名+数字隐式转化为指针,是指针就是四个字节
printf("%d\n", sizeof(*arr)); //1个字节因为是数组首元素大小
printf("%d\n", sizeof(arr[1])); //1个字节因为是数组第二个元素的大小
printf("%d\n", sizeof(&arr)); //4个字节因为&arr是数组指针
printf("%d\n", sizeof(&arr + 1)); //4个字节原因同上
printf("%d\n", sizeof(&arr[0] + 1)); //4个字节因为&arr[0]是指针
char arr[] = "abcdef";
printf("%d\n", strlen(arr)); //6个字节因为是字符串带有'\0',所以数组大小为6
printf("%d\n", strlen(arr + 0)); //6个字节因为数组名+0还是指向首元素的,所以数组大小还是6个字节
printf("%d\n", strlen(*arr)); //未定义
printf("%d\n", strlen(arr[1])); //未定义
printf("%d\n", strlen(&arr)); //6个字节但是是越界访问结果
printf("%d\n", strlen(&arr + 1)); //未定义
rintf("%d\n", strlen(&arr[0] + 1));//5个字节因为&a[0]是取首元素地址,再加上1表示跳过了一个元素,所以再strlen的话就是五个字节。
char *p = "abcdef";
printf("%d\n", sizeof(p)); //4个字节因为p是指针指向这个数组的首元素
printf("%d\n", sizeof(p + 1)); //4个字节因为指针加1还是指针
printf("%d\n", sizeof(*p)); //1个字节因为*p解引用是首元素
printf("%d\n", sizeof(p[0])); //1个字节因为是数组第二个元素
printf("%d\n", sizeof(&p)); //4个字节因为&p表示二级指针
printf("%d\n", sizeof(&p + 1)); //4个字节原因同上
printf("%d\n", sizeof(&p[0] + 1)); //4个字节因为&p[0]+1表示的是一个指针
注:sizeof()是在编译时求值,而strlen()是在运行时求值!