【c语言】 -- 指针进阶

📕博主介绍:目前大一正在学习c语言,数据结构,计算机网络。
c语言学习,是为了更好的学习其他的编程语言,C语言是母体语言,是人机交互接近底层的桥梁。
本章来学习指针进阶。
让我们开启c语言学习之旅吧。
目录
一.字符指针
二.指针数组
三.数组指针
3.1 数组指针的定义
3.2 &数组名VS数组名
3.3 数组指针的使用
四. 数组参数、指针参数
4.1 一维数组传参
编辑4.2 二维数组传参
4.3一级指针传参
4.4二级指针传参
五.函数指针
5.1类型重定义
六. 函数指针数组
七. 指向函数指针数组的指针
八.回调函数
前言:
经过上次的c语言初阶指针的学习,今天要总结的知识是进阶指针,干货满满希望大家喜欢。
知识回顾:
- 内存会划分为一个个的内存单元,每个内存单元都有一个独立的编号——称为地址,地址在c语言中也被称为指针。
- 指针(地址)需要存储起来 —— 存储到变量中,这个变量就被称为指针变量。
- 指针的大小是固定的4/8个字节(32位平台/64位平台)
- 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限
那我们继续来探讨指针的高级主题
一.字符指针
在前面的学习中,我们已经接触过字符指针了。
比如

注:pc就是一个字符指针
还有一种使用方式如下:
int main()
{const char* pstr = "hello word.";//这里是把一个字符串放到pstr指针变量里了吗?printf("%s\n", pstr);return 0;
}
代码 const char* pstr = "hello word.";
特别容易让同学以为是把字符串hello word放在字符指针psat里了,但本质是把字符串hello word.的首字符地址放到了pstr中。

上面代码的意思是把一个常量字符串的首字符 h 的地址存放到指针变量pstr中。
一道非常经典的题目
#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");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}
运行结果

解析:这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4同。
二.指针数组
在初阶指针里面我们也学了指针数组,指针数组是一个存放指针的数组。
int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
三.数组指针
3.1 数组指针的定义
数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉:
整形指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
那数组指针应该是:能够指向数组的指针。
下面代码哪个是数组指针?
int *p1[10];
int (*p2)[10];
解析:
int *p1[10]:p1会和 [ ] 去结合,[ ] 的优先级要高于*号的,所以p1就是一个数组啦,每个元素是int *,这就是一个存放指针的数组。
int (*p2) [10] ;p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
3.2 &数组名VS数组名
对于下面的数组:
int arr[10];
arr 和 &arr 分别是啥?
我们知道arr是数组名,数组名表示数组首元素的地址。
那&arr数组名到底是啥?
我们看一段代码:

可见数组名和&数组名打印的地址是一样的。
难道两个是一样的吗?
我们再看一段代码:

根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。(细细体会一下)
本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40.
数组名绝大部分情况下是数组首元素的地址
但是有2个例外:
- sizeof(数组名) - sizeof内部单独放一个数组名的时候,数组名表示的整个数组,计算得到的是数组的总大小
- &arr - 这里的数组名表示整个数组,取出的是整个数组的地址,从地址值的角度讲和数组首元素的地址是一样的,但是意义不一样。
3.3 数组指针的使用
那数组指针是怎么使用的呢?
既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
看代码:
#include <stdio.h>
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,0};int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p//但是我们一般很少这样写代码return 0;
}
一个数组指针的使用
#include <stdio.h>
void print_arr1(int arr[3][5], 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 print_arr2(int(*arr)[5], 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");}
}
int main()
{int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };print_arr1(arr, 3, 5);//数组名arr,表示首元素的地址//但是二维数组的首元素是二维数组的第一行//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址//可以数组指针来接收print_arr2(arr, 3, 5);return 0;
}
四. 数组参数、指针参数
在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
4.1 一维数组传参

4.2 二维数组传参

4.3一级指针传参

4.4二级指针传参

五.函数指针
函数在调用的时候,也会去开辟空间,也会有相应的地址,那么存放函数地址的指针就叫做函数指针。
先来看一段代码
输出的是两个地址,这两个地址是 test 函数的地址。 那我们的函数的地址要想保存起来,怎么保存? 下面我们,,看代码。
void test( )
{printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();
pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。
举一个小栗子

做一个小练习
char test(int c,float *pf)
{}
int main()
{char(*pf)(int ,float *)=test ;return 0;
}
5.1类型重定义
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10]
typdef是c语言中的类型重定义,可以将复杂的代码简单化。
比如:
void (*signal(int , void(*)(int)))(int);//简化typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t)
六. 函数指针数组
函数指针数组:数组的每个元素是函数指针类型,那函数指针的数组如何定义呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10]
答案是:parr1 parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。
函数指针数组的用途:转移表
拿到函数的地址之后,就可以通过函数的地址去调用函数,将多个函数地址存放在一个数组里面,实现一个计算器
#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 = 0;int y = 0;int input = 1;int ret = 1;int(*p[5])(int x, int y) = { 0, add, sub, mul, div };//转移表while (input){printf("**********************\n");printf("***1.add 2.sub***\n");printf("***3.mul 4.div***\n");printf("**********************\n");printf("请选择: ");scanf("%d", &input);if (input <= 4 && input >= 1){printf("请输入操作数:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);}elseprintf("输入有误\n");printf("ret=%d\n", ret);}return 0;
}
通过这种转移表的方式来实现一个计算器很明显代码的效率会特别高,将来想要添加功能会更加方便。
七. 指向函数指针数组的指针
指向函数指针数组的指针是一个 指针 指针指向一个 数组 ,数组的元素都是 函数指针 ;
那该如何定义呢?
int (*pf)(int ,int) //函数指针
int (*pfArr [ ])(int ,int) //函数指针数组
int ( * (*p) [4] ) (int , int) = &pfArr; //这个pfArr是函数指针数组的地址,p就是指向函数指针 数组的指针。
八.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
利用回调函数来实现一个计算器
#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;
}void menu()
{printf("**********************\n");printf("***1.add 2.sub***\n");printf("***3.mul 4.div***\n");printf("**********************\n");
}
void Calc(int(*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}
int main()
{int input = 0;do{menu();printf("请选择");scanf("%d", &input);switch (input){case 1:Calc(add); //回调函数break;case 2:Calc(sub);break;case 3:Calc(mul);break;case 4:Calc(div);break;case 0:printf("退出计算器\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}
运行原理:
Calc是一个函数,它的参数是一个函数指针,我们需要Calc实现什么功能的时候,只需要把对应的地址传进去,就可以实现想要的功能,这个通过函数指针调用的函数就叫做回调函数。
运行逻辑

本期就到这里啦!创作不要请多多支持,下期讲解qsort函数
相关文章:
【c语言】 -- 指针进阶
📕博主介绍:目前大一正在学习c语言,数据结构,计算机网络。 c语言学习,是为了更好的学习其他的编程语言,C语言是母体语言,是人机交互接近底层的桥梁。 本章来学习指针进阶。 让我们开启c语言学习…...
软件压力测试对软件产品起到什么作用?
一、软件压力测试是什么? 软件压力测试是一种通过模拟正常使用环境中可能出现的大量用户和大数据量的情况,来评估软件系统在压力下的稳定性和性能表现的测试方法。在软件开发过程中,经常会遇到一些性能瓶颈和稳定性问题,而软件压力测试的作…...
Stephen Wolfram:那么…ChatGPT 在做什么,为什么它有效呢?
So … What Is ChatGPT Doing, and Why Does It Work? 那么…ChatGPT在做什么,为什么它有效呢? The basic concept of ChatGPT is at some level rather simple. Start from a huge sample of human-created text from the web, books, etc. Then train…...
机器学习基础(五)
决策树 决策树是一种预测模型,它代表着对象属属性与对象值之间的一种映射关系。树中的每个节点代表一个对象,分叉路径(或者叫树枝)则代表一个属性值。 决策树常用方法: 分类树分析,是一种监督学习,用于预计结果可能为离散类型。 回归树分析,用于预计结果为实数。 CART,…...
阿里云服务器安装WordPress网站教程基于CentOS系统
阿里云百科分享使用阿里云服务器安装WordPress博客网站教程,WordPress是使用PHP语言开发的博客平台,在支持PHP和MySQL数据库的服务器上,您可以用WordPress架设自己的网站,也可以用作内容管理系统(CMS)。本教…...
【100天精通python】Day37:GUI界面编程_PyQT从入门到实战(上)
目录 专栏导读 1 PyQt6 简介: 1.1 安装 PyQt6 和相关工具: 1.2 PyQt6 基础知识: 1.2.1 Qt 的基本概念和组件: 1.2.2 创建和使用 Qt 窗口、标签、按钮等基本组件 1.2.3 布局管理器:垂直布局、水平布局、网格布局…...
数据结构—散列表的查找
7.4散列表的查找 7.4.1散列表的基本概念 基本思想:记录的存储位置域关键字之间存在对应关系 对应关系——hash函数 Loc(i) H(keyi) 如何查找: 根据散列函数 H(key) k 查找key9,则访…...
Expo项目 使用Native base UI库
装包: yarn add native-base expo install react-native-svg12.1.1 Index.js: import React from react import { View, Text } from react-native import useList from ./useList import { NativeBaseProvider, Button, Box } from native-base import styles f…...
74、75、76——tomcat项目实战
tomcat项目实战 tomcat 依赖 java运行环境,必须要有jre , 选择 jdk1.8 JvmPertest 千万不能用 kyj易捷支付 项目机器 选择 一台机器 ,安装jdk1.8的机器下载tomcat的包 上传到机器,解压tomcattomcat文件 bin文件夹: 启动文件 堆栈配置文件 catalina.sh JAVA_OPTS="-Xm…...
jmeter errstr :“unsupported field type for multipart.FileHeader“
在使用jmeter测试接口的时候,提示errstr :"unsupported field type for multipart.FileHeader"如图所示 这是因为我们 在HTTP信息头管理加content-type参数有问题 直接在HTTP请求中,勾选: use multipart/form-data for POST【中文…...
C#调用C++ DLL传参byte[]数组字节值大于127时会变为0x3f的问题解决
最近做了一个网络编程的DLL给C#调用,DLL中封装了一个TCP Client的函数接口,如下所示 //C TCP报文发送接口 int TcpClient_send(unsigned char* buffSend, unsigned int nLen) {unsigned char buff[1024];int len StringToHex(buffSend, buff);int nRet…...
【vue3+xlxs+xlsx-style-vite】vue3项目中使用xlsx插件实现Excel表格的导出和解析,已实现
在vue3项目中使用xlsx插件实现Excel表格的导出和解析 1、xlsx插件包官方 xlsx插件包官方 2、FileReader官方文档:FileReader官方文档 安装xlsx和xlsx-style-vite、file-saver npm install xlsx npm install xlsx-style-vite npm install file-saverpackage.json中查…...
Doris2.0时代的一些机遇和挑战!
300万字!全网最全大数据学习面试社区等你来! 上个周五的时候,Doris官宣了2.0版本,除了在性能上的大幅提升,还有一些特性需要大家特别关注。 根据官网的描述,Doris在下面领域都有了长足进步: 日志…...
Leetcode-每日一题【剑指 Offer 32 - I. 从上到下打印二叉树】
题目 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。 例如: 给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 返回: [3,9,20,15,7] 提示: 节点总数 < 1000 解题思路 1.题目要求我们从…...
网神 SecGate 3600 防火墙任意文件上传漏洞复现
0x01 产品简介 网神SecGate3600下一代极速防火墙(NSG系列)是基于完全自主研发、经受市场检验的成熟稳定网神第三代SecOS操作系统 并且在专业防火墙、VPN、IPS的多年产品经验积累基础上精心研发的高性能下一代防火墙 专门为运营商、政府、军队、教育、大型…...
把独显塞回CPU,新核显能够媲美RTX 30、40系显卡了
上个月,AMD 发布了 Zen4 架构 R5 7600X 的无核显版 - 7500F 。 各种数据评测和玩家实际体验大家也已经看过了,说是变相降价一点不错。 原因也很简单,感谢 Intel 。 Jon Peddie Research 刚出炉报告显示,2023 第二季度 AMD 客户端…...
Python爬虫——scrapy_工作原理
引擎向spiders要url引擎把将要爬取的url给调度器调度器会将url生成的请求对象放入到指定的队列中从队列中出队一个请求引擎将请求交给下载器进行处理下载器发送请求获取互联网数据下载器将数据返回给引擎引擎将数据再次给到spidersspiders通过xpath解析该数据,得到数…...
gRPC vs REST:创建API的方法比较
本文对gRPC和REST的特征和区别进行了介绍,这可能是当今创建API最常用的两种方法。 文章目录 一、gRPC的介绍 二、什么是REST? 三、什么是gRPC? 四、gRPC和REST的比较 (1)底层HTTP协议 (2)支持的数据…...
缓存平均的两种算法
引言 线边库存物料的合理性问题是物流仿真中研究的重要问题之一,如果线边库存量过多,则会对生产现场的布局产生负面影响,增加成本,降低效益。 写在前面 仿真分析后对线边Buffer的使用情况进行合理的评估就是一个非常重要的事情。比较关心的参数包括:缓存位最大值…...
SpringBoot的配置文件(properties与yml)
文章目录 1. 配置文件的作用2. 配置文件格式3. 配置文件的使用方法3.1. properties配置文件3.1.1. 基本语法和使用3.1.2. properties优缺点分析 3.2. yml配置文件3.2.1. 基本语法与使用3.2.2. yml中单双引号问题3.2.3. yml配置不同类型的数据类型及null3.2.4. 配置对象3.2.5. 配…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
企业大模型服务合规指南:深度解析备案与登记制度
伴随AI技术的爆炸式发展,尤其是大模型(LLM)在各行各业的深度应用和整合,企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者,还是积极拥抱AI转型的传统企业,在面向公众…...
动态规划-1035.不相交的线-力扣(LeetCode)
一、题目解析 光看题目要求和例图,感觉这题好麻烦,直线不能相交啊,每个数字只属于一条连线啊等等,但我们结合题目所给的信息和例图的内容,这不就是最长公共子序列吗?,我们把最长公共子序列连线起…...
标注工具核心架构分析——主窗口的图像显示
🏗️ 标注工具核心架构分析 📋 系统概述 主要有两个核心类,采用经典的 Scene-View 架构模式: 🎯 核心类结构 1. AnnotationScene (QGraphicsScene子类) 主要负责标注场景的管理和交互 🔧 关键函数&…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
