28

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!

5 Answers 5

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

Sign up to request clarification or add additional context in comments.

1 Comment

How to do the same if y is struct Y y[2].
21

Why not use static initialization?

struct A a = { 42 };

3 Comments

Plus: You cannot call functions to statically initialize data. That's what your compiler error is trying to tell you.
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))
Ah, nevermind. I figured it out. Apparently I can just do: struct B b = {{42}}; Nice!
6

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

3 Comments

Why global variables must have a known value at compile time?
@MehdiCharife variables in global scope gets initialized before main() gets invoked so you can not initialize them with runtime variables.
But how does the fact that you can't initialize them with runtime variables imply that they must have a known value at compile time?
3

For curious people who also use MSVC:

In C it is possible to run initialization functions before main just as it is possible in C++ (of course it is, how would C++ do it if it wasn't possible in C), however it may be somewhat confusing if you haven't read how does your runtime library work.

Long story short:

#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;

So it's not as compact source text as in C++, but it can be done. Also, before using I recommend to understand PE format first, then read crt\src\crt0.c and crt\src\crt0dat.c (search for _cinit in both files) in your MSVC installation directory so you know what is going on.

5 Comments

Is there a gcc equivalent?
@rkellerm easy answer: I don't think so. hard answer: there are more than one libcs for gcc. I think the easiest way to find out would be looking for "_start" in source code repository of your libc. That is the first function called in the elf. Usually the file containing this symbol is called crt0something.c. newlib for instance has no such initialization, it sets argc, argv and goes for main. But it wouldn't be as hard to add some code to do that. The compilation of libc is usually quite a headache though.
@rkellerm ok bud. Today I actually needed this feature so I sniffed around in glibc 2.4. Turns out it is even easier than with MS compiler. In case you still want to know - just declare your function as void __attribute__((constructor)) initializer_fn() and there is your C premain constructor ;)
Hi @Pyjong. Can you please explain what your code does and why does it work?
Hi @MehdiCharife, the first line creates new program section called .CRT$XIU in the resulting object file or binary. init_func is just a normal function, nothing special there. The last two lines create a function pointer initialized to point to init_func(), which will be stored in the previously created .CRT$XIU section. Libc library is by default added to every C program. When you run your program, it does NOT start in main(). It starts in the libc library. Libc library looks into .CRT section and runs all pointers it finds there, then it calls main().
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.