指针 (5)

news2024/10/3 5:06:39

目录

1. 字符指针变量

2. 数组指针变量

3. ⼆维数组传参的本质

 4. 函数指针变量

5.typedef 关键字

6 函数指针数组

7.转移表

计算器的⼀般实现


1. 字符指针变量

在指针的类型中我们知道有⼀种指针类型为字符指针 char*

#include <stdio.h>
int main()
{
	char* ch = "asdfff ";//这个表达式就是字符指针表达式
	//这里的字符串是常量字符串,将a的地址赋值给ch

	return 0;
}

常量字符串是不能被修改的 , 使用% s 打印字符串时候只需要首字符的地址即可

 下面大家来看这个代码:可以先思考一下这个代码的运行结果是什么?

我们发现代码中的字符串不都是一样吗? 那一定就是都相同了。可结果是这样吗?

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

 答案并不是:这是为什么呢?

 

这里因为str 1和str2 都是数组变量 ,当然虽然他们存的字符都相同,但是他们的地址不同,所以就有了str 1 不等与str2,数组是可以被修改的 。

那么str3 和ste4 为什么又相等呢?由代码中我们可以看到 str 3和str4 前面都有加const 来修饰变量,当const 修饰字符串就成为了常变量字符串,此时这里的常量字符串是不可被修改的,所以str3 和str 4 的地址是同一个地址。

2. 数组指针变量

指向数组的指针,存放数组的地址 

int main()
{
    int arr[5] = { 1,2,3,4,5 };
    int (*p) [5] = &arr;//这里的p就是数组指针,p中存放的是数组的地址

    return 0;
}

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

3. ⼆维数组传参的本质

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

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

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[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 };
	Print(arr, 3, 5);


	return 0;

⾸先我们再次理解⼀下⼆维数组,⼆维数组其实可以看做是每个元素是⼀维数组的数组,也就是⼆维 数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。

然而第一行就是二位数组的首地址。一维数组的首地址就是一个元素

 那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏就是这个⼀维数组的地址。

 代码优化:

void  Print( int ( *p)[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 ",*(*(p+i)+j));// *(p+i)等价于  *p[i]就是找到一行的第一个元素的地址   +j是因为j是列数,找到下标元素的内容
		}
		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;
}

 可以结合上面的图来理解:

 画的有些潦草,不过大概就是这么个意思

 总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式

 4. 函数指针变量

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

void  Add(int x, int y)
{

    return x + y;
 }
int main()
{
    int arr = 10;
    int (*pa)(int, int) = &Add;//pa就是函数指针

int ret = (*pa)(4, 5);
printf("%d ", ret);
    return 0;
}

函数指针类型解析

int (*pf3) (int x, int y)
 | | ------------ 
 | | |
 | | pf3指向函数的参数类型和个数的交代
 | 函数指针变量名
 pf3指向函数的返回类型
int (*) (int x, int y)

5.typedef 关键字

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

 ⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤

typedef unsigned int uint;//将ungsigned int 简化为uint 名字自己来写
int main()
{
    unsigned int n;

    uint n2;

    return 0;
}

 类型也可以 自定义名称,与类型相同

#include <stdio.h>
typedef int(*type)[5]; //当指针类型定义时候要把名称写在*号右面
int main()
{
	int a = 10;
	int(*p)[5] = &a;
	type p2 = &a;//type就是指针类型
	return 0;
}

6 函数指针数组

数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组,

#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 Dvi(int x, int y)
{
	return x / y;
}
int main()
{
	/*int (*p1)(int, int) = Add;
	int (*p2)(int, int) = Sub;
	int (*p3)(int, int) = Mul;
	int (*p4)(int, int) = Dvi;*/
	//此时上面的函数指针也可以优化为下面的函数指针数组 ,来存放这些地址
	int (*p[4])(int, int) = { Add ,Sub,Mul,Dvi };//这里存储的类型必须相同,相同类型元素
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int ret = p[i](9, 3);//p[i]是上面函数指针数组 
		printf("%d\n", ret);
	}

	return 0;
}

7.转移表

函数指针数组的⽤途:转移表 就是函数指针数组

计算器的⼀般实现

计算器的实行代码。

#include <stdio.h>
int   Sum(int x, int y)
{
	printf("请输入两个操作数\n");
	scanf("%d%d", &x, &y);
	return x + y;
}
int   Sub(int x, int y)
{
	printf("请输入两个操作数\n");
	scanf("%d%d", &x, &y);
	return x - y;
}
int   Mul(int x, int y)
{
	printf("请输入两个操作数\n");
	scanf("%d%d", &x, &y);
	return x * y;
}
int   Div(int x, int y)
{
	printf("请输入两个操作数\n");
	scanf("%d%d", &x, &y);
	return x / y;
}

void mune()
{
	printf("********************\n");
	printf("*****1.sum 2.sub****\n");
	printf("*****3.mul 4.div****\n");
	printf("*****  0 . exit  ***\n");
	printf("********************\n");
 }
int main()
{
	int input = 0;
	int a = 0;
	int b = 0;
	int ret = 0;
	do 
	{
		mune();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			 ret =Sum(a,b);
			printf("%d\n ", ret);
			break;
		case 2:

			ret =Sub(a, b);
			printf("%d\n ", ret);
			break;
		case 3:
			ret =Mul(a, b);
			printf("%d\n ", ret);
			break;
		case 4:
			ret =Div(a, b);
			printf("%d\n", ret);
			break;
		case  0:
			printf("退出计算器\n");
			break;
		default :
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);

	return 0; 

}

但是如果发现你不仅仅需要这几个,老板让你增加几个字符操作,上面代码太麻烦了

可以使用函数指针数组来实现这个计算器

#include<stdio.h>

int   Sum(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;
}
void mune()
{
	printf("********************\n");
	printf("*****1.sum 2.sub****\n");
	printf("*****3.mul 4.div****\n");
	printf("*****  0 . exit  ***\n");
	printf("********************\n");
}
int main()
{
	int input = 0;
	int a = 0;
	int b = 0;
	int ret = 0;
	int (*p[5])(int, int) = { 0,Sum,Sub,Mul,Div };//假设要加入>> 操作符 可以在后面添加,
	//指针数量增加,添加0 是为了下标元素对应
	do
	{
		mune();
		printf("请选择:");
		scanf("%d", &input);
		if ( input >=1&&input <=4)
		{
			printf("请输入两个操作数\n");
			scanf("%d%d", &a, &b);
			ret = p[input](a, b);
			printf("%d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	} while (input);

	return 0;
}

上面的代码已经得到了优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2185587.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ARM assembly: Lesson 10

今天&#xff0c;我们来看一下基于ARM汇编&#xff0c;如何实现函数的调用。 基础知识 在ARM汇编中&#xff0c;函数的前四个参数存放于 R0~R3寄存器中, 剩余的参数存放于栈中&#xff0c;返回值存放于r0。在栈中存放数值&#xff0c;可以避免在调用过程中&#xff0c;数据的…

记一次炉石传说记牌器 Crash 排查经历

大家好这里是 Geek技术前线。最近在打炉石过程中遇到了HSTracker记牌器的一个闪退问题&#xff0c;尝试性排查了下原因。这里简单记录一下 最近炉石国服回归&#xff1b;由于设备限制&#xff0c;我基本只会在 Mac 上打炉石。并且由于主要打竞技场&#xff0c;所以记牌器是必不…

解决问题AttributeError: “safe_load“ has been removed, use

解决问题AttributeError: "safe_load" has been removed, use~ 1. 问题描述2. 解决方法 1. 问题描述 在复现cdvae代码时&#xff0c;运行 python scripts/compute_metrics.py --root_path MODEL_PATH --tasks recon gen opt评估模型时&#xff0c;出现以下问题。 …

Pikachu-Cross-Site Scripting-xss盲打

xss盲打&#xff0c;不是一种漏洞类型&#xff0c;而是一个攻击场景&#xff1b;在前端、或者在当前页面是看不到攻击结果&#xff1b;而是在后端、在别的页面才看到结果。 登陆后台&#xff0c;查看结果&#xff1b;

Custom C++ and CUDA Extensions - PyTorch

0. Abstract 经历了一波 pybind11 和 CUDA 编程 的学习, 接下来看一看 PyTorch 官方给的 C/CUDA 扩展的教程. 发现极其简单, 就是直接用 setuptools 导出 PyTorch C 版代码的 Python 接口就可以了. 所以, 本博客包含以下内容: LibTorch 初步;C Extension 例子; 1. LibTorch …

python-鸡尾酒疗法/图像相似度/第n小的质数

一&#xff1a;鸡尾酒疗法 题目描述 鸡尾酒疗法&#xff0c;原指“高效抗逆转录病毒治疗”&#xff08;HAART&#xff09;&#xff0c;由美籍华裔科学家何大一于 1996 年提出&#xff0c;是通过三种或三种以上的抗病毒药物联合使用来治疗艾滋病。该疗法的应用可以减少单一用药产…

什么是ETL?什么是ELT?怎么区分它们使用场景

ELT和ETL这两种模式从字面上来看就是一个顺序颠倒的问题&#xff0c;每个单词拆开来看其实都是一样的。E代表的是Extract&#xff08;抽取&#xff09;&#xff0c;也就是从源端拉取数据&#xff1b;T代表的是Transform&#xff08;转换&#xff09;&#xff0c;对一些结构化或…

Visual Studio2017编译GDAL3.0.2源码过程

一、编译环境 操作系统&#xff1a;Windows 10企业版 编译工具&#xff1a;Visual Studio 2017旗舰版 源码版本&#xff1a;gdal3.0.2 二、生成解决方案 打开Visual Studio 2017的x64本机生成工具&#xff0c;切换到gdal3.0.2源码根目录&#xff1b;执行generate_vcxproj.b…

D25【 python 接口自动化学习】- python 基础之判断与循环

day25 for 循环 学习日期&#xff1a;20241002 学习目标&#xff1a;判断与循环&#xfe63;-35 for 循环&#xff1a;如何遍历一个对象里的所有元素&#xff1f; 学习笔记&#xff1a; for 循环与while循环的区别 for循环的定义 使用for循环遍历序列 使用for循环遍历字典…

【理论科学与实践技术】数学与经济管理中的学科与实用算法

在现代商业环境中&#xff0c;数学与经济管理的结合为企业提供了强大的决策支持。包含一些主要学科&#xff0c;包括数学基础、经济学模型、管理学及风险管理&#xff0c;相关的实用算法和这些算法在中国及全球知名企业中的实际应用。 一、数学基础 1). 发现人及著名学者 发…

目标检测评价指标

混淆矩阵&#xff08;Confusion Matrix&#xff09; 准确率&#xff08;accuracy&#xff09; 准确率&#xff1a;预测正确的样本数 / 样本数总数 &#xff08;正对角线 / 所有&#xff09; 精度&#xff08;precision&#xff09; 精度&#xff1a;预测正确里面有多少确实是…

深入理解MySQL中的MVCC原理及实现

目录 什么是MVCC&#xff1f; MVCC实现原理 Undo Log 日志 InnoDB行格式 undo日志格式 1. insert undo log格式 2. update undo log格式 事务回滚机制 Read View MVCC案例分析 案例01-读已提交RC隔离级别下的可见性分析 案例02-可重复读RR隔离级别下的可见性分析 什…

英语词汇小程序小程序|英语词汇小程序系统|基于java的四六级词汇小程序设计与实现(源码+数据库+文档)

英语词汇小程序 目录 基于java的四六级词汇小程序设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师&a…

【优选算法】(第十六篇)

目录 连续数组&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 矩阵区域和&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 连续数组&#xff08;medium&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&#xff08;LeetCode&a…

【重学 MySQL】五十一、更新和删除数据

【重学 MySQL】五十一、更新和删除数据 更新数据删除数据注意事项 在MySQL中&#xff0c;更新和删除数据是数据库管理的基本操作。 更新数据 为了更新&#xff08;修改&#xff09;表中的数据&#xff0c;可使用UPDATE语句。UPDATE语句的基本语法如下&#xff1a; UPDATE ta…

前端学习第二天笔记 CSS选择 盒子模型 浮动 定位 CSS3新特性 动画 媒体查询 精灵图雪碧图 字体图标

CSS学习 CSS选择器全局选择器元素选择器类选择器ID选择器合并选择器 选择器的优先级字体属性背景属性文本属性表格属性表格边框折叠边框表格文字对齐表格填充表格颜色 关系选择器后代选择器子代选择器相邻兄弟选择器通用兄弟选择器 CSS盒子模型弹性盒子模型父元素上的属性flex-…

Linux 安装 yum

第一步&#xff1a;下载安装包 这里以 CentOS 7 为例 wget https://vault.centos.org/7.2.1511/os/x86_64/Packages/yum-3.4.3-132.el7.centos.0.1.noarch.rpm wget https://vault.centos.org/7.2.1511/os/x86_64/Packages/yum-metadata-parser-1.1.4-10.el7.x86_64.rpm wget…

计算机网络(十) —— IP协议详解,理解运营商和全球网络

目录 一&#xff0c;关于IP 1.1 什么是IP协议 1.2 前置认识 二&#xff0c;IP报头字段详解 三&#xff0c;网段划分 3.1 IP地址的构成 3.2 网段划分 3.3 子网划分 3.4 IP地址不足问题 四&#xff0c;公网IP和私有IP 五&#xff0c;理解运营商和全球网络 六&#xff…

硬件面试(一)

网上别人的硬件面试记录&#xff0c;察漏补缺&#xff1a; 1.骄傲容易被打脸&#xff01; 励磁电感和谐振电感的比值K大小有什么含义: 励磁电感和谐振电感的比值 KKK 通常用来衡量电路的特性。当 KKK 较大时&#xff0c;表示励磁电感相对于谐振电感较强&#xff0c;可能导致…

力扣题解1870

这道题是一个典型的算法题&#xff0c;涉及计算在限制的时间内列车速度的最小值。这是一个优化问题&#xff0c;通常需要使用二分查找来求解。 题目描述&#xff08;中等&#xff09; 准时到达的列车最小时速 给你一个浮点数 hour &#xff0c;表示你到达办公室可用的总通勤时…