C语言进阶第二课-----------指针的进阶----------升级版

作者前言

🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂
​🎂 作者介绍: 🎂🎂
🎂 🎉🎉🎉🎉🎉🎉🎉 🎂
🎂作者id:老秦包你会, 🎂
简单介绍:🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂🎂
喜欢学习C语言和python等编程语言,是一位爱分享的博主,有兴趣的小可爱可以来互讨 🎂🎂🎂🎂🎂🎂🎂🎂
🎂个人主页::小小页面🎂
🎂gitee页面:秦大大🎂
🎂🎂🎂🎂🎂🎂🎂🎂
🎂 一个爱分享的小博主 欢迎小可爱们前来借鉴🎂


指针进阶

  • **作者前言**
    • 前提复习
  • 字符指针
    • 指针数组
    • 数组指针
    • 数组传参和指针传参
    • 函数指针

前提复习

  1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  2. 指针(指针变量)的大小是固定的4/8个字节(32位平台/64位平台)。
  3. 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
  4. 指针的运算。(数组元素指针相减等于两者之间的元素个数)
  5. 内存单元是有编号的,编号 == 地址 ==指针

字符指针

用法一:

#include<stdio.h>
int main()
{char a = 'w';char* p = &a;return 0;
}

这里的地址使用很简单,下面还有一种用法

#include<stdio.h>
int main()
{const char* p = "abcdef";printf("%c", *p);printf("%s", p);return 0;
}

这里的p存储的是字符a的地址,我们可以想象成"abcdef"储存在一个数组中,传入p的值为数组名,也就是首元素的地址,就是a

还有一点需要注意的是"abcdef"是一个常量字符串,不能修改,但是会有些人通过*p来修改,这会造成程序崩溃,但是却不会报出警告,往往我们会加一个const 来把错误显示出来

指针数组

指针数组本质上就是一个数组,只是类型不一样而已
比如 字符数组–存放字符的数组
整形数组—存放整形的数组
指针数组—存放指针的数组,存放在数组中的元素都是指针类型
应用1:

#include<stdio.h>
int main()
{int arr[] = { 1,2,3,4,5,6 };int arr1[] = { 2,3,4,5,6,7 };int arr2[] = { 3,4,5,6,7,8 };int arr3[] = { 4,5,6,7,8,9 };int arr4[] = { 5,6,7,8,9,10 };int* arr5[] = { arr,arr1,arr2,arr3,arr4 };//指针数组int i = 0;for (i = 0; i < 5; i++){int j = 0;for (j = 0; j < 6; j++){printf("%d ", arr5[i][j]);}printf("\n");}return 0;
}

上面代码的示意图

这种效果相当于写出了一个二维数组的效果

应用2:


```cpp
#include<stdio.h>
int main()
{char* arr[5] = { "hello bit", "hehe", "penggeC","nitejiuyeke", "C++" };int i = 0;for (i = 0; i < 5; i++){printf("%s\n", arr[i]);}return 0;
}

这里的效果就相当于写成了一个字符串组成的数组,原理就是数组arr元素保存的是字符串首字符地址,通过遍历取出来,然后一一打印出来

数组指针

指针数组是存放指针的数组,
数组指针是啥呢?,
我们可以想象一下,字符指针就是指向字符的指针, 整形指针就是指向整形的指针,所以,数组指针就是指向数组的指针

#include<stdio.h>
int main()
{int a = 10;int* p = &a;char b = 'q';char* pr = &b;int arr[10];int* pa = arr;//数组名是首元素的地址//存在两个例外//sizeof (arr)  这里的arr表示的是整个数组,sizeof(数组名)计算的是整个数组的大小// &arr 取出的是整个数组的地址return 0;
}

//数组名是首元素的地址
//存在两个例外
//sizeof (arr) 这里的arr表示的是整个数组,sizeof(数组名)计算的是整个数组的大小
// &arr 取出的是整个数组的地址,如果从值的方向考虑就会发现arr和&arr的值是一样的
数组地址是计算
可以看出数组名和 &数组名的差别,指针类型决定了指针+1 加了几个字节

#include<stdio.h>
int main()
{int arr[5] = { 1,2,3,4,5 };int(*p)[5] = &arr;//数组指针,里面的大小必须写 !!!!char arr1[100] = "123456";char(*pa)[100] = &arr1;//告诉我们pa是一个指针,指向的是char类型的数组地址char* arr2[5];char* (*pa2)[5] = &arr2;//告诉我们pa2是一个指针,指向的是char*类型的数组地址printf("%p\n", p);printf("%p\n", arr);return 0;
}

数组指针的写法就是 类型 (p)[数组大小,为了好理解我们可以理解为 char p[10],如果写出这样就会变成指针数组了,为了区分开来,才加()区分,指针类似还是char* 没有改变,只是写法不同而已

#include<stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int(*pa)[10] = &arr;int* p = arr;int i = 0;for (i = 0; i < 10; i++){printf("%d", (*pa)[i]);//如果使用pa[i] 等价于*(pa + i)}for (i = 0; i < 10; i++){printf("%d", p[i]);}return 0;

输出的时候我们可以这样理解 arr[i] 可以访问元素, p = arr 所以p[i]可以访问元素
但是pa是数组指针 *pa可以消除& 得到arr , (*pa)[i]可以访问元素

一般我们使用数组指针是在二维数组使用的
在使用之前我们回忆一下一维数组传参给函数时

#include<stdio.h>
void print(int* p, int len)
{int i = 0;for (i = 0; i < len; i++){printf("%d ", p[i]); //p[i] == *(p + i)}}
int main()
{int arr[5] = { 1,2,3,4,50 };print(arr, 5);return 0;
}

p[i] 等价于*(p + i), 所以打印地址一般用 arr + i 或者 &arr[i] 或者 &arr[0] + i

二维数组的应用

#include<stdio.h>
void print(int arr[][3], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){printf("%d ", arr[i][j]);}printf("\n");}
}
void print1(int (*pa)[3], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){printf("%d ", pa[i][j]);//pa[i] == *(pa + i)}printf("\n");}
}
int main()
{int arr[3][3] = { {1,2,3},{2,3,4},{3,4,5} };print(arr, 3, 3);print1(arr, 3, 3);return 0;
}

二维数组的分布
二维数组是一维数组的数组,我们可以想象一下二维数组arr里面的元素是一维数组,传参arr是首元素的地址,也就是把一维数组的整个地址传进去,
这里使用了数组指针pa ,如果要访问第二个元素可以写成 pa[1]

那我们就再扩展一下

int (*parr2)[10];
int (*parr3[10])[5];

第一行的意思就是,parr2是一个数组指针,指向的数组有10个元素,每个元素为整形
第二行代码的意思就是parr3是一个数组,进行存放数组指针的,可以存放10个数组指针,每个数组指针指向一个元素个数为5的数组,并且这个数组的元素类型为int

总结:这里我们要清楚数组指针的使用,及写法,比如 int (*pa)[10] = &arr
如果arr是一维数组,访问每个元素可以写成(pa)[i],不能写成pa[i],因为pa[i]
等价于
(pa + i) ,如果arr为二维数组,我们可以写成pa[i][j]访问每一个元素

数组传参和指针传参

一维数组传参
,数组传参本质上是,传递了首元素的地址数组传参,形参也可以是指针
在这里插入图片描述
数组传参,形参接收,形参的形式可以写成arr[] 或者 arr[大小] 或者 指针变量
如果碰见指针传参指针,我们就要思考要使用哪级指针接收,上图就是二级指针接收arr2数组首元素的地址,arr2数组首元素也是存储地址的

二维数传参
在这里插入图片描述
一级指针传参
在这里插入图片描述
二级指针传参
在这里插入图片描述
int* arr[5] int ** p = arr ,这个也可以
总结:指针传参有很多种传入方法,但是传入的意思是一样的

函数指针

数组指针是指向数组的指针,存放的是整个数组的地址 ----&数组名
那么函数指针是指向函数的指针,存放的是函数的地址,
那怎么得到函数的地址呢?

#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{//&函数名就是函数的地址//函数名也是函数的地址//这两者没有区别printf("%p\n", &Add);printf("%p\n", Add);//函数指针int (*pA)(int, int) = Add;//意思就是pA为函数指针 ,指向的函数的类型为int 参数的类型也为intint (*pA1)(int, int) = &Add;printf("%p\n", pA);printf("%p\n", pA1);int tr = (*pA)(2, 3);// 解引用pA找到函数,并传入参数int re = pA(2, 3);printf("%d\n", tr);printf("%d\n", re);//这种写法是因为Add和&Add是一样的 ,我们调用函数是Add(2,3),所以pA存储Add,我们也可以使用pA(2,3)效果和调用函数是一样的return 0;
}

可以看出获取函数的地址有两种 &函数名或者函数名
我们调用函数除了使用函数名,还可以使用函数指针调用,使用函数指针调用,我们要熟悉函数调用的写法,Add(2,3),因为函数指针存储的是函数名 Add,所以通过函数指针调用函数,可以写成 pA (2,3),或者(*pA)(2,3)(可以理解为解引用找到函数并传参给函数)
在这里插入图片描述
从这个图中我们可以看出函数指针变量的类型

在这里插入图片描述
我们从这张图可以看出
代码1的意思就是 把0 强制类型转换成函数指针类型 ,并且解引用找到函数调用,这个函数没有参数
代码2 是一个函数声明,看到这里是不是很吃惊,那我们分析一下,
在这里插入图片描述
signal函数有两个参数,第一个参数的类型为int 第二个参数的类型是void()(int),因为不可能是函数调用,当我们吧函数去掉就会发现 void ()(int)是一个函数指针类型,如果我们换个类型就很好理解了 int signal( int, void(*)(int) ); 平时我们函数声明就是这样的,只是这个函数指针类型有写法有点特殊,但不改变这就是一个函数指针类型,

如果觉得这样写很麻烦,我们可以把这个函数指针类型重定义
在这里插入图片描述
typedef void(vi_int)(int);注意一下不要写成typedef void()(int) vi_int;这样是不行的

总结:这里我们要熟悉函数指针类型和指针传参 方式有很多但是意思是一样的

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

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

相关文章

图像的转置之c++实现(qt + 不调包)

1.基本原理 图像的转置就是将图像的横坐标和纵坐标交换位置&#xff0c;和矩阵的转置是一样的&#xff0c;公式见下&#xff1a; 2.代码实现&#xff08;代码是我以前自学图像处理时写的&#xff0c;代码很粗糙没做任何优化&#xff0c;但很好理解&#xff09; /*图像的转置函…

Qt画波浪球(小费力)

画流动波浪 #ifndef WIDGET3_H #define WIDGET3_H#include <QWidget> #include <QtMath> class widget3 : public QWidget {Q_OBJECT public:explicit widget3(QWidget *parent nullptr);void set_value(int v){valuev;}int get_value(){return value;} protecte…

nginx负载均衡

目录 负载均衡 nginx的七层代理和四层代理 四层代理与七层代理之间的区别 四层和七层谁的速度快&#xff1f; 正向代理与反向代理 负载均衡 upstream 算法 算法总结 stream 负载均衡 通过反向代理来实现 nginx的七层代理和四层代理 七层是最常用的反向代理方式&am…

Excel---成绩相同者,名次并列排列,三步搞定

需求&#xff1a;一张成绩表&#xff0c;共341行(340条数据&#xff0c;第一条为标题)&#xff0c;根据成绩进行排序&#xff0c;成绩相同进行名次并列 一、选择生成结果的位置&#xff0c;我这里点击了一下E2单元格 二、公式—>插入–>rank函数 数值&#xff1a;D2 表示…

SpringBoot 简单入门部署

1. 环境要求 Java 8 (配置环境变量)Maven 3.3idea 2019.1.2 及以上 1.1 maven 配置 <mirrors><mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/n…

Spring源码——初识Spring容器

Spring源码之工厂&#xff08;容器&#xff09; 为什么把Spring的工厂又叫做容器呢&#xff1f; 工厂的责任是创建对象&#xff0c;但是创建完对象后还要进行存储&#xff08;针对于单例的对象来讲&#xff09;&#xff0c;以供其他地方使用&#xff0c;这就是容器。为了能存…

Redis布隆过滤器的原理和应用场景,解决缓存穿透

目录 一、redis 二、布隆过滤器 三、缓存穿透问题 四、布隆过滤器解决缓存穿透 一、redis Redis&#xff08;Remote Dictionary Server&#xff09;是一种开源的内存数据存储系统&#xff0c;也是一个使用键值对&#xff08;Key-Value&#xff09;方式的高性能数据库。Red…

vscode插件不能搜索安装

1 现象 vscode搜索自己的插件&#xff0c;报错&#xff1a; Error while fetching extensions. HXR failed2 原因 之前用vscode开发golang语言&#xff0c;设置了proxy代理&#xff0c;所以导致错误&#xff0c;删除即可 重启vscode 3 结果

后端进阶之路——Spring Security构建强大的身份验证和授权系统(四)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ 解决算法&#xff0c;一个专栏就够了★ ★ 架…

2542. 最大子序列的分数

题目描述&#xff1a; 主要思路&#xff1a; 这是一个堆的题目&#xff0c;首先将2里边的下标按照数值递减的顺序进行重新排列。依次遍历2的下标同时加上1的数值&#xff0c;堆里边存储1的大小。 class Solution { public:long long maxScore(vector<int>& nums1, …

企业权限管理(五)-订单分页

订单分页查询 PageHelper介绍 PageHelper是国内非常优秀的一款开源的mybatis分页插件&#xff0c;它支持基本主流与常用的数据库&#xff0c;例如mysql、oracle、mariaDB、DB2、SQLite、Hsqldb等。 PageHelper使用 集成 引入分页插件有下面2种方式&#xff0c;推荐使用 Maven …

学习网络基础No.2【深入理解TCP/IP】

引言&#xff1a; 北京时间&#xff1a;2023/8/9/13:04&#xff0c;昨天在摆烂中把网络基础相关知识的博客更新&#xff0c;依然还是上不了C站热榜&#xff0c;我估计是因为我账号热度不够没有上榜资格&#xff0c;也可能是因为前段时间没有积极更新&#xff0c;导致周榜被甩出…

原型模式与享元模式:提升系统性能的利器

原型模式和享元模式&#xff0c;前者是在创建多个实例时&#xff0c;对创建过程的性能进行调优&#xff1b;后者是用减 少创建实例的方式&#xff0c;来调优系统性能。这么看&#xff0c;你会不会觉得两个模式有点相互矛盾呢&#xff1f; 在有些场景下&#xff0c;我们需要重复…

Redis主从复制

目录 前言 一、Redis主从复制 &#xff08;一&#xff09;、概念 &#xff08;二&#xff09;、搭建 &#xff08;三&#xff09;、验证 二、Reids哨兵模式 &#xff08;一&#xff09;、概念 &#xff08;二&#xff09;、搭建 &#xff08;三&#xff09;、验证 总结…

一键获取数百张免费商用人脸!AI人脸生成器来袭

随着科技的发展&#xff0c;人工智能正在渗透到生活的各个角落&#xff0c;设计行业也不例外。在网页、APP、PPT 等界面设计中&#xff0c;设计师经常需要插入真实的人脸素材&#xff0c;以增强作品的真实感和场景化。但是获取素材既不容易&#xff0c;质量和价格也难免成为设计…

C# 完成串口通信RS485

C# 完成串口通信RS485|RS232上下位机交互 第零步&#xff1a; 我用的是电脑usb 转串口的所以首先是驱动程序下载&#xff0c;我们用的是CH341 下载地址&#xff1a;https://www.wch.cn/downloads/CH341SER_EXE.html 第一步&#xff1a;连接机器 RS485 上面有三个端子&#xf…

内网穿透实战应用-配置固定的远程桌面地址【内网穿透、无需公网IP】

配置固定的远程桌面地址【内网穿透、无需公网IP】 文章目录 配置固定的远程桌面地址【内网穿透、无需公网IP】第一步&#xff1a;保留TCP地址第二步&#xff1a;为远程桌面隧道配置固定的TCP地址第三步&#xff1a;使用固定TCP地址远程桌面 使用免费的cpolar生成的远程桌面公网…

多源BFS

多源 超级源点和汇点最短距离[超级汇点]昂贵的聘礼 多源BFS矩阵距离 超级源点和汇点 超级源点跟超级汇点是模拟出来的虚拟点&#xff0c;多用于图中&#xff1a; <1>同时有多个汇点和一个源点&#xff0c;建立超级汇点 1、2、3、6分别到达4或者5或者7的最短路径&#xf…

前端懒加载

懒加载的概念 懒加载也叫做延迟加载、按需加载&#xff0c;指的是在长网页中延迟加载图片数据&#xff0c;是一种较好的网页性能优化的方式。在比较长的网页或应用中&#xff0c;如果图片很多&#xff0c;所有的图片都被加载出来&#xff0c;而用户只能看到可视窗口的那一部分…

接口自动化测试框架及接口测试自动化主要知识点

接口自动化测试框架&#xff1a; 接口测试框架&#xff1a;使用最流行的Requests进行接口测试接口请求构造&#xff1a;常见的GET/POST/PUT/HEAD等HTTP请求构造 接口测试断言&#xff1a;状态码、返回内容等断言JSON/XML请求&#xff1a;发送json\xml请求JSON/XML响应断言&…