C语言指针(三)

一、字符指针变量 

 今天让小白带大家学习新的知识,首先让我们来先了解一下字符指针。 

int main()
{
    //char arr[] = "abcdef";//a b c d e f \0,数组
    //char* p = arr;//数组名是数组首元素地址
    


    char* p = "abcdef";//常量字符串
    printf("%s\n", p);
    printf("%c\n", p);
    return 0;
}

从上述代码不难看出,char* p = arr; char*p = "abcdef";是有区别的,一个是数组,一个是常量字符串,而两者最大的区别就在于常量字符串不能被修改字符数组是可以被修改的

 由上图可以看出,常量字符串不能被修改,而一旦被修改,就会报警告。

还有一点则是"abcdef"的长度是6,但char*指针在x86环境下是4个字节,显然长度不够,但编译器并没有报警告,所以我们可以得出。常量字符串也是将首字符的地址‘a’赋给p

由上图的两个printf可以得出,%s只需提供给首字符地址,就可打印字符串,而%c只能打印字符,所以结果会为‘a’。

现在让我们看一道《剑指offer》的题目

首先虽然str1和str2存放的是相同的字符串,但是str1与str2的地址不同, 所以str1与str2不同;

其次str3和str4指向的是同一个常量字符串,C/C++会把常量字符串存放在一块单独的内存区域,多个指针指向同一个字符串时,它们存储的以及指向的都是同一个字符串的地址。所以str3和str4相同。

二、数组指针变量

 整型有整型指针,字符串有字符串指针,那数组指针顾名思义就是数组的指针,用来存放整个数组的地址,能够指向整个数组的指针变量

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

 

 注意:这里一定要加上括号因为[]的优先级要高于*,所以要保证p与*先结合。否则会变成指针数组。

补充:数组与指针的区别

  • 数组就是数组,是一块连续的空间,是可以存放一个或多个数组的。
  • 指针变量是一个变量,是可以存放地址的变量
  • 数组和指针不是一回事,但是可以是一种指针来访问数组

三、二维数组传参的本质

 前面我们学习了一维数组传参的本质,现在让我们来了解一下二维数组传参的本质吧。

首先我们来看一段代码

//void Print(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[0][i][j]);//arr[0]是是第一行元素的数组名,[i]是arr[0]的首元素,[j]是是列数也是第一行的下标
//		}
//		printf("\n");
//	}
//}

//void Print(int(*arr)[5], int r, int c)//*arr是数组指针变量,指向的是数组,又因为数组名是数组的首元素地址,所以这里
// 指的是数组首元素的地址,又因为二维数组的首元素地址为一维数组,这里的一维数组是5个元素,所以是(*arr)[5]
//{
//	int i = 0;
//	for (i = 0; i < r; i++)
//	{
//		int j = 0;
//		for (j = 0; j < c; j++)
//		{
//			printf("%d ", *((*arr + i) + j));//arr[i][j]==*((*arr+i)+j)
//			//arr[i]==&arr[i][0],arr[i]是数组第i行的数组名,又因为数组名是数组首元素的地址所以为&arr[i][0],
//			//这里的[0]为下标,表示数组的首元素。
//		}
//		printf("\n");
//	}
//}

int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	Print(arr, 3, 5);

	return 0;
}

 总结:

  • 二维数组的数组名也是数组首元素的地址
  • 二维数组的数组名就是第一行的地址
  • 第一行是一个一维数组,传过去的就是第一行这个一维数组的地址
  • 二维数组可以理解为一维数组的数组,二维数组的每一行可以看做是一个一维数组,所以二维数组其实是一个一维数组的数组,二维数组的首元素也就是他的第一行

四、函数指针变量

 指针的世界可真是奇妙,居然连函数都有指针,所以大家应该知道指针的重要性了吧。

那什么叫做函数指针变量呢?

函数指针变量用来存放函数地址的,未来通过地址能够调用函数的。

int (*pf) (int x, inty) = &Add;

 

//int Add(int x, int y)
//{
//	return x + y;
//}
//
//int main()
//{
//	
//	printf("%p\n", Add);//和数组一样函数名就是函数的地址。
//	printf("%p\n", &Add);
//	return 0; 
//}

该代码的运行打印结果是一样的,所以&函数名和函数名都是函数的地址

所以我们可以尝试写一个代码来使用函数指针

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

int Sub(int x, int y)
{
	return x - y;
}

int main()
{
	int (*pA)(int, int) = &Sub;
	int ret = (*pA)(4, 5);
	printf("%d\n", ret);
	return 0;
}

这里还有一个需要注意的点:

printf("%d\n", (*pf)(2,3));//*省略时可以不加括号(*pf),但有*时要加括号
printf("%d\n", pf(2,3));

 五、函数指针数组

我们知道,当我们要运行一个复杂的程序时,需要多个函数一起运行,但有时函数多了,在主函数中一个个写出如何调用会显得冗长,所以我们引出了函数指针数组的概念,就是将函数的地址存到一个数组中去

int (*prr[3]) (int x,int y) = {Add, Sub, Div};

六、转移表

通过对函数指针和函数指针数组的学习和了解,下面我们来做一个程序,实现计算器

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//转移表
//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 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 x = 0;
//	int y = 0;
//	int input = 0;
//	int ret = 0;
//	do
//	{
//		menu();
//		printf("请选择: \n");
//		scanf("%d", &input);
//		switch (input)
//		{
//		case 1:
//			printf("请输入操作数:\n");
//			scanf("%d %d", &x, &y);
//			ret = Add(x, y);
//			printf("%d\n", ret);
//		case 2:
//			printf("请输入操作数:\n");
//			scanf("%d %d", &x, &y);
//			ret = Sub(x, y);
//			printf("%d\n", ret);
//		case 3:
//			printf("请输入操作数:\n");
//			scanf("%d %d", &x, &y);
//			ret = Mul(x, y);
//			printf("%d\n", ret);
//		case 4:
//			printf("请输入操作数:\n");
//			scanf("%d %d", &x, &y);
//			ret = Div(x, y);
//			printf("%d\n", ret);
//		case 0:
//			printf("退出\n");
//			break;
//		default:
//			printf("输入错误,请重新选择\n");
//			break;
//		}
//	} while (input);
//	return 0;
//}

但是这样写过于冗余,繁琐,因为一旦运算法则多了之后,就会无限添加case,所以我们可以试着用函数指针和函数指针数组来简化一下。

//函数指针改造也就是转移表
//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 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 x = 0;
//	int y = 0;
//	int input = 0;
//	int ret = 0;
//	int(*pf[5])(int,int) = {0, Add,Sub,Mul,Div};//我们的菜单中其选择从1开始,选0会退出,所以我们通过加上一个0
	//使其下标进行了变化    0, 1,2, 3, 4。//转移表
//	do
//	{
//		menu();
//		printf("请选择: \n");
//		scanf("%d", &input);
//		if (input <= 4 && input >= 1)
//		{
//			printf("请输入操作数\n");
//			scanf("%d %d", &x, &y);
//			ret = pf[input](x, y);//ret=(*pf[input])(x,y)
//			printf("%d\n", ret);
//		}
//		else
//		{
//			if (input == 0)
//			{
//				printf("退出游戏\n");
//			}
//			else
//			{
//				printf("选择错误,请重新选择\n");
//			}
//
//		}
//	} while (input);
//	return 0;
//}

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
社会发展日新月异,用计算机应用实现数据管理功能已经算是很完善的了,但是随着移动互联网的到来,处理信息不再受制于地理位置的限制,处理信息及时高效,备受人们的喜爱。所以各大互联网厂商都瞄准移动互联网这个潮流进行各大布局,经过多年的大浪淘沙,各种移动操作系统的不断面世,而目前市场占有率最高的就是微信小程序,本次开发一套基于微信小程序的生签到系统,有管理员,教师,学生个角色。管理员功能有个人中心,学生管理,教师管理,签到管理,学生签到管理,班课信息管理,加入班课管理,请假信息管理,审批信息管理,销假信息管理,系统管理。教师和学生都可以在微信端注册和登录,教师可以管理签到信息,管理班课信息,审批请假信息,查看学生签到,查看加入班级,查看审批信息和销假信息。学生可以查看教师发布的学生签到信息,可以自己选择加入班课信息,添加请假信息,查看审批信息,进行销假操作。基于微信小程序的生签到系统服务端用Java开发的网站后台,接收并且处理微信小程序端传入的json数据,数据库用到了MySQL数据库作为数据的存储。这样就让用户用着方便快捷,都通过同一个后台进行业务处理,而后台又可以根据并发量做好部署,用硬件和软件进行协作,满足于数据的交互式处理,让用户的数据存储更安全,得到数据更方便。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值