最近我一直在研究一些嵌入式设备,我们有一些需要在编译时初始化的结构和联合,这样我们就可以将某些东西保存在不需要修改的闪存或ROM中,并且以一点性能成本节省一点闪存或SRAM。目前代码编译为有效的C99,但是如果没有这种调整,它也可以编译为C ++代码,并且支持以这种方式编译的东西也会很棒。阻止这种情况的关键之一是我们使用C99指定的初始值设定项,它们在C ++的C子集中不起作用。我不是一个C ++ buff,所以我想知道在C ++兼容的C或者C ++中仍然允许在编译时进行初始化以便结构和联合不需要的简单方法。在SRAM中启动程序后初始化。
另外需要注意的一点是:指定初始化程序使用的一个关键原因是初始化,而不是联盟的第一个成员。此外,坚持使用标准C ++或ANSI C是为了保持与其他编译器的兼容性(我知道GNU扩展提供类似指定的初始化器而没有C99)。
答案 0 :(得分:21)
我不确定你能用C ++做到这一点。对于需要使用指定的初始值设定项进行初始化的内容,可以将它们分别放在编译为C99的.c
文件中,例如:
// 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
答案 1 :(得分:17)
基于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
答案 2 :(得分:2)
这既是答案,也是问题。我意识到这个线程已经死了,但这正是我今晚所看到的。
我做了一些探讨和最接近的事情,我可以得到我想要的东西(这与你想要的类似...我一直在使用图片,不需要使用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
这两个都是使用g++ -O0 -S main.cpp
命令生成的。显然,直观上效率较低的示例在指令数量方面产生了更高效的操作码。另一方面,在很少的情况下,我可以想象一些指令是关键的。 (另一方面,我真的很难理解不是人类写的集会,所以也许我错过了一些东西......)我认为这提供了一个解决方案,尽管已经很晚了,詹姆斯问的问题。我应该测试的下一件事是在C99中是否允许相同的初始化;如果有效,我认为它完全解决了詹姆斯的问题。
免责声明:我不知道这对g ++以外的任何其他编译器是否有效或行为相似。
答案 3 :(得分:1)
#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也可以有构造函数。可能这是你想要的吗?
答案 4 :(得分: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 ++初始值设定项的唯一内容是您需要初始化结构的所有元素,否则其余元素将使用零进行初始化。你不能挑选。但对于您的用例,这应该仍然可以。
另外需要注意的一点是:指定初始化程序使用的一个关键原因是初始化,而不是工会的第一个成员。
为此,您需要使用示例中显示的“替代方法”,其中我通过提供等效的int值来设置“float”成员。这有点像黑客,但如果能解决你的问题。
答案 5 :(得分: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)
...看起来生成的代码在main之前在运行时进行初始化。即使它有效,它也很难像C99一样可以编译,并且可以为你想要初始化的每个对象获得一个类;所以,不要打扰。