1

I was disappointed to learn recently that C does not allow assignment of variables during static variable initialization, unlike C++. E.g. the following code compiles as C++...

#include <stdio.h>

int foo()
{
  return 1;
}

static int g_i = foo();

int main( int argc, char* argv[] )
{
  printf( "%d\n", g_i );
  return 0;
}

...but issues the following error with a C compiler:

>cc -g main.c
main.c:8:1: error: initializer element is not constant
 static int g_i = foo();
 ^

I thought I could be clever by using the comma operator a-la:

static int g_i = ( foo(), 1 );

...but the compiler seemed unimpressed with my attempted cleverness, and output effectively the same error:

>cc -c main.c
main.c:8:1: error: initializer element is not constant
 static int g_i = ( foo(), 1 );
 ^ 

:(

Q: Why does use of the comma operator not work? I may be unaware of some subtlety, but my understanding led me to think it should have worked: the C compiler is demanding g_i be initialzed to a compiletime constant; supposedly the comma operator would have offered me evaluation of the code left of the comma, but assignment of the code right of the comma, which is a compiletime constant.

Q: Are there any hacks - I don't care how dirty - that would allow assignment to g_i the return value of foo() to g_i?

This is a simplified representation of a C program where I really just want to call a function before main() - I don't care about the return value, but it's a more complicated problem to call a void function before main(), which I would rather sidestep altogether by using an int function whose value is assigned to a throwaway static int variable.

StoneThrow
  • 5,314
  • 4
  • 44
  • 86
  • 2
    Why do you want to sidestep calling a void function before main? Even though standard C has no way of doing that, most platforms and compilers actually do. – Siguza Jun 15 '17 at 01:53
  • 1
    Not sure if you would call this a "dirty hack", but write `static int g_i;`, and then in the first line of `main()`, put `g_i = foo();` – M.M Jun 15 '17 at 02:23
  • 1
    If code was `static int g_ai = foo();` in `a.c` and `static int g_bi = foo();` in `b.c`, would you care which one is called first? – chux - Reinstate Monica Jun 15 '17 at 03:22
  • @Siguza - if you have experience in that regard, could you post an answer at https://stackoverflow.com/questions/43150645/calling-void-function-before-main ? I found an answer to that question, which I posted, but it was quite complex in my opinion. If you know of something easier, I'd be eager to learn. – StoneThrow Jun 15 '17 at 17:11
  • @M.M - you wouldn't have known this from the simplified example I posted, but in the "real life" example I was trying to solve, I don't have access to `main()`. The real life code is part of a library, and I was trying to avoid having the user of the library explicitly call any kind of `init()` function. But thank you for the suggestion, I acknowledge it would have worked for the problem as stated. – StoneThrow Jun 15 '17 at 17:13
  • @chux - your question exposed something I hadn't considered. As noted in my response to @M.M the posted code is a simplification of a real-world problem. In the real-world problem, this code is actually part of a library (not executable). In real life I actually would only have wanted the library to call `foo()`, in which case, my function should have been `static int foo()`. That doesn't change the premise of the question, but it removes the possibility of `foo()` being called from more than one `.c` file. – StoneThrow Jun 15 '17 at 17:18
  • Curious: Is the goal to get `foo()` called before `main()` or to get a value into `int g_i` before being read? This may affect hack solution paths as the second can be done later. – chux - Reinstate Monica Jun 15 '17 at 17:25
  • @StoneThrow That question is tagged `[c++]` only... and I see you've discovered `__attribute__((constructor))` already, which is what I would have suggested for gcc and clang (there's a similar thing misleadingly called [TLS](https://stackoverflow.com/q/14538159) for Windows). You seem to have run into trouble with some C++ APIs there, but C interfaces should work just fine. – Siguza Jun 15 '17 at 18:03
  • @chux Actually both. Let me try explain: I'm working on a library (so no access to `main()`) with a bunch of functions. I need certain initialization to be done before any of those functions can be called. I _could_ have an `init()` function which sets `g_i` to `1`, then check the value of `g_i` inside each function, and return error if it is uninitialized. But I thought: _wouldn't it be nice if initialization could be automatic?_. So I thought to statically initialize `g_i` to the return value of `foo()`, so each library function is _guaranteed_ initialization has been performed. – StoneThrow Jun 15 '17 at 18:58
  • @StoneThrow Curious, was the "check the value of g_i inside each function" approach problematic or just inelegant? As I see it, it is easy for a `init()` call to fail, even with prior-`main()` calling. Such a failure still obliges each lib function to test the success of the initialization. Further, with a failure of `init()` magically called before `main()`, there is no feed-back possible at that time. At least with "check the value of g_i inside each function" approach, error can be reported. So `static int g_i` has 3 states: 0:uninitialized, -1:failed initialization: 1: success. Good luck – chux - Reinstate Monica Jun 15 '17 at 19:12
  • 1
    @chux Just inelegant, in my opinion, but by no means the end of the world. What you point out is true and on-point. In my case, though, if the `init()` function were to have failed, the system is likely hosed anyway (e.g. what to do if `pthread_mutex_init()` fails?). So I was relying on the "unlikelihood" of `init()` failing. This question was somewhere between real-world application and academic curiosity, because of my initially being caught off-guard by `C` demanding compiletime constants for static initialization. – StoneThrow Jun 15 '17 at 19:32

1 Answers1

8

C does not support dynamic initialization. For this reason, static objects are required to be initialized with constant expressions. Constant expressions are not allowed to involve any run-time computations, even if these computations do not affect the final value of the expression. Your expression with comma operator is not a constant expression.

(Moreover, comma operator is prohibited in C constant expressions even if you don't call any functions from them. E.g. even something as trivial as (1, 2, 3) is not a constant expression either.)

All static objects in C have to be initialized at compile time, at least conceptually. The word "conceptually" in this case refers to the fact that, say, address constant expressions may actually be evaluated much later, even at load time. But the point is that once your program starts running any user-level code, all static objects have to have already known pre-evaluated values, as if they were initialized at compile time. For this reason C (as opposed to C++) does not have/does not need the concept of initialization order for static objects and cannot possibly suffer from SIOF.

So, there's no way around this restriction in standard C. You will not be able to initialize a static object with something that requires (or in any way involves) running code at run-time. Your implementation might provide implementation-specific features that might be able to do something like that, but this is way outside the realm of C language itself.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765