”数组指针变量与函数指针变量“宝典

大家好呀,我又来啦!最近我很高效对不对,嘿嘿,被我自己厉害到了。

这一节的内容还是关于指针的,比上一期稍微有点难,加油!!!

点赞收藏加关注,追番永远不迷路。

在这里插入图片描述

文章目录

  • 1. 字符指针变量
  • 2. 数组指针变量
  • 3. 二维数组传参的本质
  • 4. 函数指针变量
  • 5. typedef关键字
  • 6. 函数指针数组
  • 7. 转移表

1. 字符指针变量

根据之前学习的内容理解,字符指针变量就是:指针变量指向的对象是字符类型。

	char ch = 'w';char* pc = &ch;

这期我们学习的是char* pc = "abcdef";这种类型 。我们先从简单的开始理解

char arr[] = "qwerty"; //数组是一块连续的空间,qwerty它们是连续存放的
char* pc = arr; //这里的arr是数组首元素(q)的地址
printf("%c", *pc); //所以打印出来的是q

在此,我们先创建了字符数组【数组里的字符是可以修改的】 ,然后将q的地址储存在指针变量pc中,最后打印出来。(刚开始在这里我想错了,我想将数组的所有的地址传给指针变量,再解引用就可以打印出来数组的整个元素,提醒大家,指针变量的大小只能放4个字节,不可能放下所有地址)

char* pc = "qwerty"; //这里的qwerty是常量字符串
printf("%c", *pc);

与上面一样,这个字符串也是放在连续的空间【常量字符串是不可以修改的,但是你修改的话,并不会报错,所以我们可以这样写const char* pc = “qwerty”,这样如果我们后面忘记它不可以修改,不小心修改了,它会给我们报错的】

char* pc = "qwerty"; 这里的的赋值不是将qwerty放在p里(毕竟全部放进去需要6个字节的空间,p的空间是4个字节,大小也不够),而是将字符串中首字符的地址放在里面。

总结:常量字符串作为表达式的时候,赋值给别人,是将首字符的地址赋给别人的。接下来打印出来印证一下。

在这里插入图片描述

那如何打印整个字符串呢?使用%s打印字符串的时候,只需要提供字符串的地址

在这里插入图片描述

  • 还有一个需要注意的是:当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。

2. 数组指针变量

在上一期我们了解了指针数组(本质是数组,数组中存放的是地址)。这一节我们学习数组指针,从名字可以知道,它的本质是指针。那数组指针变量是用来做什么的?是用来存放数组的地址

我们从之前学的知识类比过来

  • 字符指针:char*(字符指针变量中存放的是字符的地址–char*指向字符的指针)
char ch = 'q';
char* pc = &ch;*和pc,*能说明pc是指针,char说明pc指向的变量是char类型)
  • 整型指针:int*(整型指针变量中存放的是整型的地址–int*是指向整型的指针)
int a = 9;
int* pa = &a;*和pa,*能说明pa是指针,int说明pa指向的变量是int类型)
  • 数组指针:(数组指针变量中存放的是数组的地址(&数组名)指向数组的指针)

如果要存放个数组的地址,就得存放在数组指针变量中
在这里插入图片描述

int arr[10] = { 0 };
int(*p)[10] = &arr;
//p的旁边放*,说明p是指针,指向的数组

这里说明一下为什么指向的是数组:在这一章节链接:C语言之脆皮牌数组宝典我们说过数组的类型是什么,这里数组int arr [10]的类型是int [10]。将(*p)去掉,剩下int [10],所以p指向的对象是数组。

arr(数组首元素地址)----int*----arr+1:跳过4个字节
&arr[0](数组首元素地址)----int* ---- &arr[0]+1 : 跳过4个字节
&arr(数组的地址)----int (*) [9]----&arr+1:跳过整个数组(4*sz个字节)

3. 二维数组传参的本质

以前我们有一个二维数组的需要传参给一个函数的时候是这样的:实参是二维数组,形参也写成二维数组的形式。

int print(int arr[3][5], int h, int l)
{int i, j = 0;for (i = 0; i < h; i++){for (j = 0; j < l; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5} };print(arr, 3, 5);
}

还有什么写法呢?

我们先理解一下二维数组,二维数组其实可以看成一维数组的数组(将一维数组看作是一个元素),数组名是数组首元素的地址,这里的首元素是一行数组,在此之前我们已经知道:数组的地址放在数组指针变量里。那形参写什么嘞?第一行的一维数组的类型是int [5] ,所以第一行的地址的类型是int(*)[5],所以形参是int ( * arr)[5]。

那就意味着**二维数组传参本质上也是传递了地址**,传递的是第一行这个一维数组的地址,那么形参也是可以写成指针形式的。

  • 写代码时需要理解的内容:

在这里插入图片描述

(1)从上图可以看出,若想拿到第i行的内容,可以写 *(arr+i),且这个和arr[i]完全等价。

在这里插入图片描述

(2) 从上图可以看出,arr[i]是第 i 行的数组名,数组名又是首元素的地址,所以arr[i]就是第i行第一个元素的地址,即arr[ i ] 就是 &arr[ i ][ 0 ], * (arr+i) 就是&arr [ i ][ 0 ] 。当写下 * (arr+i),它的意思就是第i行首元素地址。接下来出现 j 的是列 , * (arr+i)+j 的意思是第 i 行第 j 列的元素的地址,再将它解引用就是第 i 行第 j 列的元素: * (* (arr+i)+j )

  • 最终写出来的代码是:
int print(int (*arr)[5], int h, int l)
{int i, j = 0;for (i = 0; i < h; i++){for (j = 0; j < l; j++){printf("%d ", *( * (arr + i) + j));}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5} };print(arr, 3, 5);//arr是第一行数组的地址
}

4. 函数指针变量

函数也有自己的地址

  1. 以最简单的加法为例,看一下函数地址
int add(int m, int n)
{return m + n;
}
int main()
{int x = 90;int y = 89;int r = add(x,y);printf("%p\n", add);printf("%p\n", &add);//函数名和&函数名都是函数的地址,没有区别。有无&均可,它们的含义是一样的
}
  1. 整型变量的地址放在整形指针变量,字符变量的地址放在字符指针变量,所以,函数的地址放在函数指针变量中。(首先是指针,(*pf)。接着它是指向函数,(*pf)(函数的参数)。最后写它的返回值类型,返回类型(*pf)(函数的参数))下面的例子仍然以前面那个加法函数为例
int (*pf)(int,int) = add;  //形参加不加都可以
int (*pf)(int x,int y)= add; 
  1. 如何通过函数地址调用函数呢?先解引用函数指针变量,再将参数传过去即可。
int (*pf)(int,int) = add;
int r = (*pf)(3, 9); //有无*均可,有*好理解
int r1 = pf(3,9);
//正常通过函数名的方式也可以调用它
//int r = add(3,9);
printf("%d\n", r);

在这里插入图片描述

5. typedef关键字

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

  1. 以无符号整型变量为例
typedef unsigned int uint;
int main()
{//这两种方式是一样的unsigned int a = 99;uint a = 99;
}
  1. 以整型指针变量为例
typedef int* zhiint;
int main()
{int a = 9;zhiint pa = &a;
}
  1. 以数组指针变量为例(有略微不同,将新名字放在*旁边)
typedef int(*shuzu)[5];
int main()
{int arr[5] = { 1,2,3,4,5 };shuzu parr= &arr;//原本是int (*parr)[5]=&arr;
}
  1. 以函数指针变量为例(有略微不同,将新名字放在*旁边)
int add(int m, int n)
{return m + n;
}
int (*pff)(int, int);
int main()
{pff = add; 
//原本是:int (*pf)(int, int) = add;
}

6. 函数指针数组

创建一个数组,它的每个元素都是函数的地址。(注意注意:函数的返回值类型,参数个数一定要是相同的才可以)

在此之前,我们只能一个一个的创建函数指针变量:

//函数的声明
int add(int x, int y);
int sub(int x, int y):
int mul(int x, int y);
int div(int x, int y):
int main()
{int (*pa)(int, int) = add;int (*pb)(int, int) = sub;int (*pc)(int, int) = mul;int (*pd)(int, int) = div;
}
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 (*pf[4])(int, int) = { add,sub,mul,div };
//在这里先说明一下: //如果是int(*pf[4]) (int,int):说明pf先和[4]结合,使得pf是一个数组。
//将数组去掉,剩下int ( * )(int,int),这是函数指针。//如果是(*pf)[4],则pf先和*结合,使得pf是指针,指向数组[ 4 ]

在函数指针数组里,通过下标0可以找到add函数的地址,以此类推。

int (*pf[4])(int, int) = { add,sub,mul,div };//0    1  2   3
int i = 0;
for (i = 0; i < 4; i++)
{int r = pf[i](9, 3);//pf[i]是数组中下标为i的元素,(9,3)是将参数传过去printf("%d\n", r);
}

7. 转移表

那函数指针数组可以如何用呢?给大家在计算器里演示一下。

以前我们这样写,很繁琐:

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

到此结束,感谢大家观看,码字不易,留个小心心叭!!!蟹蟹。

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

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

相关文章

AI大事记(持续更新)

文章目录 前言 一、人工智能AI 1.基本概念 2.相关领域 2.1基础设施 2.2大模型 2.3大模型应用 二、大事记 2024年 2024-05-14 GPT-4o发布 2024-02-15 Sora发布 2023年 2023-03-14 GPT-4.0发布 2022年 2022-11-30 ChatGPT发布 总结 前言 2022年11月30日openai的…

从零开始学习Linux(6)----进程控制

1.环境变量 环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数&#xff0c;我们在编写C/C代码时&#xff0c;链接时我们不知道我们链接的动态静态库在哪里&#xff0c;但可以连接成功&#xff0c;原因是环境变量帮助编译器进行查找&#xff0c;环境变量通常具有…

【爬虫之scrapy框架——尚硅谷(学习笔记two)--爬取电影天堂(基本步骤)】

爬虫之scrapy框架--爬取电影天堂——解释多页爬取函数编写逻辑 &#xff08;1&#xff09;爬虫文件创建&#xff08;2&#xff09;检查网址是否正确&#xff08;3&#xff09;检查反爬&#xff08;3.1&#xff09; 简写输出语句&#xff0c;检查是否反爬&#xff08;3.2&#x…

电力系统潮流计算的计算机算法(一)——网络方程、功率方程和节点分类

本篇为本科课程《电力系统稳态分析》的笔记。 本篇为这一章的第一篇笔记。下一篇传送门。 实际中的大规模电力系统包含成百上千个节点、发电机组和负荷&#xff0c;网络是复杂的&#xff0c;需要建立复杂电力系统的同一潮流数学模型&#xff0c;借助计算机进行求解。 简介 …

免费Premiere模板,几何图形元素动画视频幻灯片模板素材下载

Premiere Pro模板&#xff0c;几何图形元素动画视频幻灯片模板 &#xff0c;组织良好&#xff0c;易于自定义。包括PDF教程。 项目特点&#xff1a; 使用Adobe Premiere Pro 2021及以上版本。 19201080全高清。 不需要插件。 包括帮助视频。 免费下载&#xff1a;https://prmu…

Fabric实现多GPU运行

官方的将pytorch转换为fabric简单分为五个步骤&#xff1a; 步骤 1&#xff1a; 在训练代码的开头创建 Fabric 对象 from lightning.fabric import Fabricfabric Fabric() 步骤 2&#xff1a; 如果打算使用多个设备&#xff08;例如多 GPU&#xff09;&#xff0c;就调用…

高级个人主页

高级个人主页 效果图部分代码领取源码下期更新预报 效果图 部分代码 <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" name"viewport" content"widthdevice-width, initial-scale1, maximum-scale1, use…

ESP32重要库示例详解(四):获取NTP时间之time库

在物联网项目中&#xff0c;时间同步和管理是至关重要的功能之一&#xff0c;特别是在需要执行定时任务或记录事件时间戳的场景下。Arduino平台通过其内置的<time.h>库提供了强大的时间处理能力&#xff0c;使得开发者能够方便地与网络时间协议&#xff08;NTP&#xff0…

PDF文件转换为CAD的方法

有时候我们收到一个PDF格式的设计图纸&#xff0c;但还需要进行编辑或修改时&#xff0c;就必须先将PDF文件转换回CAD格式。分享两个将PDF转换回CAD的方法&#xff0c;一个用到在线网站&#xff0c;一个用到PC软件&#xff0c;大家根据情况选择就可以了。 ☞在线CAD网站转换 …

css超出部分省略(单行、多行,多种方法实现)

HTML <p class"text">这是一行测试数据,这是一行测试数据,这是二行测试数据,这是一行测试数据,这是三行测试数据,这是四行测试数据</p>1.单行 .text{width: 200px;border: 1px solid #000000;white-space: nowrap; /* 控制元素不换行 */overflow: hi…

Django图书馆综合项目-学习(2)

接下来我们来实现一下图书管理系统的一些相关功能 1.在书籍的book_index.html中有一个"查看所有书毂"的超链接按钮&#xff0c;点击进入书籍列表book_list.html页面. 这边我们使用之前创建的命名空间去创建超连接 这里的book 是在根路由创建的namespacelist是在bo…

6. RedHat认证-基于公钥的认证方式

6. RedHat认证-基于公钥的认证方式 主要学习客户端访问服务端的时候&#xff0c;免密登录这一方式 注意: 免密登录只是基于公钥认证的一个附带属性(基于公钥认证的方式更加安全&#xff0c;防止黑客暴力破解) 第一步&#xff1a;将客户端生成的秘钥传送到服务器 在客户端通过…

2024中国(厦门)国际医用消毒及感控设备展览会

2024中国&#xff08;厦门&#xff09;国际医用消毒及感控设备展览会 2024 China (Xiamen) International Medical Disinfection And Infection Control Exhibition 致力于打造医用消毒及感控设备产业采购一站式平台 时 间&#xff1a;2024年11月1-3日 November 1-3, 2024 …

一文扫盲(13):电商管理系统的功能模块和设计要点

电商管理系统是一种用于管理和运营电子商务平台的软件系统。它提供了一系列功能模块&#xff0c;帮助企业进行商品管理、订单管理、会员管理、营销推广、数据分析等工作。本文将从以下四个方面介绍电商管理系统。 一、什么是电商管理系统 电商管理系统是一种集成了各种功能模块…

免费的集成组件有哪些?

集成组件是指将多个软件或系统进行整合&#xff0c;以实现更高效、更可靠的数据处理和管理。在数据管理和分析领域&#xff0c;集成组件是不可或缺的工具之一。 在当今高度信息化的时代&#xff0c;集成组件在各行各业的应用中扮演着举足轻重的角色。集成组件能够将不同来源的…

企业安全必备利器:专业级加密软件介绍

随着信息技术的迅猛发展&#xff0c;数据安全问题日益凸显&#xff0c;专业级加密软件应运而生&#xff0c;成为保护数据安全的重要工具。本文将对专业级加密软件进行概述&#xff0c;分析其特点、应用场景及分享。 一、专业级加密软件概述 专业级加密软件是指那些采用高级加密…

三分钟了解计算机网络核心概念-数据链路层和物理层

计算机网络数据链路层和物理层 节点&#xff1a;一般指链路层协议中的设备。 链路&#xff1a;一般把沿着通信路径连接相邻节点的通信信道称为链路。 MAC 协议&#xff1a;媒体访问控制协议&#xff0c;它规定了帧在链路上传输的规则。 奇偶校验位&#xff1a;一种差错检测方…

uniapp怎么使用jsx

安装vitejs/plugin-vue-jsx npm install vitejs/plugin-vue-jsx -Dvite.config.js配置 import { defineConfig } from "vite"; import uni from "dcloudio/vite-plugin-uni"; import vueJsx from vitejs/plugin-vue-jsxexport default defineConfig({plu…

upload-labs靶场通关详解(1-15)

1.pass-01 查看源代码 是js&#xff0c;属于前端校验 可以通过禁用js来上传文件 2.pass-02 根据提示是MIME绕过 MIME&#xff1a;是设定某种扩展名的文件 用一种应用程序来打开的方式类型&#xff0c;当该扩展名文件被访问的时候&#xff0c;浏览器会自动使用指定应用程序来…

冯喜运:5.14黄金价格空头延续反弹空,原油走势分析实时操作

【黄金消息面分析】&#xff1a;周二&#xff08;5月14日&#xff09;亚洲时段&#xff0c;现货黄金窄幅震荡&#xff0c;目前交投于2342美元/盎司。金价周一因获利了结下跌1%&#xff0c;收报2336.10美元/盎司&#xff0c;投资者等待本周的关键通胀数据为今年美国降息提供更多…