【C语言】指针进阶 - 指针数组 数组指针 数组指针传参 函数指针 指向函数指针数组的指针

目录

    • 一、字符指针
      • 一道题
    • 二、 指针数组
      • 解引用打印数组元素
      • &数组名 数组名
      • 字符指针初始化
    • 三、数组指针
      • 取数组地址-放在数组指针里
      • 类型
      • 数组指针的使用
      • 二维数组传参
      • 判断
    • 四、数组参数、指针参数
      • 1、一维数组传参
      • 2、二维数组传参
      • 3、一级指针传参
        • 当一个函数的参数部分为一级指针的时候,函数能接收什么参数
      • 4、二级指针传参
    • 五、函数指针
      • 1、函数指针变量 - 存放函数的地址
      • 2、类型 变量
      • 3、调用
      • 4、有趣的代码
    • 六、函数指针数组
      • 实现加减乘除
    • 六、回调函数
    • 七、指向函数指针数组的指针


一、字符指针

一道题

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	const str1[] = { "hello bit." };
	const str2[] = { "hello bit." };
	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";

	if (str1 == str2) //数组名 比较两个字符串首元素地址
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are same\n");
	//常量字符串 -不能修改的,可以加上const
	//str3 str4内容一样 指向同一字符串首地址

	return 0;
}
//《剑指offer》
str3和str4指向的是一个同一个常量字符串。
C/C++会把常量字符串存储到单独的一个内存区域,
当几个指针,指向同一个字符串的时候,他们实际会指向同一块内存。
但是用相同的常量字符串去初始
化不同的数组的时候就会开辟出不同的内存块。
所以str1和str2不同,str3和str4不同。
输出:str1 and str2 are not same 
str3 and str4 are same

二、 指针数组

指针数组 - 是一个存放指针的数组

char* arr[5]; //ar是存放字符指针的数组
int* arr2[4]; //arr2是存放整形指针的数组

解引用打印数组元素

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;

	int* arr[4] = { &a, &b, &c, &d };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%d ", *(arr[i]));
	}

	return 0;
}

&数组名 数组名

//
#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };

	int* parr[] = { arr1, arr2, arr3 };
	//数组名是首元素地址 arr1是int*类型

	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]); // p[i] == *(p+i)
			// parr[i][j] == *(parr[i]+j)
		}
		printf("\n");
	}

	return 0;
}

总结

&arr和arr,它们的值是一样的,但是意义其实是不一样的:
&arr 表示数组的地址,而不是数组首元素的地址
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40


字符指针初始化

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	const char* arr[5] = { "abcdef", "bcd", "hehe", "haha", "hello" };
	//存放每个字符串的首元素地址
	//常量字符串 不能改 加const
	
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}


三、数组指针

数组指针是指针,还是数组:

//是指针
int* p; //整形指针 - 指向整形的指针

数组指针:能够指向数组的指针。


取数组地址-放在数组指针里

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	int a = 10;
	int* pi = &a; //整形的地址存放在整形指针中

	char ch = 'w';
	char* pc = &ch; //字符的地址存放在字符指针里

	int arr[10] = { 0 };
	int* p = arr; //数组首元素的地址

	//int* parr[10]; 这样写是数组
	int (*parr)[10] = &arr; //取出的是数组的地址,应该存放在数组指针里

	return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	int arr[10] = { 0 };
	//arr; //数组名是首元素的地址
	//&arr[0]; //首元素的地址
	//
	//&arr; //取出数组的地址

	printf("%p\n", arr); //arr类型是int*
	printf("%p\n", &arr[0]); //int*
	printf("%p\n", &arr); //数组的地址类型:int(*)[10] 
	//int(*p)[10] = &arr;

	printf("%p\n", arr+1);
	printf("%p\n", &arr[0]+1);
	printf("%p\n", &arr+1);

	return 0;
}

类型

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	int* arr[10];
	int* (*p)[10] = &arr;

	return 0;
}

数组指针的使用

下标

#define _CRT_SECURE_NO_WARNINGS 1

#include 

void print1(int arr[], int sz)
{
	int i = 0; 
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

指针

#define _CRT_SECURE_NO_WARNINGS 1

#include 

void printf2(int* arr, int sz)
{
	int i = 0; 
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(arr + i));
	}
}

数组指针

#define _CRT_SECURE_NO_WARNINGS 1

#include 

void print3(int(*parr)[10], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", parr[0][i]); //从第一个元素向后找
		printf("%d ", *(parr +0)[i]);
		printf("%d ", *(parr)[i]); //(*parr) 相当于parr指向的数组的数组名
	}
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print1(arr, sz);
	print2(arr, sz);
	print3(&arr, sz); //一维数组不适合

	return 0;
}

二维数组传参

#define _CRT_SECURE_NO_WARNINGS 1

#include 

void print1(int arr[3][5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0; 
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

void print2(int(*p)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			//*(p + i)//加i找到行 加j找到元素 解引用
			//printf("%d ", *(*p + i) + 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 };
	print1(arr, 3, 5); //二维数组传参
	printf("\n");

	print2(arr, 3, 5); //首元素地址 指针接受
	//二维数组 三个元素 是一维数组 传的是一维数组的地址
	//首元素就是二维数组的第一行 1 2 3 4 5
	//*p指针 5个元素 每个元素是int类型

	return 0;
}

判断

int arr[5]; //整型数组

int *parr1[10]; 
//parr1是一个数组,10个元素,每个元素是int*的,所以parr1是一个存放指针的数组

int (*parr2)[10]; 
//parr2是一个数组指针,该指针指向的数组有10个元素,每个元素是int的

int (*parr3[10])[5]; 
//parr3是一个数组,数组有10个元素,每个元素石神一个数组指针,
//该指针指向的数组有5个元素,每个元素是int



四、数组参数、指针参数

1、一维数组传参

#define _CRT_SECURE_NO_WARNINGS 1

#include 

void test(int arr[])
void test(int* p)
{}
int main()
{
	int arr[10] = { 0 };
	test(arr);
	return 0;
}
//
#define _CRT_SECURE_NO_WARNINGS 1

#include 

void test(int** p)
{}
int main()
{
	int* arr[10] = { 0 }; //10个元素 每个元素是int*类型 首元素地址就是int*的地址 用二级指针接收
	test(arr);
	return 0;
}

2、二维数组传参

#define _CRT_SECURE_NO_WARNINGS 1

#include 

void test(int arr[3][5]) {}
void test(int arr[][5]) {} //不能省略列 可以省略行

//用指针方式传
//二维数组传首元素的时候,指的是第一行 
//用指向数组的是指针接收
void test(int(*p)[5]) {} //*p指针 5个元素 每个元素是int

int main()
{
	int arr[3][5] = { 0 };
	test(arr);
	return 0;
}

3、一级指针传参

#define _CRT_SECURE_NO_WARNINGS 1

#include 

void print(int* ptr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(ptr + i));
	}
}

int main()
{
	int arr[] = { 1,2,3,4,4,5,6,7,8,9,10 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(p, sz);

	return 0;
}

当一个函数的参数部分为一级指针的时候,函数能接收什么参数
#define _CRT_SECURE_NO_WARNINGS 1

#include 

//int* p可以是什么传的
void test(int* p)
{}
int main()
{
	int a = 10;
	int* p1 = &a;
	int arr[10] = { 0 };

	test(&a);
	test(arr);
	test(p1);

	return 0;
}

4、二级指针传参

#define _CRT_SECURE_NO_WARNINGS 1

#include 

//当函数的参数为二级指针的时候,可以接收什么参数
void test(int** ppa)
{}
int main()
{
	int a = 10;

	int* pa = &a;
	int** ppa = &pa;

	test(ppa);
	test(&pa);

	int* arr[5];
	test(arr); //指针数组的地址

	return 0;
}


五、函数指针

1、函数指针变量 - 存放函数的地址

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	printf("%p\n", &Add);
	//函数名 == &函数名
	printf("%p\n", Add);

	return 0;
}

2、类型 变量

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*pf)(int, int) = &Add; //pf是用来存放函数的地址- pf就是函数指针变量
	//*pf指针存放地址 指向Add,参数类型是int 返回类型也是int

	int arr[10] = { 0 };
	int(*parr)[10] = &arr; //parr是数组指针变量

	//类型
	int a = 10; //int
	int arr[10] = { 0 }; //int [10]
	int(*parr)[10] = &arr; //int(*)[10]
	int (*pf)(int, int) = &Add; //int(*)(int, int)

	return 0;
}

3、调用

#define _CRT_SECURE_NO_WARNINGS 1

#include 

int Add(int x, int y)
{
	return x + y;
}

//1
int main()
{
	int (*pf)(int, int) = &Add;
	int ret = Add(2, 3);
	printf("%d\n", ret); //5

	ret = (*pf)(4, 5);
	printf("%d\n", ret); //9

	return 0;
}

//2
int main()
{
	int (*pf)(int, int) = Add;
	int ret = Add(2, 3);
	printf("%d\n", ret); //5

	//ret = (*pf)(4, 5);
	ret = pf(4, 5); //*可省略
	printf("%d\n", ret); //9

	return 0;
}

4、有趣的代码

代码1

//1
#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	(*(void (*)()) 0) ();
	//void (*ptr)();
	//0强制类型转换成函数指针类型void(*)() 把0当做地址 指向了无参无返回值的函数,然后解引用调用这个函数
	//代码是一次函数调用
	//1. 代码中把0强制类型转换成类型为void(*)()的一个函数的地址
	//2. 解引用0地址 就是取0地址的这个函数 被调用的函数是无参 返回类型是void
	return 0;
}
//《C陷阱与缺陷》

代码1

//2
#define _CRT_SECURE_NO_WARNINGS 1

#include 

int main()
{
	void(*signal(int, void(*)(int)))(int);
	//此代码是一次函数声明
	//声明的函数名是signal
	//signal函数有2个参数,第一个:int类型 第二个:void(*)(int)的函数指针类型
	//signal函数的返回类型依然是:void(*)(int)的函数指针类型

	return 0;
}

代码2简化

#define _CRT_SECURE_NO_WARNINGS 1

#include 

//代码2简化
typedef void(*pfun_t)(int);

int main()
{
	pfun_t signal(int, pfun_t);

	return 0;
}


六、函数指针数组

数组是一个存放相同类型数据的存储空间,我们已经学习了指针数组:

int* arr[10]; //整型指针的数组
//函数指针的数组 - 存放函数指针的数组
#define _CRT_SECURE_NO_WARNINGS 1

#include 

int Add(int x, int y) //int (*)(int, int)
{
	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 (*pf1)(int, int) = Add;
	int (*pf2)(int, int) = Sub;

	//pfArr就是一个函数指针的数组
	int (*pfArr[4])(int, int, int, int) = { Add, Sub, Mul, Div };

	return 0;
}

实现加减乘除

函数指针数组的用途:转移表

#define _CRT_SECURE_NO_WARNINGS 1

#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;
}

void menu()
{
	printf("**********************************\n");
	printf("*******  1. add    2. sub  *******\n");
	printf("*******  3. mul    4. div  *******\n");
	printf("*******  0. exit           *******\n");
	printf("**********************************\n");
}

int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			printf("请输入2个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("ret=%d\n", ret);
			break;
		case 2:
			printf("请输入2个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("ret=%d\n", ret);
			break;
		case 3:
			printf("请输入2个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("ret=%d\n", ret);
			break;
		case 4:
			printf("请输入2个操作数:>\n");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("ret=%d\n", ret);
			break;
		case 0:
			printf("退出计算机\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}

	} while (input);

	return 0;
}

优化
使用函数指针数组的实现:

#define _CRT_SECURE_NO_WARNINGS 1

#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;
}

void menu()
{
	printf("**********************************\n");
	printf("*******  1. add    2. sub  *******\n");
	printf("*******  3. mul    4. div  *******\n");
	printf("*******  0. exit           *******\n");
	printf("**********************************\n");
}

int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		//《C和指针》
		int (*pfArr[5])(int, int) = { 0, Add,Sub,Mul,Div };
		//0 1 2 3 4

		if (input == 0)
		{
			printf("退出计算器\n");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:>");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}

	} while (input);

	return 0;
}

函数

#define _CRT_SECURE_NO_WARNINGS 1

#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;
}

void menu()
{
	printf("**********************************\n");
	printf("*******  1. add    2. sub  *******\n");
	printf("*******  3. mul    4. div  *******\n");
	printf("*******  0. exit           *******\n");
	printf("**********************************\n");
}

void Calc(int(*pf)(int, int))
{
	int x = 0;
	int y = 0;
	int ret = 0;

	printf("请输入2个操作数:>\n");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("ret=%d\n", ret);
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			Calc(Add);
			break;
		case 2:
			Calc(Sub);
			break;
		case 3:
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;
		case 0:
			printf("退出计算机\n");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}

	} while (input);

	return 0;
}


六、回调函数

回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,
当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,
而是在特定的事件或条件发生时由另外的一方调用的,
用于对该事件或条件进行响应。


七、指向函数指针数组的指针

指向函数指针数组的指针是一个 指针 指针指向一个 数组 ,
数组的元素都是 函数指针

#define _CRT_SECURE_NO_WARNINGS 1

#include 
int main()
{
	int arr[10];
	int(*p)[10] = &arr;
	//p是一个指向 整型数组 的指针

	int* arr[10]; //整型指针的数组
	int* (*p)[10] = &arr; //整型指针数组的地址
	//p是一个指向 整型指针数组 的指针

	int Add(int x, int y)
	{
		return x + y;
	}
	int (*pf)(int, int) = Add; //pf是函数指针
	int (*pfArr[5])(int, int); //pfArr是一个函数指针的数组
	int (*(*ppfArr)[5](int, int) = &pfArr;
	//ppfArr是一个指向函数指针数组的指针

	return 0;
}

你可能感兴趣的:(C语言,c语言,指针)