首页/新闻资讯/正文详情

C语言底层考点:sizeof、数组退化与内存布局

发布时间:2026/11/3 20:18:49 来源:云帆数科 栏目:资讯中心
C语言底层考点:sizeof、数组退化与内存布局
简介这是一份面向C语言笔试与面试备考者的经典试题合集聚焦指针、内存管理与关键字等高频考点适合正在准备校招、实习笔试或希望夯实C语言底层基础的中初级开发者查漏补缺。资源包共1个文件为doc文档整体约122KB篇幅紧凑但覆盖面广可直接打印或对照屏幕逐题练习。目前已有320人学习下载。文档以问答形式串起sizeof与strlen的区别、指针与数组的长度计算、栈区与堆区的内存分配差异、大小端存储顺序、结构体指针偏移运算、数组地址偏移算法以及const、static、volatile三个关键字的语义与典型用法并延伸到线程间通信的信号量、管道、消息队列、共享内存等机制。每道题多配有答案与解释部分题目还给出32位系统下的具体计算过程和示例代码便于读者在比对答案的同时理解编译器行为与内存布局形成可复用的解题思路减少笔试中因概念模糊而失分的情况。1. 数组名什么时候悄悄丢掉了长度一道 C 语言笔试里最容易判错的题往往只有一行sizeof(str)。同一个char str[] Hello在定义它的函数里量出 6传进另一个函数却量出 4——字符串一个字节都没少变的是编译器看它的方式。这份 C 语言笔试试题汇集把这条分界线放在了最前面因为它不是在考记忆而是在考一个更基础的问题编译器在编译期到底知道多少信息运行时又必须重新问一次。想通这一点后面的大小端、结构体偏移、指针加法、const与volatile才有共同的来源和落点它们全都在追问同一件事——谁、在什么时候、知道这个变量的什么。2. sizeof 与 strlen数组名在什么时候退化成指针2.1 sizeof 为什么能安全“测量”一个空指针解引用sizeof是操作符不是函数。这一点决定了它的两个反常行为它可以接类型名做参数而且它的操作数通常不参与求值。编译器只从操作数的类型里读出宽度根本不去执行那个表达式。这条规则解释了很多看起来像玄学的现象——sizeof(*p)里p是空指针也不会崩sizeof(f())里f不会被调用。#include stdio.h short f(void) { return 1; } int main(void) { int n 10; int *q n; /* sizeof 只看类型不看值*q 不会被真正求值 */ printf(%zu\n, sizeof(*q)); /* 4解引用后类型是 int */ printf(%zu\n, sizeof(q)); /* 4指针变量本身32 位实现 */ printf(%zu\n, sizeof(f())); /* 2f 没被调用只取返回类型 short */ printf(%zu\n, sizeof n); /* 4后面跟变量名时括号可以省 */ return 0; }%zu对应size_tsizeof的结果类型就是它在多数 32 位实现里被 typedef 成unsigned int。第三行最能说明问题f()是一个函数调用表达式但sizeof只需要它的返回类型所以运行时一次调用都不会发生。反过来这也是sizeof不能用来测量变长数组之外动态内存的原因——动态分配的内存在编译期根本不存在编译器手里没有可读的类型宽度。正因为sizeof完全由类型决定它才能出现在需要常量表达式的位置比如定义数组维度char str[20]、int b sizeof(str)。这类用法在嵌入式代码里很常见本质上是把“类型宽度”当成编译期常量来用。2.2 数组退化的现场三种形参写法为什么完全等价fun(char buf[8])、fun(char buf[])、fun(char *buf)在编译器眼里是同一个函数签名。方括号里的 8 不是约束只是注释编译器会主动把它丢掉。原因不复杂C 在设计时就没打算让数组成为可传递的值。如果函数调用要拷贝整个数组栈帧大小就得在编译期由实参决定而实参可以是任意长度的数组更现实的是拷贝成本一个几 MB 的数组传参就废掉了函数调用的开销模型。所以数组名在绝大多数表达式里退化为指向首元素的指针函数传参是其中最典型的一处。#include stdio.h #include string.h /* 三种写法等价buf 在函数内部已经是指针 */ void show(char buf[8]) { printf(%zu\n, sizeof(buf)); /* 4 或 8指针宽度不是 8 */ printf(%zu\n, strlen(buf)); /* 运行期数到 \0 为止 */ } int main(void) { char str1[] Hello; /* 6自动补 \0 */ char str2[5] {H,e,l,l,o}; /* 5没有 \0不是字符串 */ char str3[6] {H,e,l,l,o,\0}; /* 6手写结束符 */ char *p1 Hello; /* 4指针本身 */ char *p2[] {hello, world}; /* 8两个指针 */ printf(%zu %zu\n, sizeof(str1), strlen(str1)); /* 6 5 */ printf(%zu\n, sizeof(str2)); /* 5 */ printf(%zu %zu\n, sizeof(str3), strlen(str3)); /* 6 5 */ printf(%zu %zu\n, sizeof(p1), sizeof(p2)); /* 4 8 */ show(str1); return 0; }逐行看返回值。str1用字符串字面量初始化编译器在末尾补了一个\0所以sizeof是 6strlen数到结束符前停下是 5。str2显式给了 5 个字符一个结束符都没多sizeof是 5而strlen(str2)属于未定义行为——它会一直往后读栈上的字节直到碰巧遇到一个 0结果取决于栈里残留了什么。str3手写了\0两个值分别是 6 和 5。p1是指针宽度 4p2是“指针的数组”两个元素共 8 字节注意它不是“数组的指针”char (*p)[2]才是。show函数里的两行是整个考点的核心sizeof(buf)拿到的是指针宽度而不是 8。数组退化发生在进入函数的那一刻长度信息在传参这一步就丢了函数体里没有任何办法把它找回来。所以面试官问“函数内怎么知道数组大小”标准答案是“问不出来得让调用方传进来”而不是去优化sizeof的写法。手里有一个数组时量元素个数可以用宏/* 只在“当前作用域里 a 确实是数组”时成立 */ #define ARR_SIZE(a) (sizeof(a) / sizeof((a)[0]))这个宏在main里对str1用是 6/16对int arr[10]用是 40/410。但对一个已经退化的指针用就会得到 4/41 或者 8/42——这不是宏写错了而是类型里本来就没有长度了。很多血泪经验都来自把ARR_SIZE用在了形参上。2.3 strlen 的契约无符号返回值与缺失的结束符strlen是库函数参数类型是const char *行为和sizeof完全不在一个层面上它在运行时逐字节扫描遇到\0停下返回的是字符个数不含结束符。它没法接void *、没法接结构体、也没法接一个没有结束符的字节缓冲——最后这条是踩坑重灾区。它的返回值是size_t无符号。这就带来一个非常隐蔽的坑#include string.h void compare(const char *a, const char *b) { /* 危险size_t 相减a 比 b 短时结果回绕成极大值 */ if (strlen(a) - strlen(b) 0) { /* 条件恒为真逻辑已经错了 */ } /* 正确写法显式比较不要做减法 */ if (strlen(a) strlen(b)) { } }strlen(ab) - strlen(abcdef)在数学上是 -4在size_t的模运算里是一个接近SIZE_MAX的巨大正数于是 0恒成立。这类 bug 在笔试里以“以下代码输出什么”的形式出现在实际项目里以“为什么长度判断永远进 if”的形式出现排查起来相当费时间算得上是这门语言里最不划算的一类后悔药。顺带说一个边界sizeof用在二维数组上不退化char p[3][8]量出 24而char *p[3]量出 12三个指针。两者的区别不在维度而在每一层存的是字符还是地址。3. 栈、堆与内存分区int a 和 malloc(100) 差在哪3.1 五段式布局与变量的生命周期一个由 C 编译出来的进程地址空间大致被切成五块性质完全不同。这套划分是理解static、const、字符串字面量、悬空指针的共同基础。区域存什么谁分配生命周期栈 stack函数参数、局部变量、返回地址编译器自动随栈帧进出堆 heapmalloc/new申请的内存程序员手动手动free进程退出兜底全局/静态区全局变量、static变量编译器整个进程常量区字符串字面量、const只读数据编译器整个进程代码区函数体的机器码编译器整个进程全局区还分成初始化和未初始化两半.data存显式初始化过的全局变量.bss存没初始化的清零后者不占可执行文件的体积只在加载时按声明大小预留。这也是为什么“未初始化的全局变量默认是 0”这件事能成立——并不是有谁去赋值而是整段内存被清零过。#include stdlib.h #include string.h int a 0; /* .data全局已初始化 */ char *p1; /* .bss 全局未初始化默认 NULL */ int main(void) { int b; /* 栈内容不确定 */ char s[] abc; /* 栈4 字节运行期从常量区拷过来 */ char *p3 123456; /* p3 在栈123456\0 在常量区 */ static int c 0; /* .data静态初始化区作用域仍是本文件 */ p1 (char *)malloc(10); /* p1 指向堆 */ strcpy(p1, 123456); /* 编译器的常量折叠可能让它和 p3 同一地址 */ free(p1); p1 NULL; /* 置空避免悬空指针 */ return 0; }s和p3的差别是理解栈与常量区的关键char s[] abc声明的是一个数组编译器在栈上留出 4 字节运行期把常量区里的abc\0拷进去所以s可写char *p3 123456只声明一个指针它指向常量区通过p3写就是未定义行为典型表现是段错误。3.2 栈帧怎么长出来局部数组越界会踩到什么每次函数调用栈指针下移一段形成一个栈帧里面依次是从高地址到低地址调用者的返回地址、上一个帧基址、局部变量、临时空间。参数一般通过寄存器传递超过寄存器数量的部分压在栈上。栈向低地址方向增长堆向高地址方向增长两者中间隔着未映射的空洞撞不上。理解这个布局就能理解“局部数组越界写”为什么危险得离谱。越界往高地址方向写第一个撞上的是保存的帧基址和返回地址——函数返回时ret会跳到被覆盖的那个值上。这不是理论上的可能性而是经典攻击面的物理来源。现代编译器给出的对策是栈保护在返回地址和局部变量之间插一个随机 canary函数返回前检查它有没有被改。提示调试时看到*** stack smashing detected ***说明发生的就是这类越界别只盯着出问题的那个变量看往回查所有写入局部数组的循环边界。栈的另一个特性是大小受限典型是几 MB超了就是栈溢出最常见于递归没有终止条件或者局部数组开得过大。堆的容量受限于进程的虚拟地址空间和机器的实际内存所以“大块数据放堆上”是一条几乎无条件的规则。3.3 堆的空闲链表free 为什么不需要告诉长度malloc(100)背后并没有一个“堆管理器”在时刻记录每块内存它做的是一件更朴素的事维护一条记录空闲块的链表收到申请时遍历这条链找第一个足够大first fit的块把这块从链表里摘下来如果剩余空间还够一个最小块就切一刀把剩下的挂回链表。free则是反向操作把块还回去并检查物理相邻的块是否也空闲是就合并防止堆碎成渣。free(p)不传长度却知道要还多少答案藏在块头里。每次分配管理器会在返回给用户的地址前面多留几个字节存这一块的实际大小和状态位free时把指针往回挪固定偏移就能读到。所以传一个不是malloc返回的指针给free或者传一个已经free过的指针会读到垃圾块头表现为堆损坏或者直接中止。同一块内存释放两次double free是新手里最典型的一类翻车。还有一个常被忽略的事实malloc(100)拿到的可用空间往往是 100 字节没错但进程实际占用的更多因为块头、对齐填充和分配粒度都在里面。这也是为什么“用sizeof去验证malloc分配了多少”是行不通的——sizeof(p)量出的是指针宽度。4. 大小端与结构体布局0x12345678 在内存里怎么躺4.1 字节序是“多字节数据”的存放顺序字节序只对 int、short、long 这类多字节类型有意义char只有一个字节无所谓端。大端把高位字节放在低地址小端相反。以0x12345678为例32 位机上一个 int 占 4 字节地址偏移大端小端00x120x7810x340x5620x560x3430x780x12大端的字节顺序和人类书写顺序一致读十六进制 dump 时一眼能看出数值小端的低位在低地址硬件上的好处是加法器从低位开始进位读一个多字节值的时候地址不用变截断成小类型也只需要取前面的字节。两种做法各有道理业界没有统一于是就有了跨平台交换数据的麻烦——网络字节序定成了大端本机如果是小端收发时要转。判定本机字节序最直接的写法是拿一个多字节变量当地址看/* 返回 1 表示小端0 表示大端 */ int is_little_endian(void) { unsigned int x 0x12345678; unsigned char *p (unsigned char *)x; /* 取最低地址那个字节小端应该是 0x78 */ return *p 0x78; }这里能成立的前提是unsigned char *逐字节访问不违反别名规则且char恰好是 1 字节。用union也能做同样的事只是可读性差一点union probe { unsigned int i; unsigned char c[4]; }; /* 写 i 读 c[0]看它是 0x78 还是 0x12 */判断字节序在笔试里是小题在工程里却是解协议、读二进制文件、做序列化时的前置条件。搞反了的表现是数值全部错得离谱但又不是随机——比如把 0x1234 读成 0x3412这种“稳定的错”恰恰是字节序问题的特征。4.2 结构体大小不是成员大小相加struct BBB { long num; int index; char *name; }在 32 位环境下是 12 字节刚好等于 444。但这个“刚好”是运气换一组成员就不成立。对齐规则有两条每个成员的起始偏移必须是它对宽度的整数倍取成员自身大小和编译器对齐参数中较小的那个结构体总大小必须是最大成员对齐值的整数倍不够就补尾部填充。struct A { char c; /* 偏移 0占 1 字节 */ /* 3 字节填充下面 int 必须落在 4 的倍数上 */ int i; /* 偏移 4 */ char d; /* 偏移 8 */ /* 3 字节尾部填充总大小要对齐到 4 */ }; /* sizeof 12不是 6 */ struct B { int i; /* 偏移 0 */ char c; /* 偏移 4 */ char d; /* 偏移 5 */ /* 2 字节尾部填充 */ }; /* sizeof 8 */同样的三个成员换个顺序就从 12 变成 8。这也是为什么调试协议结构体时sizeof和“把成员大小加起来”永远对不上。想要紧凑布局可以按对齐值从大到小排列成员或者用编译器提供的 packed 属性——代价是访问可能变成非对齐读在某些架构上直接触发异常换来的是省几个字节。这是典型的空间换时间还是时间换空间的选择题没有标准答案。union的规则更简单大小等于最大成员的大小并且要向上对齐到最大对齐值。union U { char c; int i; double d; }在 32 位下是 8 字节。位域则更微妙同一个字节里从高位往低位切还是从低位往高位切取决于编译器和目标平台的字节序跨平台代码里最好别依赖位域的物理顺序。4.3 指针加法为什么要乘上类型宽度p 0x200里p是struct BBB *结果不是p 512而是p 512 * sizeof(struct BBB)。指针算术的单位是“元素”不是“字节”这是类型系统在算术上的唯一体现。同一个地址值用不同的指针类型解释加同样的偏移会落在完全不同的位置。struct BBB { long num; int index; char *name; } *p; /* 假设 p 0x100000032 位环境 sizeof(struct BBB) 12 */ /* p 0x200 0x1000000 0x200 * 12 0x1001800 */ /* (long)p 0x200 0x1000000 0x200 0x1000200 */ /* (char *)p 0x200 0x1000000 0x200 0x1000200 */三行的区别全在“加法的单位”上。第一行p还是结构体指针编译器自动乘上 12第二行先转成整数再加地址算术退化成普通整数加法第三行转成字符指针char宽度是 1所以等价于字节加法。笔试爱考这个因为它一次性检验了三件事知不知道指针算术按类型缩放、知不知道强制转换会改变缩放因子、以及会不会把(long)p n和(long *)(p n)搞混。多维数组的偏移是同一套规则的展开。int a[2][3]里a的类型是“3 个 int 的数组的指针”所以a 1跨过 12 字节*(a1)退化成int *再1跨过 4 字节。写成口诀是“解引用降一维加一跨一行”。字符指针数组char *p[] {ABC,DEF}则要小心p 1跨的是一个指针宽度*(p1)才是第二个字符串的首地址*(p1)1指向E。5. 避坑排查五个在现场最容易翻车的细节5.1 长度与类型相关的三个坑现象一函数里sizeof量数组得到 4。原因形参里的数组声明被编译器改写成指针长度信息在传参时已经丢了。解决把长度作为独立形参传进来或者用struct把数组包起来传让“数组”变成真正可以按值传递的对象。现象二strlen(a) - strlen(b) 0恒成立。原因strlen返回size_t无符号相减在结果为负时回绕成极大值。解决不要对长度做减法比较直接写strlen(a) strlen(b)或者把结果转成long再用。这个坑在笔试里伪装成“以下代码输出什么”在项目里伪装成“判断永远为真”。现象三ARR_SIZE(p)用在指针上得到 1 或 2。原因宏展开成指针宽度除以元素宽度指针类型里没有元素个数。解决只在数组还在当前作用域时用这个宏一旦跨越函数边界一律走传参。5.2 内存操作相关的两个坑现象四memcpy拷贝重叠区域后数据被写坏。原因memcpy的原样拷贝不做方向判断源和目的区间重叠时前向写会把还没读的数据覆盖掉。这是它和memmove的唯一区别——memmove会先判断重叠方向需要时改成从后往前拷。解决不确定是否重叠就用memmove它的额外开销只是一次地址比较。void *my_memmove(void *dst, const void *src, unsigned int n) { unsigned char *d (unsigned char *)dst; const unsigned char *s (const unsigned char *)src; if (d s || n 0) return dst; if (d s || d s n) { /* 无重叠或目的在前正向拷贝 */ while (n--) *d *s; } else { /* 目的落在源区间内从尾部倒着拷避免覆盖未读数据 */ d n; s n; while (n--) *--d *--s; } return dst; }现象五宏参数没加括号或者多语句宏在if后面只执行了一半。原因宏是预处理期的文本替换#define ADD(a,b) ab展开成ADD(x,y)*2就是xy*2乘法优先级插进来了。多语句宏写成ab; a;在if (cond) DO(a,b); else ...里会变成if只管住第一条语句else直接悬空。解决每个参数和整个表达式都套括号多语句用do { ... } while (0)包成一个语法上的单语句。这个do-while(0)不是什么奇技淫巧它是唯一能在宏里安全表达“多条语句”的写法因为末尾的while(0)让循环体恰好执行一次而do-while结构在if/else后面不需要额外分号。6. const、static、volatile编译器被允许做哪些假设这三个关键字看起来毫不相干其实都在回答同一个问题——这个对象的什么属性是编译器可以依赖的。const说的是“我不会通过这个名字改它”于是编译器可以把它放进只读段可以做常量折叠可以在函数签名上向调用者承诺不改实参。static说的是“这个名字只在本翻译单元可见”于是链接器不必为它导出符号编译器也知道没有人从外部改它激进一点的优化就有了依据。volatile说的是反过来的话——每次访问都必须真的去内存读不许用寄存器里的备份因为值可能被硬件或者中断改掉。这里有一处最经典的取舍const volatile组合。只读的状态寄存器正好符合这两个描述——程序不该写它const但硬件随时可能改它volatile。两个限定符不矛盾因为它们约束的是不同的主体。同理volatile int *p和int * volatile p也不是一回事前者说的是“指向的对象是易变的”后者说的是“这个指针本身是易变的”。而volatile最容易被误解成“线程安全”。它只保证不缓存、不合并、不重排这一次访问不保证多个写操作是原子的也不保证两个线程之间没有竞争。用volatile挡多线程共享变量的做法本质上是拿一个内存可见性约束去顶替互斥多数情况下不管用。再到宏和typedef的边界取舍的逻辑是同构的。宏是文本替换在预处理期生效没有作用域没有类型检查能表达typedef表达不了的东西比如MIN、ARR_SIZE、条件编译typedef是编译期的一部分有作用域有类型检查定义指针别名时不会踩#define那种“把int*拆成int加*”的坑。所以判断标准很清楚需要类型就typedef需要文本或者需要在预处理期就决定的东西才用宏。我自己的习惯是凡是要在代码里写sizeof、写指针偏移、写字节序转换的地方先在纸上把变量落在哪个段画出来——局部还是堆、常量还是可写、单位是字节还是元素。这四行画完答案基本就出来了剩下的只是措辞。刚入行时我在一次跨平台数据解析上栽过二十几行代码里同时混着小端读取和结构体对齐最后是靠着把每个字段的偏移和字节序标在注释里才理清的从那以后我每次碰二进制布局都强制先写一张偏移表再动代码。希望这些拆解能帮到你那份题集里的每道题背后都有一条这样的分界线值得一条条划过去。本文还有配套的精品资源点击获取

相关推荐

Leetcode链表专题:从基础操作到经典题目解析
Leetcode链表专题:从基础操作到经典题目解析

1. Leetcode链表专题:从基础操作到经典题目解析链表作为数据结构中的重要组成部分,一直是算法面试中的高频考点。最近在Leetcode周赛430中,链表相关题目再次成为讨论热点。今天我将带大家深入解析Leetcode链表部分的三个经典题目:… · 2026/11/3 20:18:49

macOS Node.js 多版本管理:nvm 配置与环境变量避坑指南
macOS Node.js 多版本管理:nvm 配置与环境变量避坑指南

1. 为什么 macOS 上的 Node.js 环境总在“反复重装”?——nvm 是唯一解法你有没有过这样的经历:在 macOS 上装完 Node.js,跑几个项目没问题,可一换项目版本,或者某天终端突然报错command not found: node,再… · 2026/11/3 20:17:49

BI实施方法论:从决策场景到指标口径与数据分层落地
BI实施方法论:从决策场景到指标口径与数据分层落地

简介:这份文档面向企业BI项目负责人、数据分析师与信息化实施人员,系统梳理商务智能从立项到运营的完整方法论,帮助团队解决目标不清、数据质量不稳、需求与开发脱节及上线后价值难评估等问题。压缩包仅含1个docx文件,约108KB&… · 2026/11/3 20:17:49

数据库系统原理期末备考指南与核心考点解析
数据库系统原理期末备考指南与核心考点解析

1. 数据库课程期末备考全攻略作为计算机专业的核心课程,数据库系统原理的期末考试往往让不少同学头疼。中国海洋大学的数据库期末考通常涵盖关系代数、SQL编程、范式理论、事务处理等核心内容,既考察理论基础又注重实践能力。结合多年教学重点和往届真题… · 2026/11/3 21:07:21

GitHub热榜解读:agent框架、computer-use与自托管环境的技术选型
GitHub热榜解读:agent框架、computer-use与自托管环境的技术选型

1. 从 9.22 这份热榜里,我看到了三条正在分岔的技术路线9 月 22 日这天的 GitHub 热榜挺有意思,五个项目里没有一个是纯粹的“工具类”小玩意,全部集中在三个方向上:agent 框架、computer-use、自托管环境。这三个词单看都不新鲜&… · 2026/11/3 21:07:21

数据库课程高效复习指南:从理论到实践
数据库课程高效复习指南:从理论到实践

1. 项目背景与需求分析中国海洋大学作为国内知名的"双一流"建设高校,其计算机相关专业的数据库课程一直以理论扎实、实践性强著称。每到期末季,学生们都会面临数据库课程的期末考试压力。这个项目标题虽然简短,但背后折射出的是高校… · 2026/11/3 21:07:21

从零手搓AI工程:不依赖框架实现RAG全链路
从零手搓AI工程:不依赖框架实现RAG全链路

1. 从零搭建AI工程能力:为什么“手搓一遍”比调包更值钱 这两年AI应用层的工具链成熟得吓人,LangChain、LlamaIndex、各种Agent框架几乎把能封装的都封装了。打开文档,几行代码就能跑通一个RAG问答;复制一段示例,十分钟… · 2026/11/3 21:07:21

Paperclip:Node.js与React协同的AI Agent编排协议层
Paperclip:Node.js与React协同的AI Agent编排协议层

1. “Paperclip”不是回形针:它是一套AI Agent开发范式的代号最近在几个技术社区和内部分享里,频繁看到“paperclip”这个词被当作项目代号或内部术语使用——不是指办公用品,也不是某款UI组件库,而是一个正在快速成型的、面向AI … · 2026/11/3 21:07:21

RAP Fiori开发核心:CDS+Behavior+Projection三位一体架构
RAP Fiori开发核心:CDS+Behavior+Projection三位一体架构

简介:本资源是一份面向ABAP开发者与SAP Fiori初/中级学习者的RAP(ABAP RESTful Application Programming)开发教程合集,聚焦SAP S/4HANA环境下基于ABAP的现代化Fiori应用构建。内容系统覆盖RAP核心概念、CDS视图建模、行为定义与实… · 2026/11/3 21:06:21

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码