C语言基础回顾(2026/7/18)
目录指针1. 地址、、*2.容易混淆的点3. 指针类型4.指针和数组5. 字符串指针6. 指针传参7.指针指向8.const 与指针①锁内容②锁指针③双锁④总结记忆点9.volatile与const①硬件寄存器映射②中断服务程序ISR与主循环通信10.野指针/空指针/悬垂指针①野指针②空指针③悬垂指针④总结11void * 是什么①可以接收任何类型的地址不用强转②不能直接解引用③为什么要给 %p 强制转 (void *)指针1. 地址、、*每个变量都住在内存里有一个门牌号——地址。int a 10; int *p a; // 取地址把 a 的门牌号存进 p int val *p; // * 解引用按门牌号找到房间取出里面的值 → 10x→ 问x 住哪→ 拿到地址*p→ 按地址 p 去敲门 → 读写那个位置的值int *p里*是类型的一部分意思是 p 是指针变量不是解引用记一句话声明时*表示这是一个指针使用时*表示去那看看。2.容易混淆的点int *p 和 int* p 对编译器来说完全没区别生成的代码一模一样但它们的“写法含义”和“易踩的坑”不一样。int* p 的写法; // 把 *p 看成一个整体*p 的类型是 intint *p; 的写法 传统的、最符合语言设计的写法int* p; // 看上去像p 的类型是 int* 这种是最直观也是最容易引起混淆的写法比如int* p, q; 很多人会以为p和q都是指针但其实p 是 int* q 是普通的 int 因为*只绑在p上等价于int *p, q;因此我们声明指针的时候写成int *p不要写成int* p3. 指针类型指针不光存地址还知道它指向什么类型。这对嵌入式很重要内存移位频繁用到。char *pc; // 指向 charpc1 跳 1 字节int *pi; // 指向 intpi1 跳 4 字节short *ps; // 指向 shortps1 跳 2 字节指针 ±1 跳的步长 sizeof(指向的类型)。#include stdio.h int main() { int arr[] {10, 20, 30}; int *p arr; printf(%d\n, *p); // *p指向数组的第一个元素 printf(%d\n, *(p 1)); // *(p 1)指向数组的第二个元素 printf(%d\n, *(p 2)); // *(p 2)指向数组的第三个元素 return 0; }4.指针和数组int arr[5] {1, 2, 3, 4, 5}; int *p arr; // 等价于 int *p arr[0];数值名首元素地址arr[i]本质就是*(arr i)因为数组首元素下标从0开始此处假设i3a[3]就是第四个元素 arr为首元素地址arr3指向第四个元素地址随后解引用*arri即得到第四个元素。指针可以而数组名不可以int *p;p 是一个指针变量——它里面存的是一个地址而且这个地址是可以被改变的。p;等价于p p 1;因为p是int*这里的1不是加 1 个字节而是加sizeof(int)个字节通常 4 字节。也就是让p指向下一个 int 元素。例子int a 10, b 20; int *p a; p; // 现在 p 不再指向 a // 如果 p 原来指向 ap 后 p 存的地址是 a 4p 后变成野指针了所以实际中我们常把 p 指向数组int arr[3] {1, 2, 3}; int *p arr; // p 指向 arr[0] p; // p 指向 arr[1] printf(%d\n, *p); // 输出 2因为p是变量所以可以自增、自减、重新赋值。为什么arr不行定义int arr[3] {1, 2, 3};arr 是数组名。在绝大多数表达式里它会自动转换成第一个元素的地址但这个“转换来的指针”有个关键性质它是右值常量也就是int *arrarr[0]它是右值常量不能放在赋值号左边也不能自增/自减。arr; // ❌ 编译错误左值 required需要可修改的左值就好比你不能写100; // ❌因为100是常量arr在表达式中也是个地址常量——数组在内存中的起始位置是固死的不能让它“挪动”。5. 字符串指针char *s1 hello; // 指向只读常量区 char s2[] hello; // 在栈上复制了一份可修改// s1[0] H; ❌ 段错误/未定义行为 s2[0] H; // ✅ 合法Hello6. 指针传参C 语言全是值传递。想在一个函数里改外部变量必须传地址例1int x5 y10 // 错误示范 void bad_swap(int a, int b) { int t a; a b; b t; // 改的是副本外面没变 }一句话总结实参x, y把自己的值复制了一份交给形参a, b。a, b有自己独立的地址怎么折腾都跟x, y没关系——C 语言里函数调用就是复印机永远复印值不传原件。唯一的例外想改原件传原件的地址让被调函数顺着地址摸回原件去改。例2void good_swap(int *a, int *b) { int t *a; *a *b; *b t; // 通过地址改原值 } int x 5, y 10; good_swap(x, y); // x10, y5调用 good_swap(x, y) — 参数传递瞬间把x 和y 的地址传入 a 和ba 和b 记住了x和y 对应的地址。然后*不是在声明而是在解引用因此*a是指向x 的地址取出x对应的值因此int t 5随后*a *b 解引用找到y 对应的地址下对应的值10把 它复制给x *bt 把y改成5最后得到x 10 y57.指针指向int *p; 这句话的意思是p 是一个指针变量。它的类型是 int *它指向 int。定义int *p; 中“指向 int”的意思是p 这个指针变量里存的是一个地址而这个地址对应的那块内存里放的是一个 int 类型的数据。int a 10; int *p a; // p 指向 aa 的类型是 int整型值是 10住在 0x1000。p 的类型是 int *整型指针它里面填的是 0x1000。所以我们说p指向a且因为a是int所以p是“指向 int 的指针”我们可以通过p去访问该intprintf(%d\n, *p); // 输出 10*p 就是 p 指向的那个 int也就是变量a。8.const与指针第一层容易混淆的写法int *p; // * 贴着变量名 int* p; // * 贴着类型 int * p; // * 两边都空格 int*p; // 挤成一团这四个对编译器来说是同一个东西。空格在 C 里不改变语义。所以你看到的int *p和int* p只是不同程序员的审美偏好可以当它们不存在。但这造成了一个视觉陷阱int* p, q; // 看起来像声明了两个指针 // 错p 是指针q 是普通 int // 因为 * 只跟 p 走不跟 int 走这就是为什么很多人坚持写int *p——时刻提醒自己*是绑在变量名上的不是绑在int上的。第二层const怎么看——只看*和const的相对位置原则const 修饰它左边的东西如果左边没有就修饰右边这是 C 的语法规则。所有混乱都来自一个事编译器不管空格但你眼睛要管的只有*和const谁在左谁在右分清这个就够了。把噪声删掉只留骨架①锁内容这里的锁意思是——你不能用p这个通道去改数据有两种写法int const *p / const int *p这是历史的锅。C 最初用const int *pconst打头后来又允许int const *pconst放类型后面也能修饰类型。两条路到达同一个终点int a 10, b 20; const int *p a; // *p 100; // ❌ 报错不能通过这个指针改数据 p b; // ✅ 允许指针可以改指向关键点被指的那个变量本身不一定真的是const。比如上面a就是普通int你直接写a 100;完全合法只是不能通过p这条通道改它所以内容锁更准确的说法是这条路是单行道只能读不能写。②锁指针锁的是p指针本身写法是const在*右边int *const p a; // ⚠️ 必须初始化因为之后不能改了这里的锁意思是——p这个变量里存的地址不能再变写法int * const pint a 10, b 20; int *const p a; *p 100; // ✅ 允许可以通过 p 改数据 // p b; // ❌ 报错指针不能改指向因为p一旦定下来就不能再换所以声明时必须初始化③双锁内容和指针都锁两种写法const int * const p / int const * const pconst int *const p a;你不能通过p改数据❌*p 100;也不能通过p 指向别处❌ p b;两个方向都被堵死限制最严④总结记忆点不要想const修饰int *而是想const修饰的是数据还是指针嵌入式里const指针大量用在只读寄存器映射—const volatile uint32_t *硬件状态寄存器的经典声明接口契约— 函数参数加const告诉调用者我不会改你的数据查表/常量数组—const uint16_t sin_table[]放 Flash 不占 RAM9.volatile与constvolatile 是 C/C 里的一个类型限定符和 const 平级用来告诉编译器“这个变量随时可能被意想不到的因素改变你每次用到它都必须老老实实从内存里重新读不准偷懒用缓存”一句话记volatile “易变的别优化我”。禁止优化缓存每次直读直写为什么需要volatile编译器的小聪明int flag 1; while (flag) { // 什么都不干空转 }如果没有volatile编译器发现循环里根本没人改flag就会把它当成恒定值1直接把循环优化成死循环——甚至干脆删掉读取flag的代码因为你“明明没改它”。但如果flag是被中断服务程序、硬件、另一个线程悄悄改掉的那就出大事了程序永远停不下来。volatile int flag 1; while (flag) { // 每次都会从内存读 flag 的真实值 }编译器就不敢优化了每次循环都去内存查flag有没有变成0。三大应用场景①硬件寄存器映射硬件状态寄存器会被外设自动改写// 假设 0x40021000 是一个硬件状态寄存器 #define STATUS_REG (*(volatile unsigned int *)0x40021000) while (STATUS_REG 0) { ; // 等待硬件把寄存器置 1 }如果不加volatile编译器可能只读取一次就完事导致永远等不到硬件变化。②中断服务程序ISR与主循环通信volatile int timer_tick 0; // 中断里 void TIM_IRQHandler(void) { timer_tick 1; } // 主循环 while (1) { if (timer_tick) { timer_tick 0; do_task(); } }timer_tick 在中断里改主循环读必须 volatile。10.野指针/空指针/悬垂指针这三种指针是 C 语言里最常见的指针炸了场景本质区别在于指针指向的地址是什么状态①野指针int *p声明了但是没有给p赋值。p里面可能有栈上残留的随机值比如0x7f456461456*p10往一个随机的内存地址写了 10可能造成段错误 / 改坏别人的数据 / 程序莫名奔溃修复声明时立刻初始化。②空指针在 C 语言中我们通常用NULL来表示空指针int *p NULL; // C 语言风格空指针不是未初始化的指针野指针。空指针是故意赋成的一个安全状态用来标记“这个指针现在什么也没指”。#include stdio.h int main() { int *p NULL; printf(%x\n, (void *)p); }如果随便用一个乱七八糟的数字比如-1或0xCCCCCCCC来表示“空”不仅不直观还容易和合法的内存地址混淆。因此编程界达成了一个共识干脆统一用数字0来代表“空”的状态。所以当你打印p的值时它显示出来的就是0或者十六进制的0x00000000。空指针的意义不是让你去用它而是让你能检查它空指针是 C 里的哨兵值——崩溃是可预测的比野指针的随机破坏好排查一万倍。让你去访问p对应地址下的值直接系统出手了。③悬垂指针指向已释放的内存这是最阴险的一种指针里存的地址曾经是合法的但现在那块内存已经被回收了。修复永远不要返回局部变量的地址。如果需要用malloc分配堆内存或者让调用者把缓冲区传进来。④总结11void *是什么一句话void *是 C 里的万能指针——可以存放任何类型的地址但不能直接解引用。可以把它理解成一个只有门牌号、没有户型信息的指针int *p_int → 不仅知道地址是 0x1000还知道里面住的是 int4 字节 double *p_dbl → 不仅知道地址是 0x2000还知道里面住的是 double8 字节 void *p_void → 只知道地址是 0x1000不知道里面住的是什么类型①可以接收任何类型的地址不用强转int x 42; double y 3.14; char z A; void *p; p x; // ✅ 隐式转换int* → void* p y; // ✅ 隐式转换double* → void* p z; // ✅ 隐式转换char* → void*C 语言里任何指针类型都可以隐式转成void*不需要写强制转换。这就是它万能的含义。②不能直接解引用void *p x; // *p 100; // ❌ 报错编译器不知道要读几个字节 // 是 1 字节char4 字节int8 字节double // 必须先强制转回具体类型才能用 *(int *)p 100; // ✅ 告诉编译器按 int 的规则读就像一个快递员拿着门牌号但是不知道里面寄的是什么——void*丢了类型信息要你自己先认定它是什么类型再操作③为什么要给%p强制转(void *)%p的格式说明符要求参数必须是void *类型。因为你传的可能是int*、char*、double*……但printf是变参函数编译器不做隐式转换所以必须手动统一成 void*int *p x; printf(%p\n, (void *)p); // 显式转成 void*满足 %p 的要求它就是把你指针里存的那个地址值用十六进制打印出来不转的话行为是未定义的虽然大部分平台也能跑但规范上不对。%p里的p就是pointer指针的首字母因为%x不保证位数、不保证前缀而%p保证按平台的地址格式来。

相关新闻

arcgispro错误000210无法创建输出

arcgispro错误000210无法创建输出

在运行模型构建器当中的成对相交时&#xff0c;报错error000210无法创建<文件夹>&#xff0c;可以尝试在保存的数据库路径下创建要素数据集&#xff0c;将输出的矢量保存到要素数据集当中。

2026/7/26 16:36:49 阅读更多 →
晶振类型详解:无源与有源晶振的工作原理与选型指南

晶振类型详解:无源与有源晶振的工作原理与选型指南

1. 晶振基础概念与分类在电子工程领域&#xff0c;晶振&#xff08;Crystal Oscillator&#xff09;就像电子设备的心脏起搏器&#xff0c;为数字电路提供稳定的时钟信号。我第一次接触晶振是在大学电子设计竞赛时&#xff0c;当时因为选错了晶振类型导致整个FPGA开发板无法正常…

2026/7/27 12:58:29 阅读更多 →
三亚有没有本地装修公司推荐?

三亚有没有本地装修公司推荐?

在三亚&#xff0c;想要找本地靠谱的装修公司可不是一件容易的事。现在装修市场鱼龙混杂&#xff0c;一不小心就可能踩坑。下面就来深入分析下选装修公司的要点以及三亚本地的情况。装修市场痛点2026年很多装修公司存在爆雷风险&#xff0c;一些小公司没有完善的施工管理体系&a…

2026/7/27 19:33:37 阅读更多 →

最新新闻

基于感知哈希的图片查重系统设计与优化

基于感知哈希的图片查重系统设计与优化

1. 项目概述&#xff1a;基于感知哈希的图片查重系统这个项目实现了一个能够快速识别重复或相似图片的系统&#xff0c;核心采用感知哈希&#xff08;pHash&#xff09;算法计算图片指纹&#xff0c;配合汉明距离进行相似度比对。我在实际开发中发现&#xff0c;这套方案特别适…

2026/7/28 23:07:43 阅读更多 →
Athena高级教程:自定义论文结构与公式生成的实用指南

Athena高级教程:自定义论文结构与公式生成的实用指南

Athena高级教程&#xff1a;自定义论文结构与公式生成的实用指南 【免费下载链接】Athena Structure your STEM essay in several minutes with Generative AI. 项目地址: https://gitcode.com/gh_mirrors/athena13/Athena Athena是一款基于生成式AI的STEM论文辅助工具&…

2026/7/28 23:07:43 阅读更多 →
科研绘图工具PaperXie:自动化解决学术图表痛点

科研绘图工具PaperXie:自动化解决学术图表痛点

1. 科研绘图的现状与痛点作为一名在学术圈摸爬滚打多年的科研狗&#xff0c;我深知论文图表的重要性。记得刚读研时&#xff0c;我的第一篇SCI论文被审稿人直接打回&#xff0c;其中一个重要原因就是"Figures are not publication quality"。当时用Excel随便画的柱状…

2026/7/28 23:07:43 阅读更多 →
libcom高级教程:如何利用反射生成模型为合成图像添加真实水面倒影

libcom高级教程:如何利用反射生成模型为合成图像添加真实水面倒影

libcom高级教程&#xff1a;如何利用反射生成模型为合成图像添加真实水面倒影 【免费下载链接】libcom Image composition toolbox: everything you want to know about image composition/compositing or object/subject insertion/addition/compositing. 项目地址: https:/…

2026/7/28 23:07:43 阅读更多 →
SoulSync元数据魔法:自动获取、修复与优化音视频信息的实用技巧

SoulSync元数据魔法:自动获取、修复与优化音视频信息的实用技巧

SoulSync元数据魔法&#xff1a;自动获取、修复与优化音视频信息的实用技巧 【免费下载链接】SoulSync Intelligent Music & Video Automation Platform 项目地址: https://gitcode.com/gh_mirrors/so/SoulSync SoulSync作为一款智能音视频自动化平台&#xff0c;能…

2026/7/28 23:07:43 阅读更多 →
工业HMI色彩设计规范与安全应用实践

工业HMI色彩设计规范与安全应用实践

1. 工业HMI色彩规范概述在工业控制领域&#xff0c;人机界面&#xff08;HMI&#xff09;的色彩设计绝非简单的美学问题。我曾参与过多个大型工业项目的HMI设计&#xff0c;亲眼见过因配色不当导致的操作失误案例——某化工厂因报警颜色与背景色对比度不足&#xff0c;导致操作…

2026/7/28 23:06:43 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿&#xff01;3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑&#xff1a;把几百页的财报、法规、技术手册扔给向量库&#xff0c;问一个具体问题&#xff0c;搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了&#xff0c;要么藏在几十条结果的最下面。语义相似≠真正相关&#xff0c;这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营&#xff0c;从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候&#xff0c;每天刷半小时抖音&#xff0c;手动把爆款视频的口播敲进备忘录&#xff0c;一条2分钟的视频得花十来分钟&#xff0c;碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档&#xff0c;可以直接使用&#xff01;系统支持图片、视频、摄像头等多种方式检测裂缝&#xff0c;功能强大实用。 1数据集6000张 8各类别

2026/7/28 12:04:22 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像&#xff01; pubg绝地求生目标检测数据集 1分类&#xff1a;e_body&#xff0c;14905个标签&#xff0c;txt格式 共计14244张图&#xff0c;99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别&#xff1a; allies enemy tag图片总量&#xff1a;7247张训练集&#xff1a;5139张验证集&#xff1a;1425张测试集&#xff1a;683张标注状态&#xff1a;全部已标注&#xff0c;即拿即用数据格式&#xff1a;支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻