指针的深度理解(3)

数组指针变量

 数组指针变量是什么和之前我们说过的指针数组有什么区别呢?接下来我将一一解答。指针数组是⼀种数组,数组中存放的是地址(指针)。 那数组指针变量是指针变量呢?还是数组呢? 如其名毕竟其名字的后面是指针,所以它是:指针变量。 相信我们都知道整形指针变量是: int * pint; 其是存放的是整形变量的地址,能够指向整形数据的指针。 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。 那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。

下面我们可以看一下这段代码:大家可以思考一下,p1,p2,分别代表什么?

#includestdio.h>
int main()
{
int* p1[10];
int (*p2)[10];
return 0;
}

下面来揭晓答案:

in数组指针变量  int (*p2)[10]; 解释:p2先和*结合,说明p2是⼀个指针变量变量,然后指着指向的是⼀个大小为10个整型的数组。所以p2是⼀个指针,指向⼀个数组,叫 数组指针。 这里要注意:[ ]的优先级要高于*号的,所以必须加上()来保证p2先和*结合。

接下来我们看一下:数组指针如何初始化

我们像之前一样可以运用取地址“&”符号,请看代码:

#include
int main()
{

	int arr[10] = { 0 };
	int(*p)[10] = &arr;
	return 0;

}

通过监视窗口我们可以发现,其确实存着数组arr的地址! 

指针的深度理解(3)_第1张图片

 下面给大家带来一个图解:

指针的深度理解(3)_第2张图片

接下来我们了解一下:二维数组的传参本质:

我们可以看一个简单的二维数组赋值与打印的代码

#include 
void list(int a[3][5], int r, int c)
{
	int i, j;
	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] = { {7,4,3,6,88}, {2,3,44,54,64},{31,43,55,66,7} };
	list(arr, 3, 5);
	return 0;
}

 我们可以看见我们的实参是一个二维数组,形参也是一个二维数组,这是我们没学指针时的方法,但今天我们学习了数组指针后可以稍微换一个方式。

首先我们要了解一下这个二维数组的传参:我们要知道实参传给形参的并不是整个数组的地址,而是二维数组首行的地址,那单独分开首行那不就是一个一维数组,这时我们联想到刚刚的指针数组。下面是一个arr[7][8]的呈现方式,圈住的便是第首行的元素。指针的深度理解(3)_第3张图片

那我们稍微运用一下便是下面的代码:


#include 
void list(int (*p)[5], int r, int c)
{
	int i, j;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%2d ", *(*(p + i) + j));
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {7,4,3,6,88}, {2,3,44,54,64},{31,43,55,66,7} };
	list(arr, 3, 5);
	return 0;
}

指针的深度理解(3)_第4张图片最后我们可以稍微的总结一下

二维数组传参本质上也是传递了地址,传递的是第⼀行这个一维数组的地址,二维数组传参,形参的部分可以写成数组,也可以写成指针形式。

函数指针变量

前面讲了个数各样的指针类型、样式,那么接下来我们又要讲一个函数的指针,毕竟函数也是程序的重要部分!!!我们可以进行测试一下,能否打印出函数的地址!看下面代码:

#include
int Add(int x,int y)
{
	return x + y;

}



int main()
{

	printf("%p \n", &Add);
	printf("%p \n", Add);
	return 0;
}

指针的深度理解(3)_第5张图片

我们可以发现Add函数的地址是可以打印出来的,故函数类型的指针也是存在的。也是使用“&”符号进行提取的。

函数指针的初始化

接下看一下代码:

#include 
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*pf)(int, int) = Add;

	printf("%d\n", (*pf)(11, 11));
	printf("%d\n", pf(22, 22));
	return 0;
}

指针的深度理解(3)_第6张图片

然后奉上图片理解,图文一起更好理解: 

指针的深度理解(3)_第7张图片

从上面的运行结果也可以看出,指针如何指向函数进行使用了!


最后我们讲一下函数指针的数组:

指针的深度理解(3)_第8张图片 大家可以看看上面三个中哪个是正确的函数指针数组的定义。

答案是parr1,为什么呢?首先前面说过[ ]的优先级是比 * 的优先级要高的

所以parr1先与[ ]组成数组,然后面的int  (*) ()表示其为一个函数指针类型。最后大家知道这个知识点后可以利用其改变之前的计算器代码的实现。即下面要讲的内容!

转移表

在 C 语言中,转移表(Jump Table)是一种用于实现条件跳转的优化技术。它是一个表格,其中包含了根据某种条件或输入值对应的跳转目标地址。

转移表通常用于实现具有多个分支的复杂条件控制流程。通过使用转移表,可以将复杂的条件判断转换为直接的跳转操作,从而提高程序的执行效率。

以下是一个简单的示例,展示了如何使用转移表来实现根据输入值进行条件跳转:
 

#include 

// 定义一个转移表
int jump_table[5][2] = {
    {10, 20}, 
    {30, 40}, 
    {50, 60}, 
    {70, 80}, 
    {90, 100}
};

// 根据输入值进行跳转
int main() {
    int input = 5;

    // 计算索引
    int index = input - 1;

    // 执行跳转
    printf("Branch %d: jumping to address %p\n", index + 1, jump_table[index]);

    return 0;
}

在上述示例中,我们定义了一个转移表  jump_table ,其中包含了输入值  1  到  5  对应的目标地址。然后,我们根据输入值计算出对应的索引,并使用该索引来获取转移表中的目标地址,实现跳转。

需要注意的是,转移表的实现方式和具体用法会根据不同的需求和编程语言有所差异。在实际使用中,需要根据具体情况进行适当的调整和优化。

 

下面代码是计算器简单的使用了

#include 
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, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; 
	do
	{
		printf("*************************\n");
		printf(" 1:加法   2:减法 \n");
		printf(" 3:乘法   4:除法 \n");
		printf(" 0:退出 \n");
		printf("*************************\n");
		printf("请选择你所需的功能:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
		{
			printf("输⼊有误\n");
		}
		
	} while (input);
	return 0;
}


好了今天的指针的内容就写到这里了!!

希望各位能多多支持!

下篇再见!

指针的深度理解(3)_第9张图片



 

 


 

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