문제

최근에 저는 수정될 필요가 없는 특정 항목을 플래시나 ROM에 유지하고 약간의 비용을 절약할 수 있도록 컴파일 타임에 초기화해야 하는 일부 구조체와 공용체가 있는 일부 임베디드 장치에 대해 작업해 왔습니다. 약간의 성능 비용으로 플래시 또는 SRAM을 사용합니다.현재 코드는 유효한 C99로 컴파일되지만 이 조정 없이는 C++ 코드로도 컴파일되었으므로 이러한 방식으로 컴파일되는 것도 지원하면 좋을 것입니다.이를 방지하는 주요 사항 중 하나는 C++의 C 하위 집합 내에서 작동하지 않는 C99 지정 초기화 프로그램을 사용하고 있다는 것입니다.저는 C++ 팬이 아니기 때문에 C++ 호환 C 또는 C++에서 구조체와 공용체를 필요로 하지 않도록 컴파일 타임에 초기화를 허용하는 간단한 방법이 무엇인지 궁금합니다. SRAM에서 프로그램 시작 후 초기화됩니다.

한 가지 추가 참고 사항:지정된 이니셜라이저를 사용하는 주요 이유는 공용체의 첫 번째 멤버가 아닌 것으로 초기화하기 때문입니다.또한, 표준 C++ 또는 ANSI C를 고수하는 것은 다른 컴파일러와의 호환성을 유지하기 위한 장점입니다(C99 없이 지정된 초기화 프로그램과 같은 것을 제공하는 GNU 확장에 대해 알고 있습니다).

도움이 되었습니까?

해결책

C ++에서 할 수 있는지 잘 모르겠습니다. 지정된 초기화기를 사용하여 초기화 해야하는 것들에 대해서는이를 별도로 넣을 수 있습니다. .c C99로 컴파일 된 파일 : 예를 들어 :

// In common header file
typedef union my_union
{
    int i;
    float f;
} my_union;

extern const my_union g_var;

// In file compiled as C99
const my_union g_var = { .f = 3.14159f };

// Now any file that #include's the header can access g_var, and it will be
// properly initialized at load time

다른 팁

C ++ 11은 Shing Yip의 답변과 3 년의 혜택을 바탕으로 Compile Time 초기화를 보장 할 수 있습니다.

union Bar
{
    constexpr Bar(int a) : a_(a) {}
    constexpr Bar(float b) : b_(b) {}
    int a_;
    float b_;
};

extern constexpr Bar bar1(1);
extern constexpr Bar bar2(1.234f);

집회:

    .globl  _bar1                   ## @bar1
    .p2align    2
_bar1:
    .long   1                       ## 0x1

    .globl  _bar2                   ## @bar2
    .p2align    2
_bar2:
    .long   1067316150              ## float 1.23399997
#ifdef __cplusplus
struct Foo
{
    Foo(int a, int b) : a(a), b(b) {}
    int a;
    int b;
};

union Bar
{
    Bar(int a) : a(a) {}
    Bar(float b) : b(b) {}
    int a;
    float b;
};

static Foo foo(1,2);
static Bar bar1(1);
static Bar bar2(1.234f);
#else 
 /* C99 stuff */
#endif // __cplusplus

C ++ Union에서는 생성자도 가질 수 있습니다. 이것이 당신이 원하는 것일 수 있습니까?

이것은 일종의 답과 질문입니다. 나는이 실이 죽었다는 것을 알고 있지만, 그것은 내가 오늘 밤에보고 있던 것과 정확히 일치한다.

나는 약간의 찌르기와 내가 원하는 것에 얻을 수있는 가장 가까운 일을했다 (이것은 당신이 원하는 것과 비슷하다 ... 나는 사진으로 일하고 있으며 C ++를 사용할 필요가 없지만 어떻게 될지 궁금하다. )는 첫 번째 코드 예입니다.

#include <iostream>

using namespace std;

extern "C" 
{
    typedef struct stuff
    {
        int x;
        double y;
    } things;
}

int main()
{
    things jmcd = { jmcd.x = 12, jmcd.y = 10.1234 };
    cout << jmcd.x << " " << jmcd.y << endl;
    return 0;
}

이것은 나중에 언급 할 경고가있는 C99 스타일 지정 초기화기와 매우 유사한 모양을 가지고 있습니다. (구조물을 구성하기를 원한다면 #ifdef __cplusplus로 이것을 랩핑 할 것입니다.) 내가 본 코드의 두 번째 버전은 다음과 같습니다.

#include <iostream>

using namespace std;

extern "C" 
{
    typedef struct stuff
    {
        int x;
        double y;
    } things;
}


int main()
{
    things jmcd;
    jmcd.x = 12;
    jmcd.y = 10.1234;
    cout << jmcd.x << " " << jmcd.y << endl;
    return 0;
}

기본적으로 분해를 살펴보면 첫 번째 예제가 실제로 느리게 나타납니다. 나는 어셈블리 출력을 보았고, 나는 약간 녹슬었을 것이다. 어쩌면 누군가 나에게 통찰력을 줄 수있을 것입니다. 첫 번째 CPP의 어셈블리 출력은 컴파일되었고 다음과 같습니다.

main:
.LFB957:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    pushl   %ebp
    .cfi_def_cfa_offset 8
    movl    %esp, %ebp
    .cfi_offset 5, -8
    .cfi_def_cfa_register 5
    subl    $24, %esp
    movl    $0, 12(%esp)
    movl    $0, 16(%esp)
    movl    $0, 20(%esp)
    movl    $12, 12(%esp)
    movl    12(%esp), %eax
    movl    %eax, 12(%esp)
    fldl    .LC0
    fstpl   16(%esp)
    fldl    16(%esp)
    fstpl   16(%esp)
    movl    12(%esp), %eax
    movl    %eax, 4(%esp)
    fildl   4(%esp)
    fldl    16(%esp)
    faddp   %st, %st(1)
    fnstcw  2(%esp)
    movzwl  2(%esp), %eax
    movb    $12, %ah
    movw    %ax, (%esp)
    fldcw   (%esp)
    fistpl  4(%esp)
    fldcw   2(%esp)
    movl    4(%esp), %eax
    leave
    ret
    .cfi_endproc

두 번째 예는 다음과 같습니다.

main:
.LFB957:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    pushl   %ebp
    .cfi_def_cfa_offset 8
    movl    %esp, %ebp
    .cfi_offset 5, -8
    .cfi_def_cfa_register 5
    subl    $24, %esp
    movl    $12, 12(%esp)
    fldl    .LC0
    fstpl   16(%esp)
    movl    12(%esp), %eax
    movl    %eax, 4(%esp)
    fildl   4(%esp)
    fldl    16(%esp)
    faddp   %st, %st(1)
    fnstcw  2(%esp)
    movzwl  2(%esp), %eax
    movb    $12, %ah
    movw    %ax, (%esp)
    fldcw   (%esp)
    fistpl  4(%esp)
    fldcw   2(%esp)
    movl    4(%esp), %eax
    leave
    ret
    .cfi_endproc

이 두 가지 모두 a로 생성되었습니다 g++ -O0 -S main.cpp 명령. 분명히, 직관적으로 덜 효율적인 예는 지침의 측면에서보다 효율적인 opcode를 생성했습니다. 반면에, 몇 가지 지침이 중요하다고 상상할 수있는 사례는 거의 없습니다. (다른 한편으로, 나는 인간이 작성하지 않은 조립을 이해하는 데 어려움을 겪고 있으므로 아마도 무언가를 놓치고있을 것입니다 ...) 이것은 늦게 제임스가 물었던 질문에 대한 해결책을 제공한다고 생각합니다. 다음으로 테스트해야 할 것은 C99에서 동일한 초기화가 허용되는지 여부입니다. 그것이 효과가 있다면, 나는 그것이 James의 문제를 완전히 다루고 있다고 생각합니다.

면책 조항 : 이것이 G ++ 이외의 다른 컴파일러와 유사하게 작동하는지 전혀 모릅니다.

드라이 홀 보고서 :

주어진

struct S {
  int mA;
  int mB;
  S() {}
  S(int b} : mB(b) {} // a ctor that does partial initialization
};

S1의 인라인 기본 생성자가 S (int)를 호출하고 하드 코딩 된 값을 전달하는 S에서 S1을 파생하려고 시도했습니다.

struct S1 {
  S1() : S(22) {}
} s1;

... 그런 다음 GCC 4.0.1 -o2 -s로 컴파일했습니다. 최적화는 S1.MB가 반드시 22 일이 될 것이며 컴파일 타임에 값을 할당하지만 어셈블러에서 ...

    movl    $22, 4+_s1-"L00000000002$pb"(%ebx)

... 생성 된 코드가 메인 이전에 런타임에 초기화를 수행하는 것처럼 보입니다. 그것이 효과가 있었음에도 불구하고, 그것은 C99로 편집 할 수 없으며 초기화하려는 각 객체에 대해 클래스를 도출하는 Kludge가있을 것입니다. 그러니 귀찮게하지 마십시오.

다음 코드는 g++에서 문제 없이 컴파일됩니다.

#include <iostream>

struct foo
{
  int a;
  int b;
  int c;
};

union bar
{
  int a;
  float b;
  long c;
};

static foo s_foo1 = {1,2,3};
static foo s_foo2 = {1,2};
static bar s_bar1 = {42L};
static bar s_bar2 = {1078523331}; // 3.14 in float


int main(int, char**)
{
  std::cout << s_foo1.a << ", " <<
               s_foo1.b << ", " <<
               s_foo1.c << std::endl;

  std::cout << s_foo2.a << ", " <<
               s_foo2.b << ", " <<
               s_foo2.c << std::endl;

  std::cout << s_bar1.a << ", " <<
               s_bar1.b << ", " <<
               s_bar1.c << std::endl;

  std::cout << s_bar2.a << ", " <<
               s_bar2.b << ", " <<
               s_bar2.c << std::endl;

  return 0;
}

결과는 다음과 같습니다.

$ g++ -o ./test ./test.cpp
$ ./test
1, 2, 3
1, 2, 0
42, 5.88545e-44, 42
1078523331, 3.14, 1078523331

C++ 이니셜라이저의 유일한 점은 구조체의 모든 요소를 ​​초기화해야 한다는 것입니다. 그렇지 않으면 나머지는 0으로 초기화됩니다.골라서 선택할 수는 없습니다.하지만 귀하의 사용 사례에서는 여전히 괜찮습니다.

한 가지 추가 참고 사항:지정된 이니셜라이저를 사용하는 주요 이유는 공용체의 첫 번째 멤버가 아닌 것으로 초기화하기 때문입니다.

이를 위해서는 동등한 int 값을 제공하여 "float" 멤버를 설정한 예제에 표시된 "해결 방법"을 사용해야 합니다.약간의 해킹이지만 문제가 해결된다면.

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