15. 【C语言】C语言的灵魂:指针初体验

用班级记账本讲透区块链核心原理 区块链本质上是一种多副本同步、带时间戳的操作日志系统,其技术根基在于哈希校验、共识机制与分布式存储三大要素。哈希作为数据指纹,确保任何微小改动都会引发全局可检测的编号突变;共识机制则通过多方独立验证与交叉比对,将信任建立在可重复验证的行为过程之上,而非单一权威;分布式存储进一步提升容错性与抗篡改能力,使历史记录成为全网共享的确定性事实。这些原理广泛应用于金融清算、供应链溯源、电子存证等需强可信协作的场景。本文以小学班级积分系统为载体,将哈希值映射为页脚指纹编号,把共识过程具象为三本笔记逐字核对,用铅笔橡皮还 阅读详情

前面十四篇文章,我们盖好了地基:变量存数据,函数拆模块,数组管批量。但你有没有觉得还缺一块关键拼图——为什么我们可以在函数里修改数组,却改不了普通变量?为什么 scanf 要用 & 取地址?为什么说 C 语言“贴近硬件”?

答案就在指针。

指针是 C 语言最强大、也最让初学者头疼的特性。但不要怕——你已经有变量、内存、作用域的扎实基础,理解指针就只剩一层窗户纸。今天,我们就来捅破它。


一、为什么需要指针?

先看三个你或许已经遇到的困惑:

困惑一:为什么 scanf 要加 &?

int age;
scanf("%d", &age);   // 这个 & 是什么?

困惑二:为什么在函数里修改数组,外面也能看到变化?

void fill_zero(int arr[], int n) {
    for (int i = 0; i < n; i++) arr[i] = 0;  // 外面的数组也变了!
}

困惑三:为什么我们写的 swap 函数没用?

void swap(int a, int b) {
    int temp = a; a = b; b = temp;   // 外面纹丝不动
}

这三个困惑都指向同一个核心概念:如何直接访问和操作内存中的数据。普通变量通过名字访问,但有时候我们需要通过地址来访问——这就引出了指针。


二、指针是什么?

简单说:指针就是一个变量,但它里面存的不是普通数值,而是另一个变量的内存地址。

int a = 10;
int *p = &a;   // p 里存的是 a 的地址

这里:

  • a 是一个普通的 int 变量,里面存的是 10。
  • &a 是取地址运算符,得到 a 在内存里的地址(比如 0x7ffd1c)。
  • p 是一个指针变量,它里面存的就是那个地址。
  • *p 是间接访问运算符(解引用),沿着 p 存的地址找到 a,读写它的值。

关系图:

变量 a:  [10]   <-- 地址 0x100
              ^
              |
指针 p: [0x100]  <-- p 自己的地址是 0x200

所以 *p 就是 a 本人。你写 *p = 20;,a 就变成了 20。


三、声明指针变量

声明指针时,在类型后面加一个 *:

int *p;      // p 是指向 int 的指针
char *ch;    // ch 是指向 char 的指针
double *dp;  // dp 是指向 double 的指针

声明时可以初始化:

int a = 5;
int *p = &a;    // p 指向 a

也可以分开写:

int *p;
p = &a;         // 让 p 指向 a

注意:int *p, q; 中,只有 p 是指针,q 是普通 int。如果想声明两个指针,写 int *p, *q;。这是 C 语法的一个小陷阱。


四、& 取地址与 * 解引用

这两个运算符是指针的基本功,必须熟练掌握。

1. & 取地址

把任意变量的地址取出来,返回指向该类型的指针。

int x = 42;
printf("%p\n", (void*)&x);   // 打印 x 的地址

%p 专门用来打印地址(指针值),需要强制转换为 void*。

2. * 解引用(间接访问)

通过指针访问它所指向的变量。

int a = 10;
int *p = &a;

printf("%d\n", *p);   // 输出 10,等价于 printf("%d\n", a);

*p = 20;              // 把 a 改成 20
printf("%d\n", a);    // 输出 20

*p 可以出现在赋值号的左边(左值),用来修改指向的值。


五、指针作为函数参数:实现真正的“交换”

还记得第十二篇那个失败的 swap 吗?现在用指针来拯救它。

#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;   // 取 a 指向的值
    *a = *b;         // 把 b 指向的值赋给 a 指向的位置
    *b = temp;
}

int main(void) {
    int x = 5, y = 10;
    printf("交换前: x=%d, y=%d\n", x, y);
    swap(&x, &y);    // 传 x 和 y 的地址
    printf("交换后: x=%d, y=%d\n", x, y);
    return 0;
}

输出:

交换前: x=5, y=10
交换后: x=10, y=5

成功!因为 swap 接收的是 x 和 y 的地址,它通过 *a 和 *b 直接修改了 main 里那两个变量的内存。这就是指针的核心威力:让函数有能力修改外部的变量。


六、指针与数组:一对亲兄弟

1. 数组名就是首元素的地址

int arr[5] = {10, 20, 30, 40, 50};
printf("%p\n", (void*)arr);      // 数组名直接当指针用
printf("%p\n", (void*)&arr[0]);  // 和上面一样

数组名 arr 在表达式中会被自动转换成指向首元素的指针。所以:

int *p = arr;   // p 指向 arr[0]

现在你可以用指针来访问数组元素:

printf("%d\n", *p);        // arr[0] = 10
printf("%d\n", *(p+1));    // arr[1] = 20
printf("%d\n", *(p+2));    // arr[2] = 30

p + 1 不是地址值加 1 个字节,而是加 1 * sizeof(int) 个字节——也就是跳过整个元素,指向下一个 int。这称为指针算术。

2. 指针算术

指针加整数 n,地址值增加 n * sizeof(指向的类型)。

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr;    // 指向 arr[0]

p = p + 1;       // 指向 arr[1]
p++;             // 指向 arr[2]
p += 2;          // 指向 arr[4]

也可以用下标访问指针:

int *p = arr;
printf("%d\n", p[2]);   // 等价于 arr[2],输出 30

为什么数组和指针这么亲?因为 arr[i] 在底层被编译器翻译成 *(arr + i)。这两者完全等价。甚至你可以写 i[arr],会被翻译成 *(i + arr),也是一样的(但千万别真这么写)。

3. 用指针遍历数组

#include <stdio.h>

int main(void) {
    int arr[] = {2, 4, 6, 8, 10};
    int *p;
    for (p = arr; p < arr + 5; p++) {
        printf("%d ", *p);
    }
    printf("\n");
    return 0;
}

p < arr + 5 判断指针是否越过了数组末尾(arr + 5 指向最后一个元素之后的位置,不能解引用,但可以做比较)。


七、数组作为函数参数的本质

现在我们可以解释那个困惑了:为什么函数里修改数组,外面也会变?

void modify(int arr[], int n) {
    arr[0] = 999;
}

实际上,编译器看到 int arr[] 时,会把它当成 int *arr。函数调用时,传进来的是数组首地址的副本,而不是整个数组的副本。通过这个地址,函数可以直接修改原数组。

modify(my_array, 5);   // 传的是 &my_array[0]

所以 arr[0] = 999; 等价于 *(arr + 0) = 999;,直接写到了原数组的内存上。这就是为什么数组“按引用传递”的真相——它传的是地址值。


八、void* 指针初识

有一种特殊的指针类型:void*(无类型指针)。它可以指向任何类型的数据,但不能直接解引用,因为编译器不知道它指向的数据类型大小。

int a = 10;
void *vp = &a;          // 可以指向 int
// printf("%d\n", *vp); // 错误!不能解引用 void*
printf("%d\n", *(int*)vp);  // 先强制转换回 int*,再解引用

void* 常用于通用内存操作函数,比如 malloc、memcpy,后面讲动态内存时会遇到。


九、常见错误与陷阱

1. 使用未初始化的指针

int *p;
*p = 10;   // 危险!p 指向哪里?可能是随机地址,导致崩溃

指针必须指向合法的内存(已声明的变量、数组、动态分配的内存)才能解引用。

2. 返回局部变量的地址

int* bad_func(void) {
    int x = 100;
    return &x;  // 函数返回后 x 已销毁,返回的地址无效
}

这是经典的“悬空指针”错误。要返回指针,可以返回静态局部变量、全局变量或动态分配的内存的地址。

3. 解引用空指针

int *p = NULL;
printf("%d\n", *p);  // 段错误!NULL 是空地址,不允许访问

NULL 是一个宏,表示空指针。在解引用前,一定要确保指针非空。

4. 指针类型不匹配

int a = 10;
double *dp = &a;  // 编译器警告,类型不兼容
printf("%f\n", *dp);  // 未定义行为

不同类型的指针不要随便互指,除非你很明白自己在做什么(并且用强制转换)。

5. 野指针

指针指向的内存已经释放(free 之后),但指针还在,指向的地址无效。后面动态内存部分会细讲。


十、小结

今天你第一次触碰了 C 语言的灵魂——指针。它们不是什么神秘魔法,只是存地址的变量。但它们解锁了:

  • 直接修改外部变量的能力(swap 终于能用了)
  • 高效操作数组的方式(指针算术)
  • 理解了数组作为函数参数的本质

你现在知道了:

  • & 取地址,* 解引用。
  • 指针变量声明用 int *p;
  • 数组名就是首元素地址,arr[i] 就是 *(arr + i)
  • 函数传数组本质是传地址,所以能在函数内修改原数组。

指针的旅途才刚刚开始。下一篇我们会深入指针与数组更复杂的关系——指针数组、数组指针、多级指针,以及字符串与指针的紧密联系。当你能轻松玩弄指针时,你就真正拥有了 C 语言。


课后小练习

  1. 写一个函数 void increment(int *p),让传入的整数加 1。在 main 中测试。
  2. 使用指针遍历一个 double 数组,打印所有元素,观察指针加 1 时地址增加了多少字节。
  3. 写一个函数 int array_sum(int *arr, int n),用指针算术(而不是下标)计算数组元素的和。
  4. 分析以下代码错在哪里:
    int* get_pointer(void) {
        int val = 5;
        return &val;
    }
    int main(void) {
        int *p = get_pointer();
        printf("%d\n", *p);
        return 0;
    }
    
    为什么输出可能不是 5?用静态局部变量怎么修正?

我们下期见!

💡获取本系列示例代码请访问 GitCode 仓库。

从零点亮SPI屏:在LuckFox Pico上玩转Framebuffer与LVGL图形界面 本文详细介绍了如何在LuckFox Pico上通过Framebuffer和LVGL实现SPI液晶屏的图形界面开发。从基础显示测试到LVGL移植,再到性能优化和完整UI系统构建,逐步指导开发者打造流畅的交互式界面。特别针对SPI屏的带宽限制,提供了双缓冲、帧率控制等关键优化策略。 阅读详情

相关推荐

MC68HC912B32评估板开发指南:从硬件连接到汇编编程实战

在嵌入式系统开发领域,评估板是工程师评估微控制器(MCU)性能、验证硬件设计的关键工具。其工作原理在于提供一个接近芯片厂商参考设计的纯净硬件平台,允许开发者直接访问MCU的所有引脚和资源,从而进行精准的底层硬件交互和软件调试。这种直接控制硬件的技术价值在于,它能帮助开发者透彻理解芯片的时序、电气特性和外设行为,为产品原型设计提供可靠的验证基础,有效规避后期风险。典型的应用场景包括汽车电子、工业控制等对可靠性和成本敏感的领域。本文以经典的M68EVB912B32评估板为例,详细解析其硬件构成、固件(D-Bug

weixin_30530939的博客 402

我与C++的十年:从“Hello World”到代码信仰的觉醒

十年前,我在大学宿舍里为一段“Hello World”代码报的37个错误抓狂;十年后,我作为技术负责人,用C++优化的交易系统每秒处理百万级订单。这十年,C++从“难学”的标签,变成我安身立命的技能,更成为我理解世界、表达自我的方式。今天,我想用这段充满热血的成长故事告诉你:学C++,不是为了“找工作”,而是为了开启一段与极致性能共舞的传奇人生——因为在这里,每一个bug都是成长的勋章,每一行代码都是信仰的注脚。

Start_mswin的博客 658

从零开始:Windows下QT安装与首个C++ GUI程序实战指南

C++ GUI开发是桌面应用开发的核心领域,其关键在于选择合适的框架以简化跨平台应用的构建。QT框架以其“一次编写,到处编译”的跨平台特性,成为C++ GUI开发的主流选择。它通过封装底层系统调用,提供了统一的API,显著降低了多平台适配的复杂度。在工程实践中,QT不仅提供了强大的界面库,还集成了网络、数据库等模块,形成了一个完整的开发生态。对于初学者,从环境搭建到第一个可交互窗口的创建,是掌握GUI编程的关键路径。本文以Windows平台为例,详细解析了QT安装、MinGW编译器配置、Qt Creator

weixin_30372371的博客 290

练功秘籍---A

常用整理

CarryBest的博客 1647

LVGL v8.3 图片显示与解码缓存实战:PNG 上屏、C 数组与文件双方案

LVGL v8.3 图片两种存储方式、PNG/JPG 上屏解码器挂载、LV_IMG_CACHE_DEF_SIZE 缓存调优、手动预解码头像有效性判断,及"缓存被动画清空"真实踩坑。

汪同学的博客 376

利用MATLAB 4f光学系统,在Python中进行弗劳恩霍夫衍射和傅里叶面滤波——用镜头模拟光学图像处理。.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

地球Online逃生指南(上卷)

本书是一部系统探讨人类意识困境的哲学著作。全书通过七卷本的结构,依次解构人类认知牢笼、自我本质、世界实相、痛苦根源,最终指向自由、慈悲与终极实相。第一卷揭示大脑建构的认知幻象;第二卷剖析"自我"的神经机制;第三卷以系统论阐释世界空性;第四卷建立痛苦的数学模型;第五卷提出觉知训练体系;第六卷论证利他主义的科学基础;第七卷超越逻辑束缚直指存在本质。著作融合现代神经科学、量子物理、复杂性理论与东方哲学,构建了一个从认识到解脱的完整认知框架,最终指向无条件的临在与宇宙层面的觉知。

weixin_45747731的博客 829

利用MATLAB和Excel对逆流双管热交换器的热性能进行分析。.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

static 的三种用法:C 语言里一个关键字的三张面孔

本文系统讲解 C 语言中 static 关键字的三种用法:修饰局部变量延长生命周期、修饰全局变量与函数限制作用域到本文件。通过一个包含三个文件的完整工程示例对照演示,并从存储期、作用域与链接性三个属性解释其统一原理,最后补充单元测试框架偏爱 static 函数的工程实践。

大模型全栈工程师 · Thanks_ks 298

利用MATLAB对5G NR波束成形技术的性能分析.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

上市公司赫芬达尔指数、企业勒纳指数(1999-2025年)

上市公司赫芬达尔指数(HHI)和勒纳指数(LI)是衡量市场结构和市场势力(垄断力量)的两个重要指标 赫芬达尔指数(HHI)可衡量一个公司在市场中的相对份额或集中度,通过计算公司在市场中份额的平方和得出。数值越小,说明市场份额分布越分散,竞争程度越高;数值越大,说明市场集中度越高,少数企业占据较大份额,垄断程度越高 勒纳指数(LI)用于衡量企业的市场势力,即企业偏离完全竞争价格、制定高于边际成本的价格的能力,数值越接近1,表示企业市场势力越强,垄断程度越高 本文借鉴刘贯春等(2021)、孙茹峰和李楠博(2024)的思路和方法,分别测算上市公司赫芬达尔指数、勒纳指数: 赫芬达尔指数 = (公司营业收入/同行业同年营业收入合计)的平方和 企业勒纳指数 =(营业收入-营业成本-销售费用-管理费用)/营业收入 一、数据介绍 数据名称:上市公司赫芬达尔指数、企业勒纳指数 数据范围:上市公司 时间范围:1999-2025年 样本数量:85810条 数据来源:上市公司年报 更新时间:2026年11月 数据说明:内含原始数据、测算结果 二、数据指标 股票代码 股票简称 年份 省份 城市 区县 省份代码 城市代码 区县代码 行业代码 行业名称 赫芬达尔指数 勒纳指数 营业收入 营业成本 销售费用 管理费用 三、参考文献 [1]刘贯春,叶永卫,张军.社会保险缴费、企业流动性约束与稳就业——基于《社会保险法》实施的准自然实验[J].中国工业经济,2021,(05):152-169. [2]孙茹峰,李楠博.金融化对企业绿色转型意向的影响研究[J].管理学报,2024,21(11):1612-1620+1677. [3]张倩肖,段义学.数字赋能、产业链整合与全要素生产率[J].经济管理,2023,45(04):5-21. [4]伍兆祥,代吉林,

【FDE知识库】(1):AI 编程时代开发语言选型分析,云使用go,企业用java,算法python,底层c

AI 编程时代,开发语言选型需综合考量AI适配度、编译速度、跨平台与学习成本。Python以海量语料和即时执行成首选,适用于AI/ML与原型开发;TypeScript凭借类型系统与秒级构建,成为前端及应用层主流;Go以极速编译与统一风格,契合AI代理高效迭代需求。Rust/C++虽性能强,但编译慢、学习成本高,不适于快速试错。综合推荐:优先选Python、TypeScript、Go,兼顾效率与生态,实现最优AI协作体验。

freewebsys的专栏 485

工业互联网工控安全与工业质检AI大模型建设方案.pptx

工业互联网工控安全与工业质检AI大模型建设方案.pptx

不平衡电网下自抗扰顺序模型预测PWM整流器控制仿真模型(Simulink仿真实现)

内容概要:本文围绕面向城市空中交通的多无人机路径规划问题展开研究,提出了一套融合全局规划、动态避障与局部协同调整的综合路径规划方法。研究内容涵盖基于改进RRT算法与图论的全局路径规划、基于动态风险场和动态窗口法(DWA)的模型预测控制实现动态避障,以及基于分布式协同控制的多无人机局部路径协同策略。通过Matlab平台构建仿真环境,在多种典型城市交通场景下对所提方法进行了系统性验证,结果表明该方法在路径安全性、编队稳定性与任务效率方面表现优异。文章还深入探讨了算法的创新点与未来研究方向,为复杂城市环境下多无人机系统的自主协同提供了理论支撑与技术路径。; 适合人群:具备自动化、控制理论、计算机科学或相关领域基础知识,从事无人机系统、智能交通、路径规划、多智能体协同控制等方向研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①为城市空中交通系统中的多无人机安全、高效飞行提供可靠的路径规划解决方案;②支持在复杂、动态的城市场景中开展无人机自主导航、编队飞行与协同控制的技术研究与仿真验证。; 阅读建议:建议结合提供的Matlab代码进行实践操作,重点关注算法的具体实现细节、参数配置对性能的影响以及不同仿真场景下的实验设计,以深入理解各模块的集成逻辑与实际应用效果。

通信行业CentOS 7内核网络栈优化:高并发连接下UDP/TCP性能调优与系统资源管理

内容概要:本文聚焦于CentOS 7系统在通信行业高并发场景下的性能调优实践,重点围绕内核网络栈优化展开,涵盖UDP/TCP缓冲区调整、文件句柄与连接跟踪表扩容、TIME_WAIT状态复用、软中断分布优化及tuned工具的自动化调优。通过具体配置参数和实际案例(如VoIP软交换系统)深入解析各项调优措施的设计原理与实施效果,有效解决高CPS下丢包、连接失败等问题,并展望了向云化、eBPF等动态智能调优方向的发展趋势。; 适合人群:从事通信系统运维、网络开发或Linux系统性能优化的技术人员,具备一定操作系统和网络基础知识,工作经验1年以上的中初级工程师。; 使用场景及目标:①应用于软交换、媒体网关、信令网关等高并发通信设备的系统调优;②解决UDP丢包、conntrack表满、TCP连接队列溢出等典型性能瓶颈;③提升系统在小包高频、低时延要求场景下的稳定性和吞吐能力。; 阅读建议:建议结合实际生产环境或测试平台同步验证文中配置,重点关注参数背后的物理意义(如BDP、softirq、conntrack机制),并配合ss、conntrack、dmesg等命令进行调优前后对比分析,确保安全可控地实施变更。

2024 《计算机网络》实验教学指导书.doc

2024 《计算机网络》实验教学指导书.doc

【选址和定容】模拟退火改进多目标粒子群算法在分布式电源选址和定容中的应用【IEEE69节点】(Matlab代码实现)

内容概要:本文研究了模拟退火改进多目标粒子群算法在分布式电源选址和定容中的应用,以IEEE69节点配电系统为算例进行仿真分析。通过融合模拟退火算法的强全局搜索能力与粒子群算法的高效局部收敛特性,构建了一种改进的多目标优化算法,用于解决分布式电源在配电网中的最优位置选择与容量配置问题。研究旨在实现多重优化目标,包括最小化网络有功损耗、提升节点电压质量、增强系统运行的经济性与稳定性。文中详细阐述了多目标优化模型的数学构建、算法的改进机制与实现流程,并基于Matlab平台完成了算法编程与仿真验证,结果表明所提方法在求解质量和收敛性能上均优于传统算法。; 适合人群:具备电力系统分析、优化理论基础及Matlab编程能力,从事配电网规划、分布式能源系统优化、智能优化算法应用等相关领域的科研人员、工程师以及高校研究生。; 使用场景及目标:①解决高比例分布式电源接入背景下配电网的科学规划问题;②为新型电力系统中源网协调设计提供有效的优化工具;③为相关领域的科研工作提供可复现、可拓展的算法模型与仿真代码实例,支持进一步的算法对比与功能升级。; 阅读建议:建议读者结合提供的Matlab代码与IEEE69节点标准数据,动手复现实验结果,深入理解算法的参数设置、迭代过程与多目标折衷分析方法,并可根据实际研究需求修改目标函数或引入新的约束条件以适应不同的应用场景。

034、特定场景优化(一):小目标检测的改进策略合集

本文针对工业场景中的小目标检测难题,提出了一套实战优化方案。首先从数据层面建议保持高分辨率输入(1024-1280像素)或采用切块训练策略,并适当放大标注区域。在网络结构上,提出减少下采样次数、增加高分辨率检测头、引入空间注意力机制等改进。损失函数方面推荐使用NWD匹配和QFL损失提升小目标敏感度。部署环节强调NMS优化、分辨率对齐和量化处理技巧。最后总结核心经验:优先优化数据质量而非模型结构,标注一致性比算法更重要,同时需为实际部署预留性能余量。硬件升级有时比算法调优更经济有效。

xiaoyaohou11的博客 650
上一篇: 14. 【C语言】程序的分割术:多文件编译与头文件
下一篇: 16. 【C语言】指针与数组的亲密关系
gugucoding
博客等级 码龄140天 7粉丝 102原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值