문제

C ++에서는 컴파일러가 선택한 순서 (몇 가지 제약 조건에 따라) 순서대로 정적 객체를 초기화하도록 선택할 수 있으며 일반적으로 정적 초기화 순서를 선택하거나 결정할 수 없다는 것을 알고 있습니다.

그러나 일단 프로그램이 컴파일되면 컴파일러는 이러한 객체를 초기화하기 위해 어떤 순서를 결정해야합니다. 디버깅 기호가있는 컴파일 된 프로그램에서 어떤 순서 정적 생성자를 호출 할 수 있습니까?

컨텍스트는 이것입니다. 나는 새로운 도구 체인 아래에 구축 될 때 Main () 앞에 갑자기 시작되는 상당한 프로그램이 있습니다. 이것은 정적 초기화 순서 문제이거나로드중인 라이브러리 중 하나에 문제가 있습니다. 그러나 GDB로 디버그 할 때 충돌 위치는 단순히 상징적 인 정보 나 백 트레이스가없는 원시 주소로보고됩니다. 이 두 가지 문제 중 어느 것이 정적으로 시작된 최초의 개체의 생성자에 중단 점을 배치함으로써 어떤 문제인지 결정하고 싶습니다. 그러나 어떤 객체인지 알 수 없습니다.

도움이 되었습니까?

해결책

Matthew Wilson 은이 질문에 답할 수있는 방법을 제공합니다. 이번 장 (사파리 도서 온라인 구독 필요) 불완전한 C ++. (좋은 책, 그건 그렇고) 요약하기 위해 그는 CUTrace.h 소스 파일을 포함한 클래스의 정적 인스턴스를 생성하는 헤더 (비표준 사전 처리기 매크로 사용 __BASE_FILE__) 만들어지면 그는 포함합니다 CUTrace.h 모든 소스 파일에서.

이를 위해서는 재 컴파일이 필요하지만 #include "Cutrace.h"는 스크립트를 통해 쉽게 추가하고 제거 할 수 있으므로 설정하기가 너무 어렵지 않아야합니다.

다른 팁

Linux의 G ++에서 정적 생성자 및 소멸자 순서는 .ctors 및 .dtors 섹션의 함수 포인터에 의해 결정됩니다. 충분한 디버깅을 사용할 수 있으면 실제로 백 트레이스를 얻을 수 있습니다.

(gdb) bt
#0  0xb7fe3402 in __kernel_vsyscall ()
#1  0xb7d59680 in *__GI_raise (sig=6)
    at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#2  0xb7d5cd68 in *__GI_abort () at abort.c:88
#3  0x08048477 in foo::foo() ()
#4  0x0804844e in __static_initialization_and_destruction_0(int, int) ()
#5  0x0804846a in global constructors keyed to foo_inst ()
#6  0x0804850d in __do_global_ctors_aux ()
#7  0x08048318 in _init ()
#8  0x080484a9 in __libc_csu_init ()
#9  0xb7d4470c in __libc_start_main (main=0x8048414 <main>, argc=1,
    ubp_av=0xbfffcbc4, init=0x8048490 <__libc_csu_init>,
    fini=0x8048480 <__libc_csu_fini>, rtld_fini=0xb7ff2820 <_dl_fini>,
    stack_end=0xbfffcbbc) at libc-start.c:181
#10 0x08048381 in _start () at ../sysdeps/i386/elf/start.S:119

LIBC 및 LIBSTDC ++의 디버깅 기호가 설치되어 있습니다. 보시다시피, 여기 충돌은 정적 객체 foo_inst의 foo :: foo () 생성자에서 발생했습니다.

초기화 프로세스에 침입하려면 __DO_GLOBAL_CTORS_AUX에서 중단 점을 설정하고 해체를 진행할 수 있습니다. 또는 위의 것과 같이 백 트레이스를 얻기 위해 충돌하기를 기다리십시오.

정적 공간에서 더미 변수를 초기화하고 해당 함수 호출에 중단 점을 넣을 수 있습니까?

extern "C" int breakOnMe () { return 0 };

int break1 = breakOnMe ();
float pi = 3.1415;
int break2 = breakOnMe ();
myClass x = myClass (1, 2, 3);

그런 다음 gdb 운영 break breakOnMe 프로그램을 실행하기 전에. 이는 정적 초기화에서 각각 전에 GDB가 일시 중지해야합니다.

나는 그것이 효과가 있다고 생각합니다 .. 나는 gdbbing에 약간 녹슬 었습니다.

TUS가 템플릿을 사용하여 초기화되는 순서를 찾을 수 있습니다. 의문. 관심있는 각 TU에 약간의 코드 변경이 필요합니다.

// order.h
//

#ifndef INCLUDED_ORDER
#define INCLUDED_ORDER

#include <iostream>

inline int showCountAndFile (const char * file)
{
  static int cnt = 0;
  std::cout << file << ": " << cnt << std::endl;
  ++cnt;
  return cnt;
}

template <int & i>
class A {
  static int j;
};

template <int & i>
int A<i>::j = showCountAndFile (SRC_FILE);

namespace
{
  int dummyGlobal;
}
template class A<dummyGlobal>;

#endif

기본 아이디어는 각 TU가 더미 글로벌에 대해 다른 고유 주소를 가지므로 템플릿은 각 TU에서 서로 다른 인스턴스화를 갖는다는 것입니다. 정적 멤버의 초기화는 "showCountAndFile"으로 호출하여 src_file (TU에서 설정)과 현재 값을 인쇄합니다. cnt 그러므로 순서를 보여줄 것입니다.

다음과 같이 사용합니다.

static const char * SRC_FILE=__FILE__;
#include "order.h"

int main ()
{
}

G ++는 이에 대한 도움을 제공합니다.
휴대용은 아니지만이 시점에서 당신의 주요 문제가 아닌 것이 확실합니다.

http://gcc.gnu.org/onlinedocs/gcc/c_002b_002b-attributes.html#c_002b_002b-attributes

실제로 싱글 톤을 사용하여 C ++에서 글로벌/정적 객체의 초기화 순서를 매우 효과적으로 제어 할 수 있습니다.

예를 들어, 당신은 다음과 같이 말합니다.

class Abc
{
public:
    void foo();
};

글로벌 범위에서 정의 된 해당 객체 :

Abc abc;

그런 다음 수업이 있습니다.

class Def
{
public:
    Def()
    {
        abc.foo();
    }
};

글로벌 범위에 정의 된 객체도 있습니다.

Def def;

이 상황에서는 초기화 순서를 제어하지 않으며 DEF가 먼저 초기화되면 아직 초기화되지 않은 ABC에서 foo () 메소드를 호출하기 때문에 프로그램이 중단 될 수 있습니다.

해결책은 글로벌 범위에서 기능을 수행하는 것입니다.

Abc& abc()
{
    static Abc a;
    return a;
}

그러면 DEF는 다음과 같은 것처럼 보입니다.

class Def
{
public:
    Def()
    {
        abc().foo();
    }
};

이런 식으로 ABC는 ABC () 함수의 첫 번째 호출 중에 발생하기 때문에 사용하기 전에 항상 초기화되도록 보장됩니다. 마찬가지로, DEF Global Object와 동일하게 수행하여 예상치 못한 초기화 종속성도 갖지 않도록해야합니다.

Def& def()
{
    static Def d;
    return d;
}

사용하기 전에 모든 것이 초기화되도록 단순히 초기화 순서를 엄격하게 제어 해야하는 경우 모든 글로벌 객체를 다음과 같이 글로벌 싱글 톤에 넣으십시오.

struct Global
{
    Abc abc;
    Def def;
};

Global& global()
{
    static Global g;
    return g;
}

다음과 같이 이러한 항목을 참조하십시오.

//..some code
global().abc.foo();
//..more code here
global().def.bar();

어떤 사람이 먼저 호출을받는 지에 관계없이 C ++ 멤버 초기화 규칙은 ABC 및 DEF 객체가 글로벌 클래스에서 정의 된 순서대로 초기화되도록 보장합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top