views:

519

answers:

5

What is the best way to accomplish the following in 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;
}

When I try to compile the above code, the compiler reports the following error:

"initializer element is not constant"

The bad line is this one:

struct A a = createA(42);

Can someone explain what is wrong? I'm not very experienced in C. Thanks!

+4  A: 

Why not use static initialization?

struct A a = { 42 };
BjoernD
Plus: You cannot call functions to statically initialize data. That's what your compiler error is trying to tell you.
BjoernD
That's fine, but what if I need to nest initializations? Like, say a struct B has an A in it. Can I statically initialize B with a statically initialized A?i.e. struct B b = createB(createA(42))
Scott
Ah, nevermind. I figured it out. Apparently I can just do:struct B b = {{42}};Nice!
Scott
+1  A: 

You can't call a function as an initiliazer. You need to call it inside main.

kgiannakakis
+1  A: 

The problem here is that global / file static variables in C must have a value known at compile time. This means you can't use a user defined function to initialize the value. It must be a constant expression

JaredPar
+2  A: 

You cannot invoke functions in static initialization like that. In your example, you can simply use:

struct A a = {42};

If you have a more complicated setup, you will need to provide a library construction and library destruction function that you force users of your library to call (assuming you want to be portable), or you will have to use C++ and take advantage of constructors/destructors, or you will have to take advantage of the non-standard and non-portable __attribute__((constructor)) to create a function that is run on startup to initialize it.

If you have more complicated setup, I would strongly advocate that you use C++:

class A
{
   A(){
      // can do initialization in the constructor
   }
   // ...
};

A a;

However, if you need to stick with pure C, the portable thing to do is use something like:

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
// ...

Basically, in the above, you would initialize your library with mylibrary_init and teardown your library using mylibrary_destroy. The functions using your library would require an initialized instance of mylibrary_t, and so the person who created the main function would be responsible for invoking mylibrary_init. It is also good to make the initialization function dependent on an "attributes" parameter that can be replaced with 0 or NULL as a default. That way, if you extend your library and need to accept configuration options, it is available to you. That's more a design than technical approach, though.

Michael Aaron Safyan
+3  A: 
struct A a = { .x = 42 };

More members:

struct Y {
    int r;
    int s;
    int t;
};

struct Y y = { .r = 1, .s = 2, .t = 3 };

You could also do

struct Y y = { 1, 2, 3 };

The same thing works for unions, and you don't have to include all of the members or even put them in the correct order.

nategoose