1 字符指针变量

在指针类型中,字符指针 char* 除了指向单个字符,还有一种常见用法是指向字符串常量。

int main()
{
	const char* pstr = "hello";
	printf("%sn", pstr);
	return 0;
}

const char* pstr = "hello"; ,其本质是将字符串常量首字符 'h' 的地址存入指针 pstr。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。

2 数组指针变量

顾名思义,是指向数组的指针变量(区分指向数组首元素的指针)。

//例如
int arr[10] = {0};
int (*p)[10] = &arr;

解释:p 先和 * 结合,说明 p 是一个指针变量,然后指针指向的是一个大小为10个整型的数组。所以 p 是一个指针,指向一个数组,叫数组指针。

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

3 二维数组传参的本质

有了数组指针的理解,我们就能够讲一下二维数组传参的本质了。

过去我们有一个二维数组的需要传参给一个函数的时候,我们是这样写的:

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

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);
    return 0;
}

这里实参是二维数组,形参也写成二维数组的形式,那还有什么其他的写法吗?

首先我们再次理解一下二维数组,二维数组其实可以看做是每个元素是一维数组的数组,也就是二维数组的每个元素是一个一维数组。那么二维数组的首元素就是第一行,是个一维数组。

所以,根据数组名是数组首元素的地址这个规则,二维数组的数组名表示的就是第一行的地址,是一维数组的地址。根据上面的例子,第一行的一维数组的类型就是 int [5] ,所以第一行的地址的类型就是数组指针类型 int(*)[5]。那就味着二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针形式的。如下:

#include <stdio.h>
void test(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));
            //*(*(p + i) + j) == *(p[i] + j) == 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);
    return 0;
}

详解 *(*(p + i) + j)

p + i:指针前进 i 行,每次跳过5个整数(一行的大小)

*(p + i):解引用得到第 i 行的首地址(类型从 int (*)[5] 变为 int*

*(p + i) + j:在第 i 行内,指针前进 j 个整数位置

*(*(p + i) + j):最终解引用得到第 i 行第 j 列的元素值

4 函数指针变量

4.1 函数指针变量的创建

类比一下之前学习的指针类型,函数指针变量应该是用来存放函数地址的,未来通过地址能够调用函数的。那么,先来尝试打印一下函数的地址。

#include<stdio.h>
void test()
{
	printf("%dn", 20);
}

int main()
{
	printf("%pn", test);
	printf("%pn", &test);
	return 0;
}

通过以上代码,打印出了 test() 函数的地址。其实,函数名就是函数的地址,当然也可以通过 &函数名 的方式获得函数的地址。

如果要存储函数的地址,就需要创建函数指针变量。函数指针的写法与数组指针非常类似。

#include<stdio.h>

void test()
{
	printf("hehen");
}
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	void (*pf1)() = &test;
	void (*pf2)() = test;
    
	int(*pf3) (int, int) = Add;
	int(*pf4) (int x, int y) = Add;//x和y可省略
	
    printf("%pn", pf1);
	printf("%pn", pf2);
	printf("%pn", pf3);
	printf("%pn", pf4);
	return 0;
}

int (*pf4) (int x, int y) = Add; 为例拆解一下:

int (*) (int x, int y)pf4 函数指针变量的类型。

4.2 函数指针变量的使用

#include<stdio.h>

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

int main()
{
	int a = 3, b = 5;
	int(*pf3) (int, int) = Add;//Add == pf3,两者是等价的

	printf("%d + %d = %dn", a, b, (*pf3)(a, b));//这里的*没有实际作用,只是为了方便理解
	printf("%d + %d = %dn", a, b, pf3(a, b));
	printf("%d + %d = %dn", a, b, Add(a, b));
	return 0;
}

4.3 typedef 关键字

typedef 是用来类型重命名的,可以将复杂的类型,简单化。

typedef unsigned int uint;
typedef int* ptr_t;

//数组指针重命名,int(*)[5]
typedef int(*parr_t)[5];//新的类型名必须在*的右边
//函数指针重命名,void(*)(int)
typedef void(*pfun_t)(int);//新的类型名必须在*的右边

5 函数指针数组

数组是一个存放相同类型数据的存储空间。在Part3中已经见过一些简单的指针数组,例如:

那把函数的地址存到一个数组中,这种数组就叫函数指针数组

int (*parr1[3])();

parr1 先和 [] 结合,说明 parr1 是数组,数组的内容是 int (*)() 类型的函数指针。

6 转移表

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

举例:计算器的一般实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.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 Div(int a, int b)
{
	return a / b;
}

int main()
{
	int x = 0;
	int y = 0;
	int input = 0;
	int (*pf[10])(int, int) = { 0, Add, Sub, Mul, Div};//函数指针数组
	do
	{
		printf("*************************n");
		printf("     1:add    2:subn");
		printf("     3:mul    4:divn");
		printf("     0:exitn");
		printf("*************************n");
		printf("请选择:");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出计算器!n");
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("输入x y:n");
			scanf("%d%d", &x, &y);
			int r = pf[input](x, y);
			printf("ret = %dn", r);
		}
		else
		{
			printf("输入有误!n");
		}
	} while (input);
	return 0;
}
本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]