在C中完成以下操作的最佳方法是什么?
#include <stdio.h>
struct A
{
int x;
};
struct A createA(int x)
{
struct A a;
a.x = x;
return a;
}
struct A a = createA(42);
int main(int argc, char** argv)
{
printf("%d\n", a.x);
return 0;
}
当我尝试编译上面的代码时,编译器会报告以下错误:
“初始化元素不是常数”
坏线是这一行:
struct A a = createA(42);
有人可以解释什么是错的吗?我不是很有经验C.谢谢!
答案 0 :(得分:16)
struct A a = { .x = 42 };
更多成员:
struct Y {
int r;
int s;
int t;
};
struct Y y = { .r = 1, .s = 2, .t = 3 };
您也可以
struct Y y = { 1, 2, 3 };
同样适用于工会,您不必包含所有成员,甚至不必按正确顺序排列。
答案 1 :(得分:14)
为什么不使用静态初始化?
struct A a = { 42 };
答案 2 :(得分:4)
这里的问题是C中的全局/文件静态变量必须在编译时具有已知值。这意味着您无法使用用户定义的函数来初始化该值。它必须是一个常量表达式
答案 3 :(得分:2)
您不能像这样在静态初始化中调用函数。在您的示例中,您只需使用:
struct A a = {42};
如果你有一个更复杂的设置,你需要提供一个库构造和库销毁功能,你强迫你的库的用户调用(假设你想要可移植),或者你将不得不使用C ++并采取构造函数/析构函数的优点,或者你必须利用非标准和非可移植的__attribute __((构造函数))来创建一个在启动时运行的函数来初始化它。
如果您的设置更复杂,我强烈建议您使用C ++:
class A { A(){ // can do initialization in the constructor } // ... }; A a;
但是,如果您需要坚持使用纯C,那么可移植的事情就是使用:
typedef void* mylibrary_attr_t; typedef void* mylibrary_t; #ifdef __cplusplus # define EXTERNC extern "C" #else # define EXTERNC #endif EXTERNC int mylibrary_attr_init(mylibrary_attr_t*); EXTERNC int mylibrary_attr_setparam1(mylibrary_attr_t,int); EXTERNC int mylibrary_attr_setparam2(mylibrary_attr_t,double); // .. more functions for various attributes used by library EXTERNC void mylibrary_attr_destroy(mylibrary_attr_t*); EXTERNC int mylibrary_init(mylibrary_t*,mylibrary_attr_t); EXTERNC void mylibrary_destroy(mylibrary_t*); // functions that use mylibrary_t // ...
基本上,在上文中,您将使用mylibrary_init
初始化您的库,并使用mylibrary_destroy
拆分您的库。使用库的函数需要初始化的mylibrary_t
实例,因此创建main函数的人员将负责调用mylibrary_init
。使初始化函数依赖于“attributes”参数也是很好的,该参数可以替换为0或NULL作为默认值。这样,如果您扩展库并需要接受配置选项,则可以使用它。不过,这更像是一种设计而非技术方法。
答案 4 :(得分:2)
对于同样使用MSVC的好奇人士:
在C语言中,可以在main之前运行初始化函数,就像在C ++中一样(当然,如果在C语言中不可能,C ++会怎么做),但是如果你可能有点混乱还没有看到你的运行时库是如何工作的。
长话短说:
#pragma section(".CRT$XIU",long,read)
int
init_func ()
{
// initialization
return 0; // return 0 is mandatory
}
__declspec(allocate(".CRT$XIU"))
int (*global_initializer)() = init_func;
所以它不像C ++那样紧凑的源文本,但它可以做到。此外,在使用之前,我建议首先了解PE格式,然后在MSVC安装目录中读取crt \ src \ crt0.c和crt \ src \ crt0dat.c(在两个文件中搜索_cinit),以便了解发生了什么。
答案 5 :(得分:1)
您无法将函数称为初始值设定项。你需要在main中调用它。