ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Keil逻辑分析仪Unknown Signal报错?从配置到底层原理彻底解决

Keil逻辑分析仪Unknown Signal报错?从配置到底层原理彻底解决 调了个把小时的GPIO翻转波形打开Keil内置的逻辑分析仪在Setup框里信心满满敲下一个GPIOA-ODR点击Add之后等来的不是方波而是一行冷冰冰的Unknown Signal。这种时候第一反应通常是“信号名输错了”于是换大小写、加下划线、删空格折腾一圈依然报错。我在这个报错上踩过的坑不比各位少后来认真梳理才发现绝大多数Unknown Signal根本不是信号名打错而是三个底层配置没做对。这篇文章就把这三个坑逐个拆开再附上一套我平时实际调试的完整流程希望对还在跟这行红字搏斗的人有点帮助。提示这篇文章主要针对Keil MDK 5.x、MDK 4.x以及Keil C51环境下内置逻辑分析仪Logic Analyzer添加信号报Unknown Signal的情况。内容涵盖ARM Cortex-M系列和C51系列单片机的常见用法。1. 先搞清楚逻辑分析仪到底在“认”什么信号要解决Unknown Signal不能光靠瞎试得先明白Keil的逻辑分析仪在后台做了什么。它不是简单维护一个“信号名列表”然后做字符串匹配而是在调试会话中把你输入的文本当作一个C表达式来求值。这个表达式会在目标芯片通过调试接口SWD或JTAG实时读取数据然后把数值绘制成波形。所以本质上凡是能在当前调试上下文中被求值成功的东西都能添加到逻辑分析仪里求不出来就报Unknown Signal。这就意味着能不能添加成功取决于三件事表达式本身的写法是否合法、符号是否存在于当前编译产物的符号表中、以及调试器是否能在当前运行状态下访问到这个符号对应的内存地址。任何一个环节出问题最后的表现都是同一个Unknown Signal。这也是为什么网上很多教程只讲了某一个原因而你照着操作却依然失败的缘故——因为你们踩的可能不是同一个坑。1.1 Unknown Signal报错的真实含义当一个信号添加失败时Keil的调试器会在符号表里尝试查找你输入的字符串。符号表来自编译器生成的调试信息DWARF或OMF格式里面记录了每个变量、寄存器、函数的名称、类型、地址和作用域。如果符号表里根本没有这个名字或者这个名字不在当前作用域内可见调试器就直接抛出Unknown Signal。另外还有一种情况名字存在但类型不被逻辑分析仪支持。逻辑分析仪偏爱整型、枚举、位域、指针等可以直接读内存的简单类型遇到结构体、浮点数组这类复合类型或者遇到寄存器变量放在CPU寄存器里而非内存里的变量也可能拒绝添加。这类问题在报错信息上同样是Unknown Signal容易让人误判成名字拼错。还有一个比较容易忽略的点逻辑分析仪在添加信号时需要芯片处于暂停Halt状态才能完成配置和符号解析而在采集波形时需要全速运行Run。如果你在运行状态下打开添加对话框有些版本的Keil会显示信号列表为空或者添加后没有反应。1.2 为什么很多“看起来正确”的信号名还是会失败举个例子。很多人喜欢在代码里写一句GPIOA-ODR然后到逻辑分析仪里原样输入。一般来说在MDK里这样操作是可以成功的前提是当前工程包含的芯片头文件比如stm32f1xx.h中GPIOA这个宏能够被调试器正确解析。如果GPIOA在头文件里被定义成较复杂的宏比如((GPIO_TypeDef *)GPIOA_BASE)调试表达式求值器可能能解析也可能不能解析取决于型号和编译器版本。一旦解析失败不用慌直接用寄存器地址强制转换表达式比如*(volatile unsigned long *)0x4001080C这个是GPIOA端口的输出数据寄存器ODR在STM32F103系列中的地址这么写绕开了所有宏展开和头文件依赖让调试器老老实实按绝对地址访问。用这种方式添加几乎不会出现Unknown Signal。C51平台同理。你在代码里写了P1 0xFF到逻辑分析仪里要观察P1端口直接输入P1通常可以因为P1是芯片的SFR特殊功能寄存器编译器对它有原生支持。但如果用了某些映射宏比如sbit LED P1^0那么输入LED往往不行要输入P1^0或者直接用P1再按位显示。这类问题的本质是宏在预处理阶段就被展开替换了调试符号表里不会保留宏名。所以凡是#define出来的东西到逻辑分析仪里几乎都会翻车。这也是我把“信号名到底该怎么写”单拎出来讲的原因。2. 配置一调试模式与调试器设置不对符号表根本加载不了很多时候Unknown Signal不是Keil不认识这个信号而是你压根没进入一个让它能认识信号的环境。我见过不少新手直接打开工程不编译不下载点开逻辑分析仪就开始加信号那当然会失败。逻辑分析仪必须依附于一个实际运行的调试会话而调试会话又分成两种软件仿真Simulator和硬件仿真硬件调试器连接目标板。这两种模式下信号的可见范围差异巨大。先看软件仿真。Keil的Simulator可以在没有开发板的情况下模拟芯片运行。对于C51系列Simulator对端口、定时器等外设的模拟比较完整所以逻辑分析仪里输入P1、P2这类SFR名称往往能正常工作。但对于ARM Cortex-M系列Simulator对GPIO、UART等外设的模拟精度参差不齐很多型号根本没有完整的外设行为模型。这时候你去添加GPIOA-ODR或者某个外设寄存器就可能出现Unknown Signal或者添加成功但波形一直是个平线。再看硬件仿真。使用ST-Link、J-Link、DAP-Link等调试器连接真实芯片时逻辑分析仪读取的是芯片内部真实的寄存器状态和变量值只要符号表里有这个符号且地址正确基本都能添加成功。但如果调试器选择的Target Driver没配对或者进入调试模式后没有正确加载程序同样会报错。2.1 硬件仿真与软件仿真的信号可见性差异我在实际调试中感受到的差异是这样的硬件仿真下逻辑分析仪就像一台可编程的逻辑分析仪只不过采样点是靠调试接口周期性读取目标内存来完成的。你给它一个地址它就能周期性地读出来画成波形。软件仿真的话逻辑分析仪采样的其实是模拟器内部维护的虚拟寄存器值这些值有没有被模拟器更新取决于芯片外设模型的完善度。所以第一步排查就是要确认自己到底用的是硬仿还是软仿。很多人以为装了Keil、选了ST-Link就是硬仿结果Options for Target里的Debug选项卡压根勾的还是左侧Use Simulator那自然会出现信号加不上、波形不对的怪问题。这样一个问题排查下来大概率的结论是不在正确调试模式下符号表加载不完整。调试器连不上目标板也会导致这个问题因为逻辑分析仪在添加信号的时候要向目标芯片发送调试命令如果连接不稳定或芯片没供电报的错也会异常诡异。2.2 正确配置调试器并确认进入调试状态以STM32 ST-Link为例配置步骤如下打开工程按AltF7进入Options for Target。切到Debug选项卡确保选中的是右侧Use下拉框里选择ST-Link Debugger。点旁边的Settings确认Port选SWMax Clock可以先用4MHz或者默认值如果能正常识别到芯片ID说明连接没问题。切到Utilities选项卡确认Flash Download里勾选了Reset and Run这样下载后芯片能直接跑起来。编译F7下载F8或LOAD图标然后进入调试CtrlF5。进入调试后先看看右下角寄存器窗口是否有值变化或者左上角有没有正常停在main函数入口。如果一切都正常再打开View - Analysis Windows - Logic Analyzer这时候加信号才靠谱。另外建议把Options for Target的Debug选项卡里的“Load Application at Startup”和“Run to main()”都勾上前者保证调试器启动时自动加载程序镜像和符号表后者保证一进调试就自动运行到main入口。这两个选项不勾的话符号表可能没被完整加载逻辑分析仪自然找不到信号。2.3 符号表是否加载可以用符号窗口验证我习惯在动手添加信号前先打开View - Symbol Window在搜索框里输入要观察的变量名。如果符号窗口里能看到这个符号说明符号表已经加载逻辑分析仪添加失败大概率是表达式写法或者类型问题。如果符号窗口里根本没有这个名字那问题在前端——程序没加载、符号表被剥离、或者变量被优化掉了。有一个容易忽略的点工程里如果开了“Browse Information”关闭选项或者在链接阶段使用了--strip_debug之类的参数生成的调试符号会不完整逻辑分析仪能添加的信号数量会锐减。这时候去Options for Target的Listing选项卡和Linker选项卡里检查一下不要勾选任何剥离调试信息的选项。3. 配置二信号名称的书写格式并不是随便敲个变量名就行这个问题最常见也最容易被误解。很多朋友以为逻辑分析仪添加信号就像在命令行里输入变量名一样敲对了就能加。实际上逻辑分析仪支持的是一套C表达式语法具备一定表达能力但也因此引入了一堆“看起来对、实际错”的坑。3.1 ARM内核与C51内核的信号命名差异先分平台说清楚。对于C51系列比如STC89C52、AT89S52这类端口信号的写法一般是P0、P1、P2、P3按位操作用类似P1^0的写法。因为C51编译器对SFR有天然支持所以这些信号名在逻辑分析仪里是能直接识别的。但要小心如果你定义了sbit变量比如sbit LED P1^0那逻辑分析仪里输入LED不一定认因为sbit本质上也是个宏定义调试器对它的支持并不一致。最稳的写法是直接用P1^0。对于ARM Cortex-M系列GPIO引脚本身没有像C51那样独立的位变量你需要观察的是GPIO寄存器的某个位。比如STM32的GPIOA端口观察整个输出数据寄存器就写GPIOA-ODR观察第5脚就写GPIOA-ODR (1 5)这个表达式在逻辑分析仪里是合法的可以直接添加。添加之后把Display Type设成Bit波形会显示为0/1跳变。很多人写的是GPIOA-ODR也添加成功了但波形显示的是一个十六进制数值看着一团乱麻其实就是在显示类型那里没选对。同样地读取引脚电平就观察GPIOA-IDR设置引脚就观察GPIOA-BSRR。如果你在用HAL库HAL_GPIO_WritePin里操作的是GPIOA-BSRR和GPIOA-BRR想捕捉动作观察BSRR的对应位会有惊喜。3.2 变量、数组、结构体、指针的正确表达式写法除了寄存器逻辑分析仪也支持观察普通变量但写法上有个隐蔽的坑局部变量必须在对应函数处于当前调用栈时才能添加。比如你在main函数里定义了一个局部变量counter当调试器暂停在某个中断服务函数里时你去添加counter会报Unknown Signal因为当前作用域看不到它。全局变量没有这个限制但也要注意作用域。static修饰的全局变量如果定义在a.c里而调试暂停在b.c的某一行直接输入变量名也可能找不到。解决方式是用完整限定名类似a.c::counter的写法在Keil里也可以试试。不过最保险的还是把断点停到目标文件里再添加。数组和结构体也支持。观察数组第3个元素就写arr[2]观察结构体成员就写obj.field。指针变量可以写*ptr来观察指针指向的内容。这些东西在Watch窗口里能显示在逻辑分析仪里也基本能添加但要注意类型不能太复杂。遇到结构体、联合体这类复合类型逻辑分析仪支持度很差建议拆成单个成员或者用位操作表达式代替。另外还有一类写法直接地址访问。这在前面提过比如观察0x4001080C地址上的32位数据就写*(volatile unsigned long *)0x4001080C逻辑分析仪完全支持这种C风格的强制转换加解引用表达式而且这种方式绕开了所有宏、类型、作用域的问题是我在排查Unknown Signal时的杀手锏。3.3 从Watch窗口验证表达式再复制进逻辑分析仪我自己的习惯是凡是逻辑分析仪添加失败先跑到Watch窗口里敲同样的表达式。Watch窗口和逻辑分析仪共用同一个表达式求值器如果Watch窗口能显示出数值那逻辑分析仪那边基本也能加。如果Watch窗口报错就说明表达式本身有问题或者符号不可见这时候再往宏展开、类型支持、作用域这些方向排查。实际操作时很多信号名又长又怪比如HAL库的结构体成员huart1.Instance-SR (1 5)这种长表达式手敲很容易出错我都是先在代码里选中表达式复制然后到Watch窗口粘贴验证确认能显示后再粘到逻辑分析仪的Setup对话框里。这个方法帮我省了大量排查时间。4. 配置三编译优化把信号“优化”没了这个问题最隐蔽也最容易让人心态爆炸。明明代码里清清楚楚定义了一个变量在Watch窗口里也能看到甚至单步执行时值都在变偏偏逻辑分析仪里一添加就Unknown Signal。这时候十有八九是编译器优化在捣鬼。4.1 优化等级如何让变量“人间蒸发”GCC、ARMCCKeil MDK的编译器和C51编译器在做优化时会把一些局部变量提升到CPU寄存器里而逻辑分析仪只能采样内存地址对寄存器变量无能为力。这就是为什么局部变量在-O1以下还能添加到-O2以上就开始玄学失败。更狠的是编译器还会做“死代码消除”。如果一个变量的值只在计算过程中被使用没有被输出到外设、没有被写入全局变量编译器可能认为它是无用的直接把它优化没了。这时候不仅逻辑分析仪看不到它你在Watch窗口里看它也会显示“identifier not found”之类。全局变量相对安全但不是绝对安全。如果全局变量被内联到多处代码里编译器可能生成多个副本或者把它放到寄存器里缓存。这种情况下逻辑分析仪读到的地址可能不是最新值所在的地址波形会出现跳变不真实的情况。4.2 调试期降低优化等级关键变量加上volatile最直接有效的做法是在调试阶段把编译器优化等级调到最低。对于MDK打开Options for Target - C/C选项卡Optimization选择Level 0 (-O0)。这时候所有变量都会老老实实分配到内存地址符号表也最完整。代价是代码体积变大、速度变慢但调试时期完全值得。对于某些变量即使整体开了-O2只要给它加上volatile修饰编译器就强制每次访问都从内存读写不会缓存到寄存器。在逻辑分析仪场景下volatile最大的作用不是防多线程竞争而是防优化器把变量搬进寄存器。所以我们调试时观察信号变量经常在变量定义前面加一个volatilevolatile uint32_t counter 0;另外还有一类特殊情况中断服务函数里修改的变量。如果这个变量只在中断里写、在主循环里读不加volatile的话主循环可能永远读到旧值。这种问题在逻辑分析仪上表现为波形长期不变不是Unknown Signal但同样让人抓狂。4.3 宏定义和typedef在调试符号表中的不可见性这一节值得单独拿出来说。很多人喜欢写类似这样的代码#define STATUS_LED_PIN 5 #define LED_ON() GPIOA-ODR | (1 5) #define LED_OFF() GPIOA-ODR ~(1 5)然后在逻辑分析仪里输入STATUS_LED_PIN想着观察这个信号的变化。结果当然是Unknown Signal因为宏在编译器预处理阶段就被替换掉了调试符号表里根本不会保存STATUS_LED_PIN这个名字。调试器不像编译器有完整的预处理上下文它只能看到编译完成后的符号。所以记住一个规律所有用#define定义的名字都不能直接作为逻辑分析仪的信号名。要观察宏对应的实际对象得用展开后的表达式。比如上面的LED就直接写GPIOA-ODR (1 5)。同理typedef支持相对好一些因为类型别名在调试信息里有记录但还是建议直接用底层变量名。这类问题的排查方法很简单在代码里右键点击你要观察的对象选择“Go to Definition”看它到底是被宏定义还是真实变量。如果跳到的是宏展开定义那到逻辑分析仪里就得换写法。5. 实操流程从零到一正确添加信号并查看波形讲完理论来走一遍实际流程。下面这个例子我用STM32F103C8T6Blue Pill ST-Link/V2目标是观察PA5引脚上的LED翻转波形。这套流程在MDK 5.30以上版本里验证过其他版本大同小异。5.1 工程侧需要提前做好的准备LED翻转的代码本身不复杂但有一个关键点要确保调试信息完整生成。具体在MDK里检查三处Options for Target - C/C - Debug Information复选框一定勾上。Optimization选择Level 0 (-O0)如果你非要开优化至少把目标函数的变量加上volatile。Options for Target - Debug - Load Application at Startup和Run to main()勾上。代码我一般写成这样方便逻辑分析仪捕捉#include stm32f1xx_hal.h volatile uint32_t led_state 0; int main(void) { HAL_Init(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_5; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); while (1) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); led_state GPIOA-ODR; HAL_Delay(100); } }这里我额外定义了一个全局变量led_state并时刻把ODR的当前值拷贝进去。这样做的好处是如果直接观察寄存器波形不理想还可以退而观察led_state这个变量。多一个后备手段排查会轻松很多。5.2 进入Debug后添加信号的完整步骤编译下载后按CtrlF5进入调试状态然后按F5全速运行。此时LED应该已经在闪了。如果LED没闪先回去查硬件不要继续在逻辑分析仪上浪费时间。运行状态下打开逻辑分析仪菜单栏点击View - Analysis Windows - Logic Analyzer。逻辑分析仪窗口打开后点击工具栏上的Setup按钮。在Logic Analyzer Setup对话框里找到右上角的文本框。输入GPIOA-ODR (1 5)点击Add。如果添加成功下方列表里会出现这个信号旁边有颜色标记。选中该信号在中间的Display Type区域选择Bit。点击Close关闭Setup对话框。回到逻辑分析仪主界面你会看到屏幕上出现一条波形。如果程序在运行波形应该是方波高电平和低电平交替出现。方波的周期应该和HAL_Delay(100)设置的延时对应大约200ms一个周期。我看到有人会问为什么我输入GPIOA-ODR (1 5)显示却是乱码那是因为没有把Display Type改成Bit。GPIOA-ODR是一个32位数包含所有引脚的状态你直接按十六进制看每一位的变化都混在一起当然看不清。切换成Bit模式再指定对应Bit位显示就清楚了。5.3 显示类型、颜色和位宽设置的细节Setup对话框里给每个信号都能单独设置显示类型。我整理了几种常用类型的适用场景显示类型适用场景推荐用法BitGPIO引脚电平、状态标志位配合表达式 GPIOA-ODR (15)Byte8位变量、端口数据观察整个P1端口值C51Hex32位寄存器变量观察定时器计数、状态寄存器Unsigned无符号数值曲线观察ADC采样值、计数器变化Signed有符号数值曲线观察加速度计、陀螺仪数据在Bit模式下还能指定显示电位比如我想同时观察PA5和PA6两路信号就分别添加两个信号一个写GPIOA-ODR (15)另一个写GPIOA-ODR (16)全部设成Bit然后给它们选不同颜色波形叠加在一起对比非常直观。时间轴操作上逻辑分析仪窗口支持鼠标滚轮缩放按住Shift加滚轮可以横向缩放Shift加左键拖拽可以框选放大区域。右键菜单里还能切换光标模式放上光标可以精确测量两个跳变沿之间的时间间隔这比用示波器量还方便。5.4 添加不上时的最后手段寄存器地址直写万一GPIOA-ODR这个表达式在你的工程里怎么都添加不上别纠结了直接用地址访问。查一下STM32F103的参考手册GPIOA的ODR寄存器地址是0x4001080C输入这个表达式*(volatile unsigned long *)0x4001080C这个表达式不依赖任何头文件、宏定义、符号表逻辑分析仪只需要把它当做一个内存读取操作理论上只要目标芯片处在这个地址范围就一定能读到数据。我遇到过有人的工程因为HAL库版本问题导致GPIOA宏解析异常用这种方法直接绕过去了。同样的方法适用于任何寄存器。比如观察USART1的数据寄存器DR查手册地址是0x40013804直接写*(volatile unsigned long *)0x40013804照样能看。这种硬核打法在应对复杂外设库时特别好使。6. 高频问题排查表与一些个人经验写到这里把平时群里帮人排查Unknown Signal时最常见的问题整理成一张速查表。遇到问题先按表里对照一遍能省下至少半小时的瞎折腾。故障现象可能原因解决方法变量名报Unknown Signal但代码里确实定义过优化把变量搬进寄存器或消除优化等级调-O0变量加volatileGPIOA-ODR这种寄存器表达式报错宏展开失败或头文件未被正确包含用地址强制转换表达式(volatile unsigned long)0x4001080C在Watch窗口能显示逻辑分析仪却报错表达式中含有非整数类型或复合类型不支持拆成简单表达式比如只取某个成员或按位与信号添加成功但波形一直是平线外设时钟没初始化引脚配置不对或显示类型不匹配检查初始化代码确认观察的是正确寄存器切换显示类型C51平台下sbit变量名添加不上sbit宏映射在调试符号表里不可见直接输入P1^0不要输别名暂停状态下添加信号时报错部分版本的Keil在Halt状态符号解析不完整先全速运行再打开逻辑分析仪添加RTOS工程里切换任务后信号失效当前上下文看不到其他任务的局部变量改观察全局变量或暂停在目标任务的代码里使用宏名#define LED_PIN 5添加不上宏在预处理阶段已展开符号表里没有宏名写展开后的表达式如GPIOA-ODR (1 5)加完后波形闪烁、值跳变异常采样周期和信号变化周期不匹配降低目标主频或增加延时让信号变化慢一些6.1 关于采样速率与实际信号频率的边界Keil内置逻辑分析仪本质上是通过调试接口周期性地读取目标内存这个读取频率受调试接口速率和目标芯片响应速度限制。SWD模式下的实用采样率通常在几kHz到几十kHz之间J-Link可能会快一点。这意味着它只适合观察低频信号比如GPIO电平翻转、定时器溢出事件、状态机跳变、变量数值波动。你要是拿它去看SPI时钟或者高速PWM大概率看到的是残缺甚至完全失真的波形这不是配置问题是采样率跟不上。真到了那种场景还是老老实实上外部逻辑分析仪比如Saleae逻辑分析仪、PulseView支持的DSLogic、或是几十块钱的8通道24MHz采样设备效果天差地别。所以在设计实验时我会刻意把目标信号放慢。比如调试LED闪烁先不加HAL_Delay(100)之前的类似延时代码反而把延时改成1000ms让波形周期拉长到2秒这样逻辑分析仪采样的点数更多波形更清晰。等确认逻辑正确后再改回实际延时值。6.2 多通道对比和测量游标的组合技巧逻辑分析仪里的多信号叠加非常适用于观察因果关系。我经常把触发信号和响应信号同时添加比如把按键引脚电平GPIOB-IDR (11)和LED输出电平GPIOA-ODR (15)放一起然后全速运行按一下按键再暂停就能直观看到输入和输出之间的时序关系。如果LED响应比按键按下慢了几百毫秒不用怀疑就是代码里延时逻辑有问题。测量游标对时间计算很有帮助。把光标放在波形上可以读取当前位置的时间戳两个光标之间的差值就是时间间隔。我在分析串口波特率时试过让单片机输出一个脉宽已知的方波再用游标量实际脉宽反过来估算系统时钟是否准确。这个方法比直接看示波器还方便。6.3 我踩坑后总结的几个习惯性做法最后分享几个我个人的习惯不一定适合所有人但对减少Unknown Signal出现频率确实有效。第一搭工程的时候就把调试要观察的全局变量单独放到一个文件里统一加volatile统一命名前缀。调试时直接用copy-paste把变量名弄到逻辑分析仪从源头上杜绝手打错误。我自己的工程里都一个debug_config.h里面放一坨flags和状态变量平时不参与业务逻辑专门留给调试工具观察。第二在添加信号前先在Watch窗口验证一遍表达式。这几乎成了我的肌肉记忆。验证能通过再复制到逻辑分析仪成功率接近百分之百。第三调试阶段常驻-O0。有人担心-O0下编译的代码运行速度慢、体积大对调试来说这都不是事。真正进入性能调优阶段再开优化那时候会用示波器加外部逻辑分析仪做验证Keil内置逻辑分析仪已经不太够用了。第四如果添加信号时卡住不动或者添加后逻辑分析仪窗口不刷新先退出调试会话重新进一次。Keil的调试环境偶尔会缓存过期符号表重启调试进程就能解决。这个坑我在长时间反复修改代码后碰到过好几次大部分情况下重启调试都能恢复。对我来说逻辑分析仪这个功能熟悉之后很多调试问题会变得简单很多。尤其是状态机跳变和GPIO时序这类问题肉眼看着波形远比单步翻代码来得直观。如果你还在被Unknown Signal较劲不妨照着上面三个配置逐项检查——先看调试模式和符号表再看表达式写法最后别忘了那个偷摸优化你的编译器。这套流程我实测下来基本能覆盖绝大多数情况。
返回列表