c++ - gcc、segfault和静态变量地址变化之谜(跨栈帧)
问题描述
我在我的应用程序中遇到了一个段错误,并且现在已经研究了几个小时。我正在使用 gdb 分析回溯并注意到以下内容:
(gdb) frame 3
(gdb) info address C_STATIC_STRING
Symbol "C_STATIC_STRING" is static storage at address 0x66a660.
(gdb) frame 2
(gdb) info address C_STATIC_STRING
Symbol "C_STATIC_STRING" is static storage at address 0x66b800.
上面有 2 个堆栈帧const string C_STATIC_STRING
在同一个头文件中引用相同的内容,但是一个帧正确地寻址变量(第 3 帧),而另一个(第 2 帧)具有偏移地址(如果我计算正确,则为 4512 字节)。
- 0x66a660 一个地址正确的字符串
- 如果读取 0x66b800 会导致错误:Cannot access memory at address 0xffffffffffffffe8
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39.0.3)
附加信息:
我设法使用更简单的代码重现了该问题:
- constants.h - 包含宏和常量
#ifndef CONSTANTS_H
#define CONSTANTS_H
using namespace std;
#include <iostream>
#include <string>
#ifndef C_MACRO
#define C_MACRO "MACRO "
#endif
const std::string CONSTANT = C_MACRO "CONSTANT_STRING";
#endif
- Test1 类 - 有一个私有字符串,它在构造过程中使用 CONSTANT test1.h 初始化
#ifndef TEST1_H
#define TEST1_H
using namespace std;
#include <iostream>
#include <string>
#include "constants.h"
class Test1 {
public:
Test1();
std::string getString() {
return m_str;
}
private:
std::string m_str;
};
#endif
测试1.cpp
using namespace std;
#include <iostream>
#include <string>
#include "test1.h"
Test1::Test1(): m_str(std::string("Extra ") + CONSTANT)
{
};
- 测试类 - 拥有 Test1 test.h 的一个实例
#ifndef TEST_H
#define TEST_H
using namespace std;
#include <iostream>
#include <string>
#include "test1.h"
#include "constants.h"
class Test {
public:
Test1 getTest() {
return m_test;
}
private:
Test1 m_test;
};
#endif
test.cpp - 几乎是空的
using namespace std;
#include <iostream>
#include <string>
#include "test.h"
- main.cpp -- 有一个 Test 类的静态实例
using namespace std;
#include <iostream>
#include <string>
#include "test.h"
namespace NOTSTD {
Test variable;
}
using namespace NOTSTD;
int main()
{
std::cout << variable.getTest().getString() << " printed";
}
现在构建过程
- 生成文件
#Test makefile
CPP = g++
CPPFLAGS = -Wall -ggdb -O0
AR = ar
RANLIB = ranlib
OUTPUT = test
all:: $(OUTPUT)
for_static = test1.o
static_lib.a: $(for_static)
$(AR) qc $@ $(for_static)
$(RANLIB) $@
$(OUTPUT): static_lib.a test.o main.o
$(CPP) ${CPPFLAGS} test.o main.o -o $(OUTPUT) static_lib.a
%.o : %.cpp
$(CPP) $(CPPFLAGS) -c $< -o $@
clean:
rm -f $(OUTPUT)
rm -f *.o
rm -f *.a
Test1 被编译成一个静态库,然后用于编译其余部分。在 Cygwin 中,它按预期工作在 OEL 7 上,它会出现分段错误(无论优化级别如何)如果我省略静态链接库并只在 test1 中编译,那么它也适用于 OEL。
反汇编似乎表明问题在于静态变量/常量的初始化顺序。
我不太擅长 C++ 和编译器。也许有人知道到底发生了什么?GCC错误还是只有我?
解决方案
我想总结一下我从上面有用的评论中学到的东西:
- 静态变量在不同的翻译单元中有不同的地址是不可避免的。
- 由于称为“静态初始化惨败”的现象,以我的方式使用静态变量依赖于“好运”,以便在使用变量之前对其进行初始化。如果在编译时运气不在您身边,您将在尝试使用该变量时遇到分段错误。
为了解决问题 nr 2,我将我的静态变量包装在一个方法(某种 getter)中,并使用该方法而不是变量。它强制在正确的时间初始化其他静态变量。该方法看起来像这样:
Test getTest(){
static Test test;
return test;
}
感谢 David Schwartz 和 n.'pronouns'm 的指导。
推荐阅读
- python - 基于用户输入的循环程序
- laravel - Helm:在 Job 中的其他容器上执行命令
- ios - 如何用单个标记多次点击
- arrays - 如何引用数组变量中的所有元素
- javascript - 函数名称,“onButtonClick”或“onClickButton”
- c - C指针不等于另一个
- list - 我的问题是关于在 Dart 中构建不断增长的多维列表(数组)的方法
- linux - 如何使用 linux shell 选择文件中不同列的内容?
- android - 应用未设置:此应用仍处于开发模式,Facebook UI 更改
- excel - Excel 从非连续单元格中查找中位数并忽略 0 值