You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

静态变量作用域问题: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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 09:24:54