37

最近、いくつかの組み込みデバイスに取り組んでいます。ここでは、コンパイル時に初期化する必要のある構造体と共用体がいくつかあります。これにより、変更する必要のない特定のものをフラッシュまたは ROM に保持し、少し節約できます。フラッシュまたは SRAM に少しパフォーマンスを犠牲にします。現在、コードは有効な C99 としてコンパイルされますが、この調整がなければ C++ コードとしてもコンパイルされていました。これを防ぐ重要な要因の 1 つは、C++ の C サブセット内では機能しない C99 指定の初期化子を使用していることです。私は C++ にあまり詳しくないので、C++ と互換性のある C でこれを実現する簡単な方法を知りたいと思っています。

もう 1 つの注意点: 指定イニシャライザを使用する主な理由は、共用体の最初のメンバーではなく初期化することです。また、標準の C++ または ANSI C に固執することは、他のコンパイラとの互換性を維持するためにプラスになります (C99 を使用せずに指定された初期化子のようなものを提供する GNU 拡張について知っています)。

4

6 に答える 6

23

C ++でできるかどうかはわかりません。指定された初期化子を使用して初期化する必要があるものについては、.cC99 としてコンパイルされたファイルに個別に配置できます。

// 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
于 2009-05-13T04:10:23.687 に答える
18

Shing Yip の回答に基づいて構築され、3 年の時間の恩恵を受けて、C++11 はコンパイル時の初期化を保証できるようになりました。

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
于 2012-07-21T13:43:21.933 に答える
2
#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 ++では、共用体にコンストラクターを含めることもできます。これはあなたが望んでいたものかもしれませんか?

于 2009-05-13T05:20:11.670 に答える
2

これは一種の答えであり、質問でもあります。このスレッドが死んでいることに気づきましたが、まさに今夜私が調べていたものです。

私はいくつかの突っ込みを行い、私が望むものに最も近いものを手に入れることができました(これはあなたが望むものに似ています... ) は最初のコード例です。

#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 でラップするでしょう。) 私が調べたコードの 2 番目のバージョンは次のとおりです。

#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

2 番目の例は次のようになります。

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

これらは両方ともg++ -O0 -S main.cppコマンドで生成されました。明らかに、直感的に非効率な例は、命令数に関してより効率的なオペコードを生成しました。一方で、いくつかの命令が重要であると想像できるケースはほとんどありません。(一方で、私は人間によって書かれたものではないアセンブリを理解するのに本当に苦労しているので、何かが欠けているのかもしれません. 次にテストする必要があるのは、C99 で同じ初期化が許可されているかどうかです。それが機能する場合、James の問題を完全に解決すると思います。

免責事項: g++ 以外の他のコンパイラでこれが機能するか、同様に動作するかどうかはわかりません。

于 2011-07-12T05:45:53.443 に答える
0

次のコードは、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++ 初期化子に関する唯一のことは、構造体のすべての要素を初期化する必要があるか、残りがゼロで初期化されることです。選んで選ぶことはできません。しかし、それはあなたのユースケースではまだ問題ないはずです.

もう 1 つの注意点: 指定された初期化子を使用する主な理由は、共用体の最初のメンバーではなく初期化することです。

そのためには、同等の int 値を指定して「float」メンバーを設定する例に示されている「回避策」を使用する必要があります。ちょっとしたハックですが、問題が解決する場合。

于 2009-05-13T04:25:13.303 に答える
0

ドライホールレポート:

与えられた

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

S から S1 を派生させようとしました。ここで、S1 のインライン デフォルト コンストラクターは S(int) を呼び出し、ハードコードされた値を渡します ...

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

...そしてgcc 4.0.1 -O2 -Sでコンパイルします。希望は、オプティマイザーが s1.mB が必然的に 22 であることを認識し、コンパイル時に値を割り当てることでしたが、アセンブラーから...

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

...生成されたコードは、メインの前に実行時に初期化を行うようです。機能したとしても、C99 としてコンパイルすることはほとんどできず、初期化するオブジェクトごとにクラスを派生させる手間がかかります。だから、気にしないでください。

于 2009-05-13T04:28:28.930 に答える