进阶指针知识点总结

今天分享一个自己总结的关于进阶指针的知识点:

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.****.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()是在运行时求值!