5

After stumbling onto this question and reading a little more here (c++ but this issue works the same in C/C++ AFAIN) I saw no mention to what is realy happening inside the function.

void f(){
  static int c = 0;
  printf("%d\n",c++);
}

int main(){
  int  i = 10;
  while(i--)
    f();
  return 0;
}

In this snippet, c lifetime is the entire execution of the program, so the line static int c = 0; has no meaning in the next calls to f() since c is already a defined (static) variable, and the assignment part is also obsolete (in the next calls to f()), since it only takes place at the first time.

So, what does the compiler do? does it split f into 2 functions - f_init, f_the_real_thing where f_init initializes and f_the_real_thing prints, and calls 1 time f_init and from that onward, only calls f_the_real_thing?

5
  • 2
    "In this snippet, c lifetime starts when f() is first called from main" - Not for a C static, no. Commented Jul 25, 2017 at 11:02
  • Simply treats the variable like one of a global scope, reserving initialized data in the .data section, while keeping the linkage static to the module. Commented Jul 25, 2017 at 11:06
  • @StoryTeller I was mislead by the C++ answer. I'll edit, thanks Commented Jul 25, 2017 at 11:19
  • "(c++ but this issue works the same in C/C++ AFAIN)" That's not really true. First because there is no such thing as "C/C++". Second because in C++ cou can initialize a static variable with the return value of a function. In C this is not possible. Therefore please don't mix up languages. Commented Jul 25, 2017 at 12:20
  • the program loader allocates the memory and initializes it. Thereafter, the execution of the program reads/writes the variable in memory. Commented Jul 25, 2017 at 22:40

3 Answers 3

8

The first assignment is not "obsolete" - it ensures c is zero the first time f() is called. Admittedly that is the default for statics: if no initialiser is specified, it will be initialised to zero. But a static int c = 42 will ensure c has the value 42 the first time the function is called, and the sequence of values will continue from there.

The static keyword means that the variable has static storage duration. It is only initialised once (so will have that value the first time the function is called) but changes then persist - any time the value is retrieved, the value retrieved will be the last stored in the variable.

All the compiler does is place the variable c into an area of memory that will exist - and hold whatever value it was last set to - for as long as the program is running. The specifics of how that is achieved depends on the compiler.

However, I have never seen a compiler that splits the logic of the function into multiple parts to accommodate the static.

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

2 Comments

" Admittedly that is the default for statics: if no initialiser is specified, it will be initialised to zero". Plus one for especially that.
I didn't mean the assignment is obsolete entirely - only after the first call to f, but I guess I could have said that in a more coherent way
6

Although the standard does not dictate how compilers must implement behavior, most compilers do a much less sophisticated thing: they place c into static memory segment, and tell the loader to place zero into c's address. This way f comes straight to pre-initialized c, and proceeds to printing and incrementing as if the declaration line where not there.

In C++ it optionally adds code to initialize c to static initialization function, which initializes all static variables. In this case, no call is required.

In essence, this amounts to c starting its lifetime before the first call to f. You can think of c's behavior as if it were a static variable outside f() with its visibility constrained to f()'s scope.

1 Comment

Note that the C++ case would be different for objects with dynamic initialization
4

The C standard doesn't specify how the required behaviour for static storage duration must be implemented.

If you're curious about how your particular implementation handles this, then you can always check the generated assembly.

(Note that in your particular case, your code is vulnerable to concurrency issues centred around c++ not necessarily being atomic; also its vulnerability to int overflow, although i-- does act as an adequate termination condition.)

3 Comments

C++ not being atomic?
Yes, it was a nice pun on the part of the OP.
@CIsForCookies: Indeed it is, and therefore not relevant here, yet.

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.