深入理解指针03

news2024/11/17 3:57:24

1. 字符指针变量

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

⼀般使⽤:

int main()
 {
 char ch = 'w';
 char *pc = &ch;
 *pc = 'w';
 return 0;
 }

还有⼀种使⽤⽅式如下:


int main()
{
	const char* pstr = "hello world";//这⾥是把⼀个字符串放到pstr指针变量⾥了吗?
	printf("%s\n", pstr);
	return 0;
}

代码 const char* pstr = "hello world."; 特别容易让同学以为是把字符串hello world 到字符指针 pstr ⾥了,但是本质是把字符串 放 hello world.⾸字符的地址放到了pstr中。 

《剑指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;
 }

运行结果如下:

这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域, 当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始 化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。 

2. 数组指针变量

 2.1 数组指针变量是什么?

之前我们学习了指针数组,指针数组是⼀种数组,数组中存放的是地址(指针)。 数组指针变量是指针变量?还是数组?

答案是:指针变量。

我们已经熟悉:

• 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。

• 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。

那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量

2.2 数组指针变量怎么初始化

数组指针变量是⽤来存放数组地址的,那怎么获得数组的地址呢?就是我们之前学习的 & 数组名

如果要存放个数组的地址,就得存放在数组指针变量中,如下:

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

我们调试也能看到 &arr 和 p 的类型是完全⼀致的

3. ⼆维数组传参的本质

有了数组指针的理解,我们就能够讲⼀下⼆维数组传参的本质了。 过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的

#include<stdio.h>

void test(int arr[3][5], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

}

int main()
{
	int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };
	test(arr, 3, 5);
	return 0;
}

这⾥实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗? ⾸先我们再次理解⼀下⼆维数组,⼆维数组起始可以看做是每个元素是⼀维数组的数组,也就是⼆维 数组的每个元素是⼀个⼀维数组。那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组。 如下图:

所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀ 维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 型就是数组指针类型 int [5] ,所以第⼀⾏的地址的类 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址传递的是第⼀ ⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:


#include<stdio.h>

void test(int(*p)[5], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d ",*(*(p+i)+j));
		}
		printf("\n");
	}

}

int main()
{
	int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };
	test(arr, 3, 5);
	return 0;
}

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

4. 函数指针变量

4.1 函数指针变量的创建

什么是函数指针变量呢?

根据前⾯学习整型指针,数组指针的时候,我们的类⽐关系,我们不难得出结论:

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

那么函数是否有地址呢? 我们做个测试 :

#include <stdio.h>
 void test()
 {
 printf("hehe\n");
 }
 int main()
 {
 printf("test:  %p\n", test);
 printf("&test: %p\n", &test);
 return 0;
 }

输出结果如下:

确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 & 函数名 的⽅式获得函数的地址。  如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数指针变量的写法其实和数组指针 ⾮常类似。如下: 

 void test()
 {
 printf("hehe\n");
 }
 void (*pf1)() = &test;
 void (*pf2)()= test;
 int Add(int x, int y)
 {
 return x+y;
 }
 int(*pf3)(int, int) = Add;
 int(*pf3)(int x, int y) = &Add;

函数指针类型解析:

 

4.2 函数指针变量的使⽤

通过函数指针调⽤指针指向的函数。


#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*pf3)(int, int) = Add;
	printf("%d\n", (*pf3)(2, 3));
	printf("%d\n", pf3(3, 5));
	return 0;
}

输出结果:

5

8

4.3 两段有趣的代码

 代码1:

 (*(void (*)())0)();

代码2:
void (*signal(int , void(*)(int)))(int);

两段代码均出⾃:《C陷阱和缺陷》这本书

4.3.1 typedef关键字

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

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

 如果是指针类型,能否重命名呢?其实也是可以的,⽐如,将 int* 重命名为 ptr_t ,这样写:

1 typedef int* ptr_t;

但是对于数组指针和函数指针稍微有点区别: ⽐如我们有数组指针类型  int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

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

函数指针类型的重命名也是⼀样的,⽐如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写

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

5. 函数指针数组 

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


int* arr[10];
//数组的每个元素是int*

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

 答案是:parr1

parr1 先和 [] 结合,说明parr1是数组,数组的内容是什么呢?

是 i nt (*)() 类型的函数指针 

6. 转移表

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

举例:计算器的⼀般实现:


#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 dis(int a, int b)
{
	return a / b;
}

void menu()
{
	printf("*********************\n");
	printf("***1.add   2.sub*****\n");
	printf("***3.mul   4.dis*****\n");
	printf("***0.exit       *****\n");
	printf("*********************\n");

}


int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		menu();
		int input = 0;
		printf("请选择:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个数字:");
			scanf("%d%d", &x, &y);
			ret = add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个数字:");
			scanf("%d%d", &x, &y);
			ret = sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个数字:");
			scanf("%d%d", &x, &y);
			ret = mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个数字:");
			scanf("%d%d", &x, &y);
			ret = dis(x, y);
			printf("%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 dis(int a, int b)
{
	return a / b;
}

void menu()
{
	printf("*********************\n");
	printf("***1.add   2.sub*****\n");
	printf("***3.mul   4.dis*****\n");
	printf("***0.exit       *****\n");
	printf("*********************\n");

}


int main()
{
	int x = 0;
	int y = 0;
	int input = 1;
	int ret = 0;
	int (*p[5])(int, int) = { 0,add,sub,mul,dis };
	do
	{
		menu();
		int input = 0;
		printf("请选择:\n");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入两个数字:\n");
			scanf("%d%d", x, y);
			ret = (*p[input])(x, y);
			printf("%d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出游戏\n");
		}
		else
		{
			printf("输入错误\n");
		}
	} while (input);
	return 0;
}

 好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。

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

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

相关文章

【并查集专题】【蓝桥杯备考训练】:网络分析、奶酪、合并集合、连通块中点的数量、格子游戏【已更新完成】

目录 1、网络分析&#xff08;第十一届蓝桥杯省赛第一场C A组/B组&#xff09; 2、奶酪&#xff08;NOIP2017提高组&#xff09; 3、合并集合&#xff08;模板&#xff09; 4、连通块中点的数量&#xff08;模板&#xff09; 5、格子游戏&#xff08;《信息学奥赛一本通》…

win10下自由切换多版本JDK操作

1.在window 系统变量 path路径追加%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 2.下载多版本jdk zip文件解压到到C:\Program Files\Java\目录下 3.定义切换Java版本的bat文件,内容如下 @echo off @echo -------------------welcome to use Java version switch service------------…

设计模式 之 简单工厂模式+工厂模式

简单工厂模式 创建一个工厂类&#xff0c;对实现了同一个接口的多个类进行实例的创建。 //抽象类 人 public abstract class HuMan {public abstract void Talk(); } //黑人实现类 public class BlackHuman : HuMan {public override void Talk(){Console.WriteLine("I a…

python的ITS 信息平台的设计与实现flask-django-nodejs-php

第二&#xff0c;陈列说明该系统实现所采用的架构、系统搭建采用的服务器、系统开发环境和使用的工具&#xff0c;以及系统后台采用的数据库。 最后&#xff0c;对系统进行全面测试&#xff0c;主要包括功能测试、查询性能测试、安全性能测试。 分析系统存在的不足以及将来改进…

新材料正在加速金属3D打印的应用步伐

在金属3D打印领域&#xff0c;材料性能是影响工件综合表现的关键因素&#xff0c;如强度、硬度、耐腐蚀性、抛光性能以及导热性能等&#xff0c;都与材料息息相关&#xff0c;好的材料是推动金属3D打印向更多领域应用的基础。 在这一背景下&#xff0c;上海毅速新材料推出的多款…

二十二 超级数据查看器 讲解稿 其他高级功能

二十二 超级数据查看器 讲解稿 其他高级功能 ​​点击此处 以新页面 打开B站 播放当前教学视频 点击访问app下载页面 百度手机助手 下载地址 ​ 这节课我们讲超级数据查看器高级功能2&#xff0c;讲的是设置密码以外的其他功能。 进入高级功能&#xff0c;先讲一下列表样…

【MySQL】理解关系型数据库&数据的数据模型

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

优化选址问题 | 基于节约算法求解考虑碳排放及带时间窗的物流选址问题附matlab代码

目录 问题代码问题 节约算法(Savings Algorithm)通常用于解决车辆路径问题(Vehicle Routing Problem, VRP),特别是当需要考虑如何有效地组织车辆的路线以最小化总行驶距离时。然而,当问题扩展到包括碳排放和带时间窗的物流选址问题时,算法需要相应的调整。 在这个扩展…

软考高级:软件架构评估概述和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

WM8978 —— 带扬声器驱动程序的立体声编解码器(6)

接前一篇文章&#xff1a;WM8978 —— 带扬声器驱动程序的立体声编解码器&#xff08;5&#xff09; 九、寄存器概览与详解 1. 整体概览 WM8978芯片共有58个寄存器&#xff0c;整体总表如下&#xff1a; 2. 详细说明 在此&#xff0c;只介绍WM8978较为常用的那些寄存器。 &…

Java NIO和IO之间的区别

前言 NIO&#xff08;New IO&#xff09;&#xff0c;这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的&#xff0c;但实现方式不同&#xff0c;NIO主要用到的是块&#xff0c;所以NIO的效率要比IO高很多。在Java API中提供了两套NIO&#xff0c;一套是针对标准输入输出…

【Spring 事务详解】声明式事务概念

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

element-plus下拉框和输入框宽度不一致,:popper-append-to-body=“false“失效

遇到的问题&#xff1a;我修改了el-select输入框的宽度之后&#xff0c;发现下拉选项和输入框的宽度不一样了! 2. 原因控制台看到下拉项的DOM元素时插入到body里面了&#xff0c;使用:popper-append-to-body"false"发现已经废弃&#xff0c;最后发现替换成:teleporte…

Android 动态类加载实现免安装更新

随着Html5技术成熟&#xff0c;轻应用越来越受欢迎&#xff0c;特别是其更新成本低的特点。与Native App相比&#xff0c;Web App不依赖于发布下载&#xff0c;也不需要安装使用&#xff0c;兼容多平台。目前也有不少Native App使用原生嵌套WebView的方式开发。但由于Html渲染特…

宜搭低代码高级认证实操题2 faas连接器加密解密

密钥维护页-保证有一条数据 敏感信息提交页 存档页&#xff0c;只是用来存数据的审批的时候不用这个表提交数据不然会出两条 授权查看页 FaaS连接器先下载好他的示例代码然后按照要求配置好参数直接拷贝进去就行 然后需要在云开发环境里面先new一个terminal然后跑一下./builde…

全智能深度演进,一键成片让视频创作颠覆式提效

全智能一键成片&#xff0c;让内容创作的「边际成本」逼近于零。 大模型和AIGC技术的发展&#xff0c;可以用“日新月异”来形容&#xff0c;其迭代速度史无前例&#xff0c;涌现出的各类垂直应用模型&#xff0c;也使得音视频行业的应用场景更加广泛和多样化。 然而&#xff…

Mora: Enabling Generalist Video Generation via A Multi-Agent Framework

Mora: Enabling Generalist Video Generation via A Multi-Agent Framework PDF: https://arxiv.org/html/2403.13248v1 1 概述 为弥补Sora不开源的缺陷&#xff0c;本文提出多代理框架Mora&#xff0c;整合先进视觉AI代理&#xff0c;复制Sora的全能视频生成能力。Mora能利用…

目标检测——PP-YOLOE-R算法解读

PP-YOLO系列&#xff0c;均是基于百度自研PaddlePaddle深度学习框架发布的算法&#xff0c;2020年基于YOLOv3改进发布PP-YOLO&#xff0c;2021年发布PP-YOLOv2和移动端检测算法PP-PicoDet&#xff0c;2022年发布PP-YOLOE和PP-YOLOE-R。由于均是一个系列&#xff0c;所以放一起解…

网络带宽 (网速) 在线测试

网络带宽 [网速] 在线测试 1. 测网速2. SPEEDTEST3. 下载、上传4. 宽带速率对照表5. 时延6. 抖动7. 丢包8. 测速节点9. 网线References 1. 测网速 https://www.speedtest.cn/ 2. SPEEDTEST https://www.speedtest.net/ ​ 3. 下载、上传 网络数据传输分为发送数据和接收数据…

Uni-app/Vue/Js本地模糊查询,匹配所有字段includes和some方法结合使用e

天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.第一步 需要一个数组数据 {"week": "全部","hOutName": null,"weekendPrice": null,"channel": "门市价","hOutId": 98,"cTime": "…