【发布时间】:2017-12-01 19:29:57
【问题描述】:
总结
我有几个 C 源文件,它们都声明了单独的同名静态全局变量。我的理解是每个文件中的静态全局变量应该只在该文件中可见,不应该应用外部链接,但实际上我在调试时可以看到同名变量共享相同的内存地址。
就像static 关键字被忽略,全局变量被视为extern。这是为什么呢?
示例代码
foo.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someFooFunc(void) {
myVar = VALUE_B;
}
bar.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someBarFunc(void) {
myVar = VALUE_C;
}
baz.c:
/* Private variables -----------------------------------*/
static myEnumType myVar = VALUE_A;
/* Exported functions ----------------------------------*/
void someBazFunc(void) {
myVar = VALUE_D;
}
调试观察
- 在每个函数内的
myVar = ...行上设置断点。 - 从 main 开始依次调用
someFooFunc、someBarFunc和someBazFunc。 - 在
someFooFuncmyVar内部最初设置为VALUE_A,越过该行后设置为VALUE_B。 - 内部
someBarFuncmyVar出于某种原因在越线之前最初设置为VALUE_B,而不是我所期望的VALUE_A,这表明链接器可能已经合并了基于它们具有的单独全局变量同名。 -
someBazFunc在被调用时也是如此。 - 如果我使用调试器在每个断点给出相同地址时评估
&myVar的值。
工具和标志
工具链:GNU ARM GCC (6.2 2016q4)
编译器选项:
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -mlong-calls -O1 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -DDEBUG -DTRACE -DOS_USE_TRACE_ITM -DSTM32L476xx -I"../include" -I"../system/include" -I"../system/include/cmsis" -I"../system/include/stm32l4xx" -I"../system/include/cmsis/device" -I"../foo/inc" -std=gnu11 -MMD -MP -MF"foo/src/foo.d" -MT"foo/src/foo.o" -c -o "foo/src/foo.o" "../foo/src/foo.c"
链接器选项:
arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mlong-calls -O1 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -T mem.ld -T libs.ld -T sections.ld -nostartfiles -Xlinker --gc-sections -L"../ldscripts" -Wl,-Map,"myProj.map" --specs=nano.specs -o ...
【问题讨论】:
-
这可能是调试器中的一些名称修改问题,导致它欺骗您。不要信任调试器,而是尝试从它们各自的翻译单元中打印变量的地址和值。
-
嗯,不同模块中变量的相同命名可能会破坏调试器符号解析。考虑查看
someFooFunc、someBarFunc和someBazFunc汇编代码 - 这可能会提示您这些变量实际上共享相同的地址(这不应该是真的)。 -
为什么编译用C前端,链接用g++?
-
使您的程序在变量有或没有单独存储时行为会有所不同,并通过运行程序确认输出。也许链接器检测到它可以做它正在做的事情,因为它不会影响程序。
-
GDB's manual on program variables 描述如何解析特定变量。一个是
bar.c::myVar,另一个是foo.c::myVar。此外,如果可用,建议使用-gstabs,并希望您没有带有成员c的类foo。