过关斩将,擒“指针”(下)

美丽的外貌千篇一律,有趣的灵魂万里挑一

指针是C语言的灵魂

应该有不少小伙伴对指针还是比较迷迷糊糊的吧

本期 阿紫 接着带大家一起过关斩将,擒 “ 指针 ”

过关斩将,擒“指针”(下)_第1张图片


目录

1.字符指针

2.指针数组

3.数组指针 

3.1数组指针的定义 

3.2 &数组名 vs 数组名

 3.3数组指针的使用

4.函数指针 

5.函数指针数组 

6. 指向函数指针数组的指针 

7.回调函数 


1.字符指针

字符指针:指向字符指针,指针类型为 char*

	char ch = 'a';
	char* pc = &ch;

一般使用方法:

#include
int main()
{
	char ch = 'a';
	char* pc = &ch;
	*pc = 'b';
	printf("%c\n", ch);

	return 0;
}

还有一种使用方法:

#include
int main()
{
	const char* pc = "abcdef";
	printf("%s\n", pc);

	return 0;
}

思考1:为什么 const char* pc = "abcdef" 前面要加 const

答:因为字符指针 pc 指向的是常量字符串,常量是不能发生改变的,所以加 const 避免修改常量字符串。 

思考2:const char* pc = "abcdef",是把 abcdef 字符串放入字符指针变量 pc 中吗?

答:不是,是把字符串中首字符地址放入了 pc 中,也就是把 a 的地址放在 pc 中 。 

过关斩将,擒“指针”(下)_第2张图片

 面试题:

#include
int main()
{
	char arr[] = { "abcdef" };
	char brr[] = { "abcdef" };
	const char* crr = "abcdef";
	const char* drr = "abcdef";
	if (arr == brr)
	{
		printf("arr and brr are same\n");
	}
	else
	{
		printf("arr and brr not are same\n");
	}
	if (crr == drr)
	{
		printf("crr and drr are same\n");
	}
	else
	{
		printf("crr and drr not are same\n");
	}

	return 0;
}

 运行结果:

 arrbrr 是字符数组,是把 abcdef 存储在字符数组中,它们的内存不是同一块,所以它们的地址也就不相同。而 crrdrr 是字符指针,是把 abcdef 常量字符串的首元素地址存储字符指针中,所以它们指向的是同一块内存空间。 过关斩将,擒“指针”(下)_第3张图片

 2.指针数组

 指针数组:它是一个数组,数组中的每个元素都是类型指针

int* arr[10]; //整形指针的数组

 首先 arr 先跟 [10] 结合说明它是一个有十个元素数组,每个元素都是 int* 类型的指针。

3.数组指针 

3.1数组指针的定义 

数组指针:是一个指针,指向的是一个数组 

	int arr[6] = { 1, 2, 3, 4, 5, 6 };
	int(*pi)[6] = &arr;

pi 先和 * 结合说明它是一个指针变量[6] 说明它指向了一个数组,数组中有 6 个元素,int 说明每个元素都是 int 类型。

  • 注:[] 的优先级要高于 * 号的,所以必须加上()来保证 p 先和 * 结合。 

3.2 &数组名 vs 数组名

int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  •  数组名 arr :表示首元素地址
  • &数组名:表示整个数组地址 

过关斩将,擒“指针”(下)_第4张图片

arr &arr 它们打印出来的地址虽然一样 ,但它们代表的含义不一样。arr 表示数组首元素的地址,而 &arr 表示整个数组的地址,arr + 1跳过了一个元素,而&arr + 1跳过了整个数组。

过关斩将,擒“指针”(下)_第5张图片

sizeof(arr[0])是计算 arr[0] 所占空间大小,而 sizeof(arr)是计算整个数组所占空间大小 。

结论:&数组名sizeof(数组名) 表示整个数组,其余的数组名都表示数组首元素的地址

 3.3数组指针的使用

 既然数组指针指向的是数组,那数组指针中存放的也就是数组的地址

#include
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int(*pi)[10] = &arr;

	return 0;
}

数组 arr 的地址赋值给数组指针变量 p ,但我们一般很少这样写代码 ,因为这样写代码没有任何的意义。

一般我们将数组指针用于二维数组

#include
void print_arr(int(*arr)[3], 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][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
	print_arr(arr, 3, 3);

	return 0;
}

数组名arr,表示首元素的地址  。但是二维数组的首元素是二维数组的第一行  ,所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址。

学了指针数组和数组指针我们来一起回顾并看看下面代码的意思: 

int arr[5];//整型数组
int *parr1[10];//数组指针
int (*parr2)[10];//指针数组
int (*parr3[10])[5];//???

 int (*parr3[10])[5]:首先 parr3 跟 [10] 结合说明它是一个数组,每个元素都是指针数组类型。

4.函数指针 

函数指针:首先是它是一个指针,它指向的是一个函数 

过关斩将,擒“指针”(下)_第6张图片

 函数名&函数名:都表示函数的地址

 要是想保存函数的地址,就得用到函数指针

过关斩将,擒“指针”(下)_第7张图片

p 先跟 * 结合 说明它是指针,后面的一个 () 表示它指向了一个无参的函数,前面的 void 表示返回值 void。

《 C陷阱和缺陷 》 这本书中有这样两个代码:

//代码1
(*(void (*)())0)();

将 0 强制类型转换为 void(*)() 函数指针类型,那么 0 就被当成了一个函数的地址,然后解引用调用,实际上这就是一次函数调用,调用的是 0 作为地址处的函数。

//代码2
void (*signal(int , void(*)(int)))(int);

signal 先跟后面的括号结合,说明 signal 函数名,函数参数第一个参数是 int,第二个参数是函数指针(该函数指针指向的函数参数是int,返回类型是void)返回值也是一个函数指针该函数指针指向的函数参数是int,返回类型是void,所以这是一次函数声明。 

5.函数指针数组 

函数指针数组:首先它是一个数组,数组的每一个元素都是指针指向的是函数。 

int(*arr[10])();

 首先 arr 先跟 [10] 结合说明它是一个数组,数组中的每一个元素都是 int(*)()类型的函数指针。

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

#include
void menu()
{
	printf("*********************\n");
	printf("*** 1.add   2.sub ***\n");
	printf("*** 3.mul   4.div ***\n");
	printf("****** 0.exit *******\n");
	printf("*********************\n");
}
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 div(int a, int b)
{
	return a / b;
}

int main()
{
	int a, b;
	int input = 1;
	int ret = 0;
	int(*p[5])(int a, int b) = { 0, add, sub, mul, div }; //转移表
	while (input)
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数:");
			scanf("%d %d", &a, &b);
			ret = (*p[input])(a, b);
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n", ret);
	}
	return 0;
}

6. 指向函数指针数组的指针 

指向函数指针数组的指针 :首先它是一个指针,它指向了一个数组,数组中每个元素都是函数指针。

	void(*p)();//函数指针
	void(*p1[1])();//函数指针数组
	void(*(*p2)[2])();//指向函数指针数组指针

7.回调函数 

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

qosrt 函数使用:

#include 
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
  return (*( int *)p1 - *(int *) p2);
}
int main()
{
    int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
    int i = 0;
    
    qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);//把一个函数地址传给另一个函数
    for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
   {
       printf( "%d ", arr[i]);
   }
    printf("\n");
    return 0;
}

你可能感兴趣的:(开发语言,c语言,蓝桥杯)