静态变量作用域问题:STM32L0项目中static对象双实例排查
问题:单元测试中静态对象出现双实例的原因排查
我在单元测试中为了访问被测模块的所有静态变量与函数,直接把buildingblock.c包含进了unittest.c。使用gcc-11.2.1编译,目标平台是STM32L0微控制器。在小型测试示例里,静态对象my只有一个实例,单元测试正常通过;但在大型开发项目中,调试发现这个对象出现了两个实例,没有编译报错或警告,但测试结果异常。只有当我排除buildingblock.c的单独编译、仅通过头文件包含的方式时,对象实例才唯一,测试符合预期。现在想找出这个双实例问题的原因。
源码:buildingblock.h
#ifndef __BUILDINGBLOCK_H #define __BUILDINGBLOCK_H #include <stdint.h> uint8_t pub_getVar(); #endif
源码:buildingblock.c
#include "buildingblock.h" typedef struct{ uint8_t var; }my_t; static my_t my = { .var = 0 }; static uint8_t getVar(){ return my.var; } #ifndef YES_TESTCODE uint8_t pub_getVar(){ return my.var; } #endif
源码:unittest.h
#ifndef __UNITTEST_H #define __UNITTEST_H // 这个宏用于处理测试代码的特殊逻辑 // 比如:如果在单元测试中定义公共变量/函数,链接器会因重复定义报错 #define YES_TESTCODE void ut_run(); #endif
源码:unittest.c
// 使用lib-unity测试框架 #include "unity.h" // 单元测试库头文件 #include "unittest.h" #include "buildingblock.c" // 测试静态变量自增 static void test(){ my.var++; uint8_t ret = getVar(); TEST_ASSERT_EQUAL_UINT8(1,ret); } // 从main函数调用此函数执行测试 void ut_run() { pub_getVar(); UNITY_BEGIN(); RUN_TEST(test); UNITY_END(); }
问题原因分析
核心问题出在编译单元重复定义:
- 在大型项目中,你大概率同时将
buildingblock.c作为独立编译单元进行编译,又在unittest.c中通过#include "buildingblock.c"把它的代码嵌入到了unittest.c的编译单元里。 static关键字的作用域是单个编译单元:独立编译的buildingblock.o里会生成一个my的实例;unittest.c编译时,因为嵌入了buildingblock.c的代码,会在unittest.o里生成另一个完全独立的my实例。- 小型测试中你可能只编译了
unittest.c(没有单独编译buildingblock.c),所以只有一个实例,测试正常;但大型项目中两个编译单元都被编译链接,就出现了双实例。 - 当你排除
buildingblock.c的单独编译时,只有unittest.c这一个编译单元包含了buildingblock.c的代码,自然只有一个my实例,测试符合预期。
补充:直接包含.c文件的方式虽然能快速访问静态成员,但很容易引发这类编译单元重复的问题。更规范的做法是为单元测试编写专门的测试桩,或者在被测代码中通过条件编译暴露静态成员给测试代码(比如用#ifdef TEST把静态成员临时改为全局,或者提供测试专用的访问接口)。
内容的提问来源于stack exchange,提问作者MaxM.
相关产品推荐
相关产品推荐

