一、背景
笔者正在写一个以stm32为微控制器的环境检测设备,将数据通过wifi模块上传到云服务的程序。当写到数据解析部分的时候,使用了cJSON库。并在cJSON库中做了一些修改。
奇怪的问题来了,本来烧录程序后,应该先运行设备初始化等信息才对 (如下图)
但是当我移植cJSON库并且修改了代码后,发现程序直接跑崩溃了,我甚至还没调用相关的json解析函数,怎么就能跑丢了呢?
二、分析
理论上程序如果产生段错误,堆栈溢出等现象,会在运行时才崩溃,这还没运行到这个函数呢呢,说明bug不是产生在运行时,那么设想一下程序编译运行的几个步骤:预处理->编译->汇编->链接->可执行文件。
很显然 (其实也不显然,折磨了我一个下午) 就联想到,是不是在链接的时候出的问题。我就找到了我新增的函数部分。慢慢调试。
这里我使用了#if 预处理指令方便调试,可以发现当编译包含了fprintf语句时,程序直接崩溃,而不编译这段程序则正常输出。
那么本质问题是什么呢?
在许多嵌入式系统和特别是裸机环境中,标准C库函数(如 printf
)可能依赖于底层的I/O实现,这通常需要开发者提供,比如 putchar
或 _write
等函数。这些底层函数可能与具体的硬件接口(例如UART)交互,而这些接口在嵌入式环境中可能没有默认实现。如果这些函数没有被正确实现但被调用了,可能会导致不确定的行为或运行时错误。这种情况下的“错误”通常不会在编译阶段捕捉到,因为从编译器的角度看,printf
是一个完全合法的函数调用。问题通常在链接阶段或运行时暴露出来,因为那时缺少了实际的函数实现。
所以解决办法,就是不使用fprintf或者printf啦,或者将printf重定向,重写putchar、wirite函数的实现即可。故在keil中要慎用标准输入输出!