【问题标题】:Initializing a Global Struct in C在 C 中初始化全局结构
【发布时间】:2011-02-01 02:10:36
【问题描述】:

在 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 方面不是很有经验。谢谢!

【问题讨论】:

    标签: c struct constants initialization


    【解决方案1】:

    对于同时使用 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),这样你就知道发生了什么。

    【讨论】:

    • 有 gcc 等价物吗?
    • @rkellerm 简单的回答:我不这么认为。硬回答:gcc 有不止一个 libcs​​。我认为最简单的查找方法是在 libc 的源代码存储库中查找“_start”。这是精灵中调用的第一个函数。通常包含此符号的文件称为 crt0something.c。例如 newlib 没有这样的初始化,它设置 argc、argv 并用于 main。但是添加一些代码来做到这一点并不难。不过,libc 的编译通常很头疼。
    • @rkellerm 好的,伙计。今天我确实需要这个特性,所以我在 glibc 2.4 中四处寻找。事实证明它比使用 MS 编译器更容易。如果您仍然想知道 - 只需将您的函数声明为 void __attribute__((constructor)) initializer_fn() 并且有您的 C premain 构造函数;)
    【解决方案2】:
    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 };
    

    同样的事情也适用于工会,您不必包括所有成员,甚至不必将它们按正确的顺序排列。

    【讨论】:

    • 如果ystruct Y y[2],如何做同样的事情。
    【解决方案3】:

    你不能像这样在静态初始化中调用函数。在您的示例中,您可以简单地使用:

    结构 A a = {42};

    如果您有一个更复杂的设置,您将需要提供一个库构建和库销毁函数,强制您的库的用户调用(假设您想要可移植),或者您将不得不使用 C++ 并采取构造函数/析构函数的优势,或者您将不得不利用非标准和不可移植的 __attribute__((constructor)) 创建一个在启动时运行的函数来初始化它。

    如果您有更复杂的设置,我强烈建议您使用 C++:

    A级 { 一种(){ // 可以在构造函数中进行初始化 } // ... }; 一个;

    但是,如果您需要坚持使用纯 C,则可移植的做法是使用类似以下内容:

    typedef void* mylibrary_attr_t; typedef void* mylibrary_t; #ifdef __cplusplus # 定义 EXTERNC extern "C" #别的 # 定义外部 #万一 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); // .. 库使用的各种属性的更多函数 外部无效 mylibrary_attr_destroy(mylibrary_attr_t*); EXTERNC int mylibrary_init(mylibrary_t*,mylibrary_attr_t); 外部无效 mylibrary_destroy(mylibrary_t*); // 使用 mylibrary_t 的函数 // ...

    基本上,在上面,您将使用mylibrary_init 初始化您的库并使用mylibrary_destroy 拆除您的库。使用你的库的函数需要mylibrary_t 的初始化实例,因此创建主函数的人将负责调用mylibrary_init。使初始化函数依赖于“属性”参数也很好,该参数可以默认替换为 0 或 NULL。这样,如果您扩展库并需要接受配置选项,您就可以使用它。不过,这更像是一种设计而非技术方法。

    【讨论】:

      【解决方案4】:

      这里的问题是C中的全局/文件静态变量必须有一个在编译时已知的值。这意味着您不能使用用户定义的函数来初始化该值。必须是常量表达式

      【讨论】:

        【解决方案5】:

        您不能将函数调用为初始化程序。您需要在 main 中调用它。

        【讨论】:

          【解决方案6】:

          为什么不使用静态初始化?

          struct A a = { 42 };
          

          【讨论】:

          • 另外:不能调用函数来静态初始化数据。这就是你的编译器错误试图告诉你的。
          • 这很好,但是如果我需要嵌套初始化怎么办?比如说,一个结构 B 里面有一个 A。我可以用静态初始化的 A 静态初始化 B 吗?即 struct B b = createB(createA(42))
          • 啊,没关系。我想到了。显然我可以这样做: struct B b = {{42}};不错!
          猜你喜欢
          • 2015-07-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-22
          • 1970-01-01
          • 2017-06-17
          相关资源
          最近更新 更多