C语言指针详解:从数组、指针基础到多级指针与函数回调

发布时间:2026/10/29 11:11:25

C语言指针详解:从数组、指针基础到多级指针与函数回调 数组是一段存放数据的连续内存空间。数组名表示第一个数组元素的地址内存偏移量取决于数据类型。对数组名取地址数组名时表示整个数组此时内存偏移量为整个数组的大小sizeof(数组名)。数组传参会退化为了效率。C 语言不会在传参时拷贝整个数组那样太慢且费栈空间而是只传一个地址。指针是地址内存访问偏移量为4个字节(32位处理器)/8字节64位处理器无论指向的数据类型如何都以地址类型进行解析。数组名不是指针它是表示整个数组的左值。只是在大多数表达式中会隐式转换为指向首元素的指针。指针是变量地址定义写法术语本质例子int *p[3]指针数组它是数组装的是指针char *menu[] {File, Edit}int (*p)[3]数组指针它是指针指向一个数组指向二维数组的一行void (*f)(int)函数指针它是指针指向一个函数回调函数、信号处理int *f(int)指针函数它是函数返回一个地址malloc 返回值数组的地址数组的几个地址—— a、a、a[0]当我们定义一个数组 a 时编译器根据指定的元素个数和元素的类型分配确定大小元素类型大小*元素个数的一块内存并把这块内存的名字命名为 a。名字 a 一旦与这块内存匹配就不能被改变。数组名a数组名a作为地址使用时表示数组首个元素的地址(指针)那它所指向的类型就是数组元素类型加减一个整数就相当于加减(整数*sizeof(元素类型))。数组名a可以看成一个指针常量它的值不能被修改不能单独作为左值使用。aa表示对编译器分配好内存的变量a取地址得到的是整个数组的地址(指针)其所指向的类型是数组的类型加减一个整数就相当于加减(整数*sizeof(数组的类型))。a[0]a[0]是数组的首个元素a[0]是对数组首个元素取地址得到的是数组首个元素的地址(指针)那它所指向的类型就是数组元素类型加减一个整数就相当于加减(整数*sizeof(元素类型))。表达式物理数值指向的对象指向对象的类型1 操作增加的字节数a数组首地址数组第一个元素 a[0]int1 * sizeof(int) 4a[0]数组首地址数组第一个元素 a[0]int1 * sizeof(int) 4a数组首地址整个数组int [5] (数组类型)1 * sizeof(a) 20野指针‼️野指针是指针指向的位置是不可知的随机的、不正确的、没有明确限制的。三大来源未初始化的指针指针指向已释放的内存指针超出变量作用域避免方法创建指针时对其设置一个初始值或NULL指针用完后释放内存并记得将其赋值为NULL避免指针访问越界避免返回函数中变量的指针指针的核心作用间接修改通过地址修改函数外部的变量文件指针指针传参动态内存堆上分配的内存必须用指针操作高效传递传指针避免结构体拷贝开销泛型编程void*实现通用数据结构如qsort字符串、数组、结构体指针数组 数组指针指针数组数组中的元素都是指针数组指针指向数组的指针指针数组int *p[5]是一个数组数组中的每个元素都是指向 int 的指针而数组指针int (*p)[5]是一个指针它指向一个包含 5 个 int 的数组。二者本质不同一个是“数组”一个是“指针”因此在内存布局和指针运算上也完全不同。函数指针 指针函数函数指针指向函数的指针#include stdio.h int Add(int x, int y) { return x y; } int main() { // 函数指针变量pf保存了Add函数的地址变量类型为int (*)(int, int) int (*pf)(int x, int y) Add; /* int (*pf)(int x, int y) Add; // AddAdd int (*pf)(int, int) Add; // 形参可以省略 int (*pf)(int, int) Add; // AddAdd形参可以省略 */ int sum (*pf)(3, 5); /* int sum pf(3, 5); // pf(3, 5) (*pf)(3, 5) int sum Add(3, 5); */ printf(%d\n, sum); // 8 return 0; }⚡ 回调函数回调函数通过函数指针调用的函数。你调用我提供的函数我在适当时机回调你的函数。也就是函数指针a作为参数在另一个函数b的参数列表中同时这个a函数还在另一个b中被调用#include stdio.h // 回调函数类型定义 typedef void (*Callback)(int); /* ​​typedef 的作用​​它是在定义一个新类型别名而不是简单的“A 是 B 的别名” 这里定义的类型是 ​​函数指针类型​​不是普通的数据类型 ​​(*Callback)(int) 的解析​​ *Callback这是一个指针变量名称是 Callback (int)它指向的函数接受一个 int 参数 void函数的返回类型是 void 整体合起来Callback 是一个 ​​指向函数的指针类型​​ */ // 接收回调函数作为参数的函数 void processNumbers(int arr[], int size, Callback callback) { for (int i 0; i size; i) { callback(arr[i]); // 调用回调函数 } } // 实际的回调函数实现 void printNumber(int num) { printf(Number: %d\n, num); } void squareNumber(int num) { printf(Square: %d\n, num * num); } int main() { int numbers[] {1, 2, 3, 4, 5}; int size sizeof(numbers) / sizeof(numbers[0]); printf(Printing numbers:\n); // 为什么不需要给函数传递参数?,其实 printNumber 这个地方只需要提供一个地址还没调用 // processNumbers(numbers, size, printNumber());是错误写法 processNumbers(numbers, size, printNumber); printf(\nPrinting squares:\n); processNumbers(numbers, size, squareNumber); return 0; }指针函数一个函数的返回值是一个指针的函数 --- int* a(void)指针常量和常量指const int *p; // 常量指针指向的值不可改指向可改 int *const p; // 指针常量指向不可改指向的值可改 const int *const p; // 都不可改悬空指针悬空指针是指向已释放或不存在内存的指针。产生悬空指针的情况释放后使用在内存被释放后继续使用指向该内存的指针。函数返回局部变量的地址从函数返回局部变量的地址后一旦函数退出局部变量的内存会被回收。避免悬空指针的方法及时重置指针释放内存后将指针设置为NULL。避免返回局部变量的地址使用动态分配的内存或全局/静态变量。野指针和悬空指针的区别野指针 (Wild Pointer)出生就带病。指针变量刚被创建时没有初始化它的值是随机的。悬空指针 (Dangling Pointer)后天致残。指针指向的内存被释放了但指针还指着那块地。void*指针void*被称为“万能指针”或“泛型指针”或“无类型指针”。它只表示“这是一个内存地址”但不说明该地址处存放的数据类型。 核心规则不能直接解引用因为它不知道要读取几个字节不知道类型。错误void *p; *p 10;正确*(int*)p 10;(必须先强转)不能直接进行算术运算在标准 C 中不能对void*进行或--因为不知道步长虽然 GCC 插件允许将其视为 1 字节但不建议这样做。 核心作用通用接口泛型malloc返回void*所以它可以分配任何类型内存。memset(void *s, int c, size_t n)可以清空任何类型的数组。回调函数中的上下文传递 在多线程如pthread_create或排序qsort中void*用于传递用户自定义的数据结构。多级指针指向指针的指针用于存储上一级指针的地址。如一级指针保存变量的地址。二级指针保存“一级指针变量”的地址。在函数内部修改外部指针的指向如果你传一级指针给函数函数内修改的是该指针的“副本”外部指针不会变。只有传二级指针才能真正改掉外部指针指向的内存地址。void allocateMemory(int **pp) { // 通过解引用二级指针修改外部的一级指针 *pp (int *)malloc(sizeof(int) * 5); } int main() { int *p NULL; allocateMemory(p); // 传入指针的地址二级指针 // 此时 p 不再是 NULL而是指向了堆内存 free(p); }二维数组/指针数组的动态处理char *argv[]命令行参数在传参时会退化为char **argv。argv指向第一个字符串指针的地址。*argv得到第一个字符串的首地址。**argv得到第一个字符串的第一个字符。对于int a[3][4];a是数组名指向第一行类型是int (*)[4]数组指针。a[0]是第一行的首元素地址类型是int *。((a i) j)等价于a[i][j]。#include stdlib.h int main() { int rows 3, cols 4; // 1. 申请行指针数组 (二级指针) int **matrix (int **)malloc(rows * sizeof(int *)); // 2. 为每一行申请空间 for (int i 0; i rows; i) { matrix[i] (int *)malloc(cols * sizeof(int)); } // --- 使用 matrix[i][j] --- // 3. 释放必须先释放每一行 for (int i 0; i rows; i) { free(matrix[i]); } // 4. 最后释放行指针数组本身 free(matrix); matrix NULL; // 避免悬空指针 return 0; }链表操作删除节点时需要修改头指针
延伸阅读

更多相关文章

2026/10/29 3:49:27

树莓派4B机载计算机开发与优化实战指南

1. 树莓派机载计算机硬件资源概述树莓派作为一款信用卡大小的微型计算机,凭借其强大的硬件性能和丰富的扩展接口,已成为机载计算机领域的明星产品。我使用树莓派4B作为飞行器的机载计算机已有两年多时间,它完美平衡了性能、功耗和体积这三个关…

2026/10/26 22:59:29

VC++与Word原生集成:COM自动化实现高性能报告生成

1. 项目概述:为什么选择VC与Word集成?在数据处理和办公自动化的领域里,我们常常面临一个经典场景:后台程序处理完一堆数据,最终需要生成一份格式规范、图文并茂的Word报告。你可能用过Python的python-docx,…

2026/10/28 2:02:40

STM32与MPU6050实现欧拉角姿态检测实战

1. 项目概述在嵌入式系统开发中,姿态检测是一个常见且重要的功能需求。STM32作为广泛使用的微控制器,配合MPU6050六轴传感器实现欧拉角输出,是许多运动控制、无人机、机器人等项目的基础。这个组合之所以流行,是因为它提供了成本效…

2026/10/29 11:10:05

第1周摸底测试

第1周摸底测试(选择题) 姓名:__________ 日期:__________ 限时:30分钟 满分:40分提示:第1—10题为公共基础知识(你之前可能没学过,凭感觉选即可);…

2026/10/29 11:10:05

鸿蒙 ExtensionAbility断点调试

ExtensionAbility 的生命周期函数不像页面那样有明确的入口,调试起来容易找不到北。本文基于官方文档,介绍两种调试 ExtensionAbility 生命周期函数的方式。 一、调试方式 可通过两种方式对 ExtensionAbility 生命周期函数进行调试: 方式说…

2026/10/29 11:10:05

免安装PDF盖章工具,透明度和位置可调

软件介绍 这次要说的是一款 PDF 专业签章工具,主要用来给 PDF 文件盖章用。之前其实已经推荐过一次,这次再拿出来聊,是因为它现在功能比之前完善了不少,可玩的地方更多了。 版本与整体情况 它是绿色单文件版,不用安…

2026/10/29 11:10:05

大模型学习21 大模型学习 - LangGraph1_概述

1 什么是LangGraph LangGraph 是一个低级编排框架和运行时环境,用于构建、管理和部署长期运行的有状态智能体(agents)。核心理念是将 Agent 工作流建模为图(Graph),其中: 节点(Nod…

2026/10/29 11:05:05

第3讲:Prompt 工程与管理——像管理代码一样管理提示词

一、为什么需要管理 Prompt初级做法: ┌──────────────────────────────────────────────┐ │ func askAI(question string) string { │ │ prompt : "请回答以下问题:" ques…

2026/10/28 14:42:12

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/28 15:28:56

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/28 3:32:29

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/29 0:03:42

学生选课系统DFD图绘制指南:边界划分与分层校验实战

简介:文档以顶层与第一层DFD图为核心,完整刻画学生选课系统的逻辑模型,面向软件工程课程设计、系统分析学习及毕设前期建模人群。顶层图界定系统边界,外部实体包括管理员、教师和学生三类角色;第一层图进一步细化用户登…

2026/10/29 0:03:42

JIRA敏捷落地:Scrum角色权限、Sprint与燃尽图避坑指南

简介:围绕“基于JIRA的敏捷开发项目管理”这一主题,面向产品负责人、Scrum Master及研发测试成员,梳理Scrum角色分工、迭代流程与JIRA工具配置的对应关系,适合初、中级团队导入敏捷管理时参考。压缩包共1个docx文件,大…

2026/10/29 0:03:42

SEMI S8人机工程验收实战:从条款拆解到现场实测的完整指南

简介:面向半导体设备设计、制造与运维工程师及安全合规人员,这份资料聚焦SEMI标准体系下的设备安全规范,重点收录SEMI S8-0308《半导体制造设备人体工程学工程安全指南》。内容围绕设备设计原则、错误与事故预防、人体工程学适配三大主线展开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑