指针学习(3)

 

目录

1.字符指针变量

​编辑2.数组指针变量

2.1定义

2.2存放

3.二维数组传参的本质

4.函数指针变量

4.1创建

4.2使用

 ​编辑

5.typedef关键字

6.函数指针数组

6.1转移表

1.字符指针变量

顾名思义,字符指针也就是char*,在使用中,一般简单的使用可以这样

#include<stdio.h>
int main()
{
	char ch = 'w';
	char* p = &ch;
	return 0;
}

那如果是字符串,那就可以这样

#include<stdio.h>
int main()
{
	char ch[] = "abcdef";
	char* p = ch;
	*p = 'w';
	printf("%s ", p);
	return 0;
}

 

这里的首字符成功的被‘w'所替换掉了;那吧字符串直接存入指针里面,又是什么效果呢?

#include<stdio.h>
int main()
{
	/*char ch[] = "abcdef";
	char* p = ch;*/
	char* p = "abcdef";
	*p = 'w';
	printf("%s ", p);
	return 0;
}

发现运行结果什么都没有。实际上在第二段代码中(char ch[] = "abcdef";  char* p = ch;)是将字符串首元素地址存入了指针变量p中,且数组内容是可以更改的。而在第三段代码中(char* p = "abcdef";)也同样只是存入了字符串首元素的地址,而不是整个字符串的地址,且字符串内容是无法修改的,即(*p='w';是错误的);

两种表达方式,指针均是指向字符串首字符的,因为是连续存放,所以能访问到所有元素;因为第二种写法,字符串内容不可修改,所以更好的写法是在*前面加const(const char * p="abcdef";),以便来提醒我们,内容不可改。

注:%s打印地址只用提供首元素地址。

《剑指offer》中有这样一道题

#include <stdio.h>
int main()
{
 char str1[] = "hello bit.";
 char 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 not same\n");
 
 return 0;
}

2.数组指针变量

指针数组,指针数组是⼀种数组,数组中存放的是地址(指针);顾名思义,数组指针,是一种指针,是指向数组的指针。

2.1定义

数组指针变量的定义是这样的

int (*p)[10];

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

2.2存放

如果要存放数组的地址,那就得用数组指针变量

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr;
	return 0;
}

其中,int是p指向的数组的元素类型,p是数组指针变量名,[10]是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;
}

这里函数test用二维数组来接收主函数传来的二维数组,是可行的。实际上,二维数组在储存的时候并不是我们直观的用横和列来排列组合的,而是二维数组里的每个元素是⼀维数组的数组,也就是⼆维数组的每个元素是⼀个⼀维数组。那么⼆维数组的首元素就是第⼀行,是个⼀维数组。

所有我们结合数组名的规则(数组名是数组首元素的地址),二维数组的数组名表示的就是第⼀行的地址,是一维数组的地址。那么二维数组arr[3][5]的首元素就是第一行数组,即arr[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));
		}
		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;
}

4.函数指针变量

函数指针变量应该是用来存放函数地址的,希望通过地址能够调用函数的。

之前提到过,“&数组名”,是数组的地址,而“数组名”是数组首元素的地址;在函数指针中,无论是“&函数名”还是“函数名”均是指函数的地址。

4.1创建

如果要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针非常类似。

//代码1
void test()
{
 printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)()= test;//&test和test一样


//代码2
int Add(int x, int y)
{
 return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的

4.2使用

#include<stdio.h>
int add(int x, int y)
{
	return x+y;
}
int main()
{
	int (*pf)(int, int) = &add;
	int a = add(2, 3);//函数名调用
	printf("%d\n", a);
	int b = (*pf)(2, 3);//函数指针调用
	printf("%d\n", b);
	int c = pf(2, 3);//函数指针调用
	printf("%d\n", c);
	return 0;
}

 

由此我们也可以发,在调用函数指针的时候,*可以省略,就像上面代码(int b = (*pf)(2, 3);)和(int b = pf(2, 3);)是等价的

5.typedef关键字

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

typedef unsigned int uint;//将unsigned int 重命名为uint


unsigned int a;
uint a;//重命名后,两者等价

如果是指针类型也同样可以重命名,例如

typedef int* ptr_t;//将int*重命名为ptr_t

int* p;
ptr_t p;//重命名后,二者等价

但是对于数组指针函数指针稍微有点区别:

比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

typedef int(*parr_t)[5]; //新的类型名必须在*的右边


int (*p)[5];
parr_t p;//重命名后,二者等价

比如将函数指针类型将 void(*)(int) 类型重命名为 pfun_t ,就可以这样写:

typedef void(*pfun_t)(int);//新的类型名必须在*的右边


void(*pa)(int);
pfun_t pa;//重命名后,二者等价 

6.函数指针数组

要把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,怎么定义呢

int (*parr1[3])();

parr1 先和 [ ] 结合,说明 parr1是数组;将parr1[3]去掉剩余的int (*)() ,是说明这是int (*)()类型的函数指针

接下来就是函数指针数组的使用了

//计算两个整数的加减乘除
#include<stdio.h>
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;
}
int main()
{
	int (*fparr[4])(int, int) = { add,sub,mul,div };
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int r = fparr[i](8, 4);
		printf("%d\n", r);
	}
	return 0;
}

6.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, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");	
		printf(" 3:mul 4:div \n");
		printf(" 0:exit \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输⼊操作数:");
			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;
}

使⽤函数指针数组的实现: 

#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, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	do
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf(" 0:exit \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;
}

如果还觉得麻烦,还可以这样

#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;
}
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("输入操作数:");
	scanf("%d %d", &x, &y);
	ret = add(x, y);
	printf("ret = %d\n", ret);
}
int main()
{
	int input = 1;
	do
	{
		menu();
		printf("请选择:");
		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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值