gcc、段错误和静态变量地址更改之谜(跨堆栈帧)

gcc, segfault, and the mystery of the changing address of a static variable (across stack frames)

我的应用程序出现了一个段错误,现在我已经研究了好几个小时了。我正在使用 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 字节)。

g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39.0.3)

附加信息:

我已经使用更简单的代码重现了这个问题:

#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
#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

test1.cpp

using namespace std;

#include <iostream>
#include <string>
#include "test1.h"

Test1::Test1(): m_str(std::string("Extra ") + CONSTANT) 
{
     
};
#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"


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。

反汇编似乎表明问题在于静态 variables/constants.

的初始化顺序

我不太擅长 C++ 和编译器。也许有人知道到底发生了什么? GCC 错误还是只有我?

我想总结一下我从上面的有用评论中学到的东西:

  1. 静态变量在不同的地方有不同的地址是必然的Translation Units.
  2. 由于被称为Static Initialization Fiasco的现象,以我的方式使用静态变量,依赖于“好运”变量在使用前被初始化。如果在编译时运气不佳,您将在尝试使用变量时遇到段错误。

为了解决问题 2,我将我的静态变量包装在一个方法中(getter 之类的)并使用该方法而不是变量。它强制在正确的时间初始化其他静态变量。该方法看起来像这样:

Test getTest(){
    static Test test;
    return test;
}

感谢 David Schwartz 和 n.'pronouns' 的指导。