第四节——从深层剖析指针(让你不再害怕指针)

news2024/11/27 3:49:36

在这里插入图片描述

文章目录

  • 1. 字符指针变量
    • 剑指offer例题
  • 2. 数组指针变量
    • 2.1 数组指针变量是什么?
    • 2.2 数组指针变量怎么初始化
  • 3. ⼆维数组传参的本质
    • 代码实现
  • 4. 函数指针变量
    • 4.1 函数指针变量的创建
    • 4.3 两段有趣的代码
      • 4.3.1 typedef 关键字
  • 5. 函数指针数组的定义


1. 字符指针变量

在指针的类型中有一种指针类型叫字符指针char*
简单代码如下:

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

再看如下代码:

 int main()
 {
     const char* pstr = "hello bit.";
     printf("%s\n", pstr);
     return 0;
 }

这里是把⼀个字符串(hello bit.)放到指针变量里了吗?
其实本质是把字符串hello bit 首字符的地址放到了pstr中(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;
}

a.首先,我们要清楚:创建数组名str1为其开辟了一块空间,str2也等同开辟了一块空间,显然,二者的地址并不相同。
我们又知道,数组名比较的是首元素的地址,因此str1 != str2

b.字符串“hello bit.”向内存申请了一块空间,指针str3指向了字符串的首元素的地址,str4也是指向字符串首元素的地址。
由于hello bit.的地址并不发生变化,即str3和str4都是指向同一个地址,那么str3 == str4


2. 数组指针变量

2.1 数组指针变量是什么?

前面我们学习了指针数组,指针数组是⼀种数组,数组中存放的是地址(指针)。

数组指针变量是指针变量?还是数组?
答案是:指针变量。(存放的应该是数组的地址,能够指向数组的指针变量。)

 int (*p)[10];

p先和*结合,说明p是⼀个指针变量,然后指针指向的是⼀个⼤小为10个整型的数组。所以p是
⼀个指针,指向⼀个数组,叫数组指针。(由于[]的优先级高于*,故要用()保证*和p集合)

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

数组指针变量是⽤来存放数组地址的,那怎么获得数组的地址呢?

int arr[10] = {0};
&arr;//得到的就是数组的地址

在这里插入图片描述


3. ⼆维数组传参的本质

前言:有⼀个⼆维数组的需要传参给⼀个函数的时候是这样写的。

#include<stdio.h>

void print(int arr[][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 ", 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 < c;j++)
		{
		  //这段也可以写成printf("%d", *(*(p+i)+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 };
	print(arr, 3, 5);
	return 0;
}

4. 函数指针变量

4.1 函数指针变量的创建

学习完数组指针后,根据类比关系,我们不难得出结论:
函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。
那么,我们该如何知道函数是否有地址呢?

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

int main()
{
	//二者是等价的
	printf("%p\n", &Add);
	printf("%p\n", Add);
	return 0;
}

在这里插入图片描述
可以发现:函数名就是函数的地址
那么有什么办法能将函数地址存起来呢?
仿效数组指针的方式,那肯定有函数指针变量将函数地址存起来(且此形式和数组指针类似)。

通过函数指针变量的方式完成对应操作
#include<stdio.h>

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

int main()
{
	int (*pf)(int, int) = &Add; //pf函数指针变量
    int ret = (*pf)(2, 3);
	//int ret = (pf)(2, 3);
    printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

4.3 两段有趣的代码

《C陷阱和缺陷》有这样两个代码:

  1. (*(void (*)())0)();
  2. void (*signal(int , void(*)(int)))(int);

我们先来解读第一段代码:

int main()
{
    ( * ( void (*)() ) 0 ) ();
	return 0;
}

我们先从0着手
在这里插入图片描述
1.将0强制类型转换成void(*) () 这种类型的函数指针,这个函数没有参数,返回类型是void.
2.然后去调用()地址处的函数.


第二段代码:
在这里插入图片描述

4.3.1 typedef 关键字

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

例:若你觉得unsigned int 写起来不方便

typedef unsigned int uint;

那数组指针重命名又是咋样的呢?

typedef int(*parr_t)[5]

新的命名并不是写在最右边,而应该写在()里边。

同理,函数指针重命名也是如此

typedef void(*pfun_t)(int);

了解了typedef关键字后,我们清楚了它的作用是化繁为简(缺点:旁人并不能明白这个代码是啥类型,因此在旁边加上注释更有利于理解)。
我们可以将上面两段有趣的代码进行简化:

1. typedef void(*pfun_t)(int);
 
2. pfun_t signal(int, pfun_t);

5. 函数指针数组的定义

上一节我们学习了指针的概念及使用,那么是否有一种方式能将函数的地址存放到数组中呢?
这个数组就叫函数指针数组,那函数指针的数组如何定义呢

int (*parr1[3])();

parr1先和[]结合,说明parr1是一个数组,其指向的内容是int (*)()类型的函数指针。
下一节会详细讲解函数指针数组的应用内容(明白其重要性)。

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

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

相关文章

Stable Diffusion绘画 | AI 图片智能扩充,超越PS扩图的AI扩图功能(附安装包)

来到「文生图」页面&#xff0c;输入固定的起手式提示词。 第1步&#xff0c;开启 ControlNet&#xff0c;将需要扩充的图片加载进来&#xff1a; 控制类型选择「Inpaint」&#xff0c;预处理器选择「inpaint_onlylama」&#xff0c;缩放模式选择「缩放后填充空白」&#xff1…

【C++差分数组】3224. 使差值相等的最少数组改动次数|1996

本文涉及知识点 C差分数组 LeetCode3224. 使差值相等的最少数组改动次数 给你一个长度为 n 的整数数组 nums &#xff0c;n 是 偶数 &#xff0c;同时给你一个整数 k 。 你可以对数组进行一些操作。每次操作中&#xff0c;你可以将数组中 任一 元素替换为 0 到 k 之间的 任一…

【目标检测】集装箱缺陷检测数据集1476张5类缺陷VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1476 标注数量(xml文件个数)&#xff1a;1476 标注数量(txt文件个数)&#xff1a;1476 标注…

什么是高斯积分,以及如何求它的值(error function)

文章目录 什么是高斯积分高斯积分与误差函数的关系求值证明过程技巧1 两个相互独立的积分的乘积转为双重积分技巧2 富比尼定理技巧3 坐标系转换总结 什么是高斯积分 高斯积分的公式如下&#xff1a; 高斯积分与误差函数的关系 参考wiki&#xff0c;误差函数的定义如下&…

Ajax和axios简单用法

Ajax Ajax&#xff08;Asynchronous JavaScript And XML&#xff0c;异步的JavaScript和XML&#xff09;。 作用是&#xff1a; 数据交换&#xff1a;通过Ajax可以给服务器发送请求&#xff0c;并获取服务器响应的数据。异步交互&#xff1a;可以在不重新加载整个页面的情况…

【汇编语言】寄存器(CPU工作原理)(一)—— 寄存器的基础知识及存储

文章目录 前言1. 寄存器2. 通用寄存器3. 字在寄存器中的存储结语 前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但仅仅从课程的角度出发就太片面了&#xff0c;其实学习汇编语言可以深入理解计算机底层工作原…

1、如何查看电脑已经连接上的wifi的密码?

在电脑桌面右下角的如下位置&#xff1a;双击打开查看当前连接上的wifi的名字&#xff1a;ZTE-kfdGYX-5G 按一下键盘上的win R 键, 输入【cmd】 然后&#xff0c;按一下【回车】。 输入netsh wlan show profile ”wifi名称” keyclear : 输入完成后&#xff0c;按一下回车&…

初阶C语言-指针

1.指针是什么&#xff1f; 理解指针的两个要点&#xff1a; 1.指针是内存中一个最小单元的编号&#xff0c;也就是地址 2.口头语中说的指针&#xff0c;通常是指指针变量&#xff0c;是用来存放内存地址的变量 总结&#xff1a;指针就是地址&#xff0c;口语中说的指针通常是指…

详解单流Transformer架构中图像/文本/表格/视频/音频经过transformer提取模态特征并生成令牌特征以及CLS到底是什么作用?

系列论文研读目录 文章目录 系列论文研读目录论文中经常出现的如下图中所示结果&#xff0c;其中到底做了什么&#xff1f;1、一张图片 经过self attention 和Feed forward 生成的是什么&#xff1f;具体流程如下&#xff1a;结果&#xff1a; 举个具体计算的例子假设条件1. 自…

【hot100-java】【最长递增子序列】

dp篇 一眼单调栈 之前写的python题解 佬解 单调栈二分查找 我靠&#xff0c;大师我悟了 class Solution {public int lengthOfLIS(int[] nums) {if (nums.length<1){return 1;}List<Integer>stacknew ArrayList<>();stack.add(nums[0]);for(int i1;i<num…

吊打ChatGPT4o!大学生如何用上原版O1辅助论文写作(附论文教程)

目录 1、用ChatGPT生成论文选题2、用ChatGPT生成论文框架3、用ChatGPT进行文献整理4、用ChatGPT进行论文润色5、用ChatGPT进行问题求解6、用ChatGPT进行思路创新7、用ChatGPT进行论文翻译8、如何直接使用ChatGPT4o、o1、OpenAI Canvas 9、OpenAI Canvas增强了啥&#xff1f;10、…

Python中字符串的基本操作

文章目录 1、字符串序号2、字符串切片3、字符串切割4、字符串替换5、字符串删除6、字符串判断7、字符串拼接8、其他操作 字符串是 python 中常用的数据类型&#xff0c;python中可以使用单引号或双引号来创建字符串&#xff0c;python 中没有字符类型&#xff0c;字符在 python…

算法笔记(十二)——BFS 解决 FloodFill(洪水灌溉)

文章目录 图像渲染岛屿数量岛屿的最大面积被围绕的区域 FloodFill&#xff08;洪水灌溉&#xff09; 颜色填充 想Windows画图板中的油漆点一下可以把一个联通的块儿全部染色 本质就是找一块区域里性质相同的联通块 图像渲染 题目&#xff1a;图像渲染 思路 BFS一层一层搜索&…

4.资源《Arduino UNO R3 proteus 电机PID参数整定工程文件(含驱动代码)》说明。

资源链接&#xff1a; Arduino UNO R3 proteus 电机PID参数整定工程文件&#xff08;含驱动代码&#xff09; 1.文件明细&#xff1a; 2.文件内容说明 包含&#xff1a;proteus工程&#xff0c;内含设计图和工程代码。 3.内容展示 4.简述 工程功能可以看这个视频 PID仿真调…

通过 Groovy 实现业务逻辑的动态变更

Groovy 1、需求的提出2、为什么是Groovy3、设计参考1_引入Maven依赖2_GroovyEngineUtils工具类3_GroovyScriptVar类4_脚本规则表设计5_对应的实体类6_数据库访问层7_GroovyExecService通用接口 4、测试5、其他的注意事项6、总结 1、需求的提出 在我们日常的开发过程中&#xf…

一、图解C#教程

一、堆和栈 程序运行时&#xff0c;数据存储在内存中。 使用堆和栈来存储数据 1、栈 栈是一个内存数组&#xff0c;先进后出原则。 可以存储&#xff1a;某些类型变量的值&#xff1b;程序当前执行环境&#xff1b;传递给方法的参数&#xff1b; 入栈&#xff1a;把数据放…

【超级详细解释】力扣每日一题 134.加油站 48. 旋转图像

134.加油站 力扣 这是一个很好的问题。这个思路其实基于一种贪心策略。我们从整个路径的油量变化来理解它&#xff0c;结合一个直观的“最低点法则”&#xff0c;来确保找到正确的起点。 问题的核心&#xff1a;油量差值的累积 对于每个加油站&#xff0c;我们有两个数组&…

精选算法入门——day2

精选算法入门——day2 题目一题干解题思路一解题思路二解题思路三思路三代码 题目二题干解题思路代码 题目三题干解题思路一代码解题思路二代码解题思路三代码 题目四题干解题思路代码 题目一 题干 数组中有一个数字出现的次数超过数组长度的一半&#xff0c;请找出这个数字。…

提高顾客满意度,餐饮业如何开展客户调研?

餐饮行业需明确调研目的&#xff0c;选择合适工具&#xff0c;设计问卷&#xff0c;收集并分析数据&#xff0c;持续追踪优化。通过客户调研&#xff0c;提升服务质量、顾客满意度和竞争力&#xff0c;利用ZohoSurvey等工具实现高效调研。 一、明确调研目的 进行客户调研前&am…

ssm基于JAVA的酒店管理系统的设计与实现

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目 录 第1章 绪论 1 1.1 选题动因 1 1.2 目的和意义 1 1.3 论文结构安排 2 第2章 开发环境与技术 3 2.1 S…