首页 > 其他分享 >指针学习(3)

指针学习(3)

时间:2024-08-16 15:54:51浏览次数:14  
标签:return int ret 学习 数组 printf 指针

 

目录

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;
}

标签:return,int,ret,学习,数组,printf,指针
From: https://blog.csdn.net/2401_85711575/article/details/141246765

相关文章

  • 操作系统实验学习进度
    最近开始学习操作系统和机组的相关知识,写一个学习进度的笔记作为鞭策,其中的dayn不一定全是一天内完成的,同时,大部分文字来源于学习资料rCore-Tutorial-Book第三版。DAY1-应用程序与基本执行环境HelloWorld的执行过程在Ubuntu上利用cargo创建并执行了最简单的rust程序......
  • 【linux学习指南】Linux管理文件与处理数据二(重定向与管道)
    文章目录......
  • 学习SSD—day1_20240814
    1.SSD的基本概念以及结构  SSD是一种以半导体(半导体闪存)作为存储介质吗,使用纯电子电路实现的存储设备。    SSD硬件包括几大组成部分:主控、闪存、缓存芯片DRAM(可选,有些SSD上可能只有SRAM,并没有配置DRAM)、PCB(电源芯片、电阻、电容等)、接口(SATA、SAS、PCIe等),其主体......
  • 基于nodejs+vue每日一课微党课学习管理平台[程序+论文+开题]-计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景在信息化高速发展的今天,党员教育作为党的建设的重要组成部分,面临着如何高效、便捷、创新地传播党的理论知识与实践经验的挑战。传统的党员学习方式受限于时......
  • 《机器学习》 KNN算法、数据可视化 No.1
    一、了解机器学习1、什么是机器学习        机器学习是一种人工智能(AI)的分支,旨在让计算机通过数据自动学习和改进。机器学习算法被设计用于从数据中提取模式和规律,然后利用这些模式和规律来做出预测或做出决策,而无需明确的程序指令。        机器学习的基本......
  • 基于深度学习的图片风格转化
    基于深度学习的图片风格转化作者:禅与计算机程序设计艺术/ZenandtheArtofComputerProgramming1.背景介绍1.1问题的由来图片风格转化是计算机视觉领域一个充满魅力且极具挑战性的课题。它旨在将一张普通照片转换成具有特定艺术风格的图像,如梵高风格、莫奈风格、......
  • 程序员如何平衡日常编码工作与提升式学习?
    在当今技术日新月异的环境下,软件开发人员面临着一个核心挑战:在承担高强度的编码工作的同时,如何持续学习以提升专业技能和适应行业变化。以下,我们从专业角度深入探讨一系列策略,旨在帮助程序员在工作与个人发展之间构建一个高效且可持续的平衡。1.明确目标导向的学习计划确......
  • Java学习笔记6--标识符
    标识符的含义标识符的含义是指在程序中,我们自己定义的内容;譬如,类的名字,方法名称以及变量名称等等,都是标识符。命名规则(硬性要求)1、标识符可以包含英文字母,0-9的数字,美元符号以及下划线。2、不能以数字开头。3、标识符不能是关键字。4、Java标识符大小写敏感,长度无限制。......
  • Java学习笔记5--关键字和保留字
    一、概念Java关键字(KeyWord):对Java的编译器有特殊的意义,他们用来表示一种数据类型或者表示程序的结构.保留字(ReserveWord):即它们在Java现有版本中没有特殊含义,以后版本可能会作为有特殊含义的词,或者该词虽然在Java中没有特殊含义,以后版本也不打算使用,但在其它语言中有特殊含义......
  • Springcloud大学生在线学习平台-计算机毕业设计源码43038
    目录1绪论1.1选题背景与意义1.2国内外研究现状1.3论文结构与章节安排2系统分析2.1可行性分析2.1.1经济可行性2.1.2技术可行性2.1.3社会可行性2.2系统流程分析2.2.1系统开发流程2.2.2用户登录流程2.2.3系统操作流程2.2.4添加信息流程2.2.5......