3

I understand that static variables are initialized only once during the lifetime of the program (see here for reference). Also, Static variables maintain their value between function invocations. How then, are static variables modified?

For example how is "a" modified in the following piece of code:

#include <stdbool.h>

void foo(){
    static int a;
    printf("%d\n",a);
    a++;
    printf("%d\n",a);
}
int main()
{
foo();
foo();
return 0;
}

Contrariwise, how are non-static variables modified? more specifically, how is "a" modified in the following peice of code?

 #include <stdbool.h>

void foo(){
    int a = 2;
    printf("%d\n",a);
    a++;
    printf("%d\n",a);
}
int main()
{
foo();
foo();
return 0;
}
Community
  • 1
  • 1
Fabulous
  • 735
  • 1
  • 10
  • 28
  • All variables can only be initialised once during their lifetime (i.e. when they come to lif). Not sure what your problem is. What do you mean with "updated"? They are simply written to. – too honest for this site Sep 07 '16 at 19:09
  • Both programs shown here have the same output. It might help to clarify your question by showing an example where the static variable is in a function other than `main()`. – Code-Apprentice Sep 07 '16 at 19:10
  • It makes more sense in a function other than `main` which may be called many times. The `static` local scope variable is *initialised* only once, but that is quite different from assigning another value as the code runs. The `static` qualifier just means that after the function ends, and the variable goes out of scope, it retains its value. – Weather Vane Sep 07 '16 at 19:11

4 Answers4

5

in your first example, the static variable has local scope, but has the same lifecycle as a global variable, and initialized once at program startup. You do that when you want to achieve a side effect (something is initialized, global function call counter...: calling the subroutine has an effect even if it returns nothing...)

It has the same address at every call (which means you could return its address and modify it from somewhere).

in your second example, you define an auto variable, allocated and initialized each time. It may have a different address depending on the call chain (in all calls, recursive, threaded, the variable is guaranteed to be unique, and not modifiable by other calls/threads: no edge effect, reetrant)

Won't make much difference on a main(), but if you put that in a subroutine, in the first case, 2 consecutive calls will yield a different result (2,3, then 3,4), whereas in the second case, 2 consecutive calls will yield the same result (2,3 twice)

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • The C standard does not require using a stack nor a heap. And typical implementations don't allocate statric variables on the heap, even **iff** they support one for dynamicc memory allocation. – too honest for this site Sep 07 '16 at 19:12
  • The edit is wrong, too. Of course static variables (like any other variable) can be access from a different subroutine. For statics, this is true even when the function has terminated. – too honest for this site Sep 07 '16 at 19:12
  • heap is wrong, you're right it's for malloc and such. Is that bss then? well, I have edited my question so it is more generic. after all, implementation is at the compiler discretion.. – Jean-François Fabre Sep 07 '16 at 19:15
  • This is a good answer, but I wouldn't say "heap" and allocated. I'd use [as your edit] "global memory" or "data segment" and "linked into". `static int x = 5` goes to `data` and [global scope] `int y;` goes to `bss` – Craig Estey Sep 07 '16 at 19:18
  • I have tried to account for all good remarks. is "global linkage" better? – Jean-François Fabre Sep 07 '16 at 19:21
  • No, all local variables have *no* linkage, regardless of their storage duration. Being able to return a pointer to the variable that remains valid has nothing to do with linkage, and everything to do with duration. – John Bollinger Sep 07 '16 at 19:24
  • 1
    The C standard does not have "global" linkage. Static variables can have internal, external or no linkage. Static variables in a function have none. Anyway, that is not a matter of linkage, but storage duration. And passing the address of a local variable is common practice. Actually the code in the question shows one typical usage. There is nothing weird about that. – too honest for this site Sep 07 '16 at 19:26
  • what is a linkage? – Fabulous Sep 07 '16 at 19:29
  • typical me, always try to get down to assembly code. We didn't need all that linkage/heap/stack information, just the correct way to use the vars. thanks. – Jean-François Fabre Sep 07 '16 at 19:34
  • @simrandhamija, linkage is, roughly, a characteristic describing what parts of a program can refer *directly* to a function or object. It is related to "scope", but the two are not the same thing. – John Bollinger Sep 07 '16 at 19:34
  • In an ELF executable [at least], when you specify a global `static int xxx = 23;` you get a symbol with a `LOCAL` binding. When you have a function scoped `static int yyy = 37;` you get a symbol with `LOCAL` binding and a uniquely generated name, such as `yyy.1836`. So, they are both visible in the symbol table of the executable as they both need to be relocated when the executable is loaded. – Craig Estey Sep 07 '16 at 19:37
  • I see. Since static variables maintain their value between function invocations, I was confused as to how they are "updated" or changed in any way. I also wanted to know how their "updation" is different from any non-static variable's "updation". – Fabulous Sep 07 '16 at 19:38
  • @Jean-Francois Fabre also, what is edge effect? when might it be used? I am editing my question so that it can illustrate your answer :) – Fabulous Sep 07 '16 at 19:41
  • 1
    damn my French betrayed me. I think it's called a side effect. Means that if you call the subroutine and ignore the results, the state of the program is different on subroutine exit. It can be a simple print to the console, write a file to disk, or modifying a global variable. – Jean-François Fabre Sep 07 '16 at 19:51
  • @Jean-Francois Fabre Thanks! edited my question so that it matches your answer :) – Fabulous Sep 07 '16 at 20:01
  • Mais, oui. L'expression idiomatique, c'est _side effect_. And, I won't embarrass myself further with my long forgotten high school French. The other idiom is "edge case", for something obscure/extreme that might not show up in most data, usually as a starting or ending condition. (e.g.) All prime numbers are odd, except for 2. Thus, 2 may be considered an "edge case" – Craig Estey Sep 07 '16 at 20:07
  • A French enthusiast :) we call that "effet de bord" (bord=edge) also used to describe what happens on the edges, and "generalized" (by people who don't know zit about computers) to describe a "side effect". But a side effect is not evil, it may be done on purpose and rightly! – Jean-François Fabre Sep 07 '16 at 20:17
  • Oui. A Paris, je suis allé voir la pièce d'Eugène Ionesco, «La Cantatrice chauve», en français, naturellement :-) A side effect is anything a function modifies/uses that is _not_ just its arguments and/or return value [or derived thereof]. That is, a function that is not _pure_. OP's first `foo` is _not_ pure, because `a` is `static`. OP's second `foo` _is_ pure. – Craig Estey Sep 07 '16 at 20:34
  • 1
    @simrandhamija: If you want it exactly, read the standard: http://port70.net/~nsz/c/c11/n1570.html#6.2.2 – too honest for this site Sep 07 '16 at 21:08
  • @CraigEstey: I don't see anything about that - or the requirement to use ELF - in the C standard. I think this is TMI for OP. – too honest for this site Sep 07 '16 at 21:11
  • @CraigEstey: Edge case is not the same as side-effect. A side-effect is something not along the straight program flow, an edge-case is when values are at the edge of range. Those are the most interesting cases when testing software, as they often generate unexpected (and unwanted) results. – too honest for this site Sep 07 '16 at 21:18
  • @Olaf ELF was a practical example, but works for a.out, COFF, etc as well because it _has_ to. It is how the compiler must generate the symbol(s) to support `void foo(void) { static int x = 72; } void bar(void) { static int x = 97; } in the same `.c` file. An `nm` would show these as type `d` (vs. `D`). Or, you could never have `static` anything because you couldn't link it/to it. – Craig Estey Sep 07 '16 at 21:33
  • @Olaf I had two comments about "side effect" and "edge case". In the first one I said: "The idiomatic expression is _side effect_". And, then [in the same comment], I said: "The _other_ idiom is 'edge case'". But, saying this doesn't imply that the two terms are synonymous [nor did I intend it that way]. If you look at both comments, I believe you'll see that I got both terms correct. In context [of a "pure" function], side effect is as I've described. – Craig Estey Sep 07 '16 at 21:42
  • @olaf thankyou for pointing out that subtle distinction between side-effect and edge-case. I should post this topic as a seperate question perhaps. – Fabulous Sep 07 '16 at 23:34
  • 1
    @simrandhamija: Please not on this site! They are common terms and not directly related to programming. – too honest for this site Sep 08 '16 at 01:34
4

A different example may make this a bit more clear.

Assume you have the following function:

void foo( void )
{
  int a = 1;
  printf( "&a = %p, a = %d\n", (void *) &a, a++ );
}

We create a variable a with an initial value of 1, print out its address and value, and then increment it. This variable is not visible outside the scope of the foo function.

The variable a has auto storage duration; its lifetime is limited to the lifetime of the foo function. A new instance of a will be created and initialized every time foo is entered, and that instance is destroyed every time foo exits1.

Now we add the static keyword to the declaration:

void foo( void )
{
  static int a = 1;
  printf( "&a = %p, a = %d\n", (void *) &a, a++ );
}

a now has static storage duration; a single instance of a is created and initialized once at program startup, and that instance is maintained until the program exits. a is still only visible by name within the scope of foo, but its lifetime extends beyond the lifetime of the foo function.

In both cases, the value of a is updated in exactly the same way; we update the contents of the memory location that a corresponds to. The only difference is how the contents of that memory location are maintained over the lifetime of the program.

To drive this home, take the following code:

#include <stdio.h>

void foo( void )
{
  int a = 1;
  printf( "&a = %p, a = %d\n", (void *) &a, a );
  a++;
}

void bar( void )
{
  int b = 2;
  foo();
  printf( "&b = %p, b = %d\n", (void *) &b, b );
  b++;
}

void bletch( void )
{
  int c = 3;
  bar();
  printf( "&c = %p, c = %d\n", (void *) &c, c );
  c++;
}

int main( void )
{
  foo();
  bar();
  bletch();
  bar();
  foo();
  return 0;
}

All of a, b, and c are declared auto (which is the default). When I build and run this code, I get the following output:

&a = 0x7fff701d234c, a = 1
&a = 0x7fff701d232c, a = 1
&b = 0x7fff701d234c, b = 2
&a = 0x7fff701d230c, a = 1
&b = 0x7fff701d232c, b = 2
&c = 0x7fff701d234c, c = 3
&a = 0x7fff701d232c, a = 1
&b = 0x7fff701d234c, b = 2
&a = 0x7fff701d234c, a = 1

New instances of a, b, and c are created and initialized when their respective functions are entered and destroyed when the functions exit. They get different addresses depending on where the functions are in the call chain2.

If I change that code such that a is declared

static int a = 1;

I get the following output:

&a = 0x500a58, a = 1
&a = 0x500a58, a = 2
&b = 0x7fffd1e75ccc, b = 2
&a = 0x500a58, a = 3
&b = 0x7fffd1e75cac, b = 2
&c = 0x7fffd1e75ccc, c = 3
&a = 0x500a58, a = 4
&b = 0x7fffd1e75ccc, b = 2
&a = 0x500a58, a = 5

So, a couple of things are immediately apparent - the address of a doesn't change with each call to foo, and the value of a is not re-initialized every call. Again, the instance for a is created and initialized once when the program starts up, and that instance persists beyond the lifetime of foo.

Declaring b as static gives us

&a = 0x500a58, a = 1
&a = 0x500a58, a = 2
&b = 0x500a5c, b = 2
&a = 0x500a58, a = 3
&b = 0x500a5c, b = 3
&c = 0x7fffc301f8cc, c = 3
&a = 0x500a58, a = 4
&b = 0x500a5c, b = 4
&a = 0x500a58, a = 5

Don't read too much into the address values themselves; that all depends on the platform. Obviously, on my platform, static items are stored in a very different location than auto items, but another platform might not show as obvious a difference.


  1. In practice, the space for auto variables is taken from the runtime stack, although that's an implementation detail, not a language requirement.
  2. Notice how the same addresses may be re-used for different objects depending on where the function is in the call chain.

John Bode
  • 119,563
  • 19
  • 122
  • 198
2

How is the static variable updated?

I suspect you are getting confused by the fact that a variable declaration that includes an initializer is syntactically similar to an assignment expression. The fact that a variable with static duration (or any other variable) is initialized at most once does not have anything to do with how you can modify that variable later. It is a statement about program semantics, not a restriction on your code.

Alternatively, you are perhaps confused about the difference between "initialization" and "assignment". These have specific, different meanings in this context. This contains an initialization of variable a, but no assignment to it:

int main() {
   int a = 2;  /* initialization */
   printf("%d",a);
}

This contains an assignment to variable a, but no initialization of it:

int main() {
   int a;  /* no initialization */
   a = 2;  /* assignment */
   printf("%d",a);
}

This contains both an initialization of a and an assignment to it:

int main() {
   int a = 2;
   printf("%d",a);
   a = 3;
   printf("%d",a);
}

You can update a static variable via any operator suitable for its type that has a side effect of modifying its operand (=, +=, ++, etc.), or in any number of indirect ways via pointer. For most purposes, code using a variable does not need to pay any attention to its storage duration. In particular, the two examples above are equally valid and produce the same output if variable a is made static. Don't take that to mean that static variables are without characteristic uses, but "What are static variables useful for?" is an entirely separate question.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • as per your answer, it is possible to assign a variable without initializing it (in your second piece of code you declare a and assign it a value.) What constitutes initialization then? – Fabulous Sep 07 '16 at 20:05
  • also, what do you mean by side effect? – Fabulous Sep 07 '16 at 20:08
  • @simrandhamija, Initialization is the process of giving a variable the value that it has *at the beginning* of its lifetime. From the perspective of C semantics, variables have their initial values as soon as they come into existence. In particular, all variables with static duration have their initial values by the time execution begins in `main()`. – John Bollinger Sep 07 '16 at 20:38
  • @simrandhamija, all variables with static storage duration are initialized. If you do not specifiy an explicit initializer, then they take an initial value specified by C. Automatic variables, on the other hand, (local variables not declared `static`) have an indeterminate initial value if you do not provide an explicit initializer. Not only *may* you assign a value to such variables, you *must* assign a value to such variables before you use their values. If you do not do so then undefined behavior results. – John Bollinger Sep 07 '16 at 20:41
  • 1
    @simrandhamija: Yes, you may assign to a variable without first initializing it. A `static` variable (declared with the `static` keyword or at file scope) is implicitly initialized to zero, depending on its type. An `auto` variable (declared within a function or block without the `static` keyword) without an initializer has an *indeterminate* value, which can include a *trap representation* (a bit pattern that doesn't correspond to a valid value for that type). Attempting to read an uninitialized `auto` variable leads to undefined behavior. – John Bode Sep 07 '16 at 20:45
  • 1
    @simrandhamija, every expression in C whose type is not `void` produces a value. Expressions may also have other effects on the execution environment, such as modifying the value of a variable -- all such effects other than computation of the value are "side effects". For example, evaluating the expression `a = 2` yields the value 2, and has the side effect of setting `a`'s value to 2. Evaluating the expression `puts("Hello world!")` produces a value indicative of the success or failure of the function in causing the *side effect* of sending the string to the standard output. – John Bollinger Sep 07 '16 at 20:47
0

The question aims to draw out how static variables are modified, given the fact that static variables maintain their value between function invocations. I also wanted to know how the above would be different from the modification of any non-static variable.

As per the answers posted, it turns out that all variables are modified either directly (via operands) or indirectly (via pointers).

However, the answers posted emphasize the difference in storage duration of static and non-static variables.

Firstly, it is important that one understands the difference between Initialization, Declaration and Assignment, as pointed out (quite consicely) by John Bollinger's answer. The answer points out that none of the italicized operations performed on any kind of variable affect it's storage duration.

Next, it is important one understands that a static variable has the same life-cycle as that of a global variable (regardless of whether the static variable has a local or global scope). This is pointed out by Jean-Francois Fabre's answer. Additionally, one might rule out the play of linkage since, as pointed out in the comments by @Olaf, a local variable has no linkage, whilst a static variable might have internal,external or no linkage. However, a static variable inside a function has no linkage.

Finally, one must realize how static variables behave, as pointed out brilliantly by John Bode's Answer

Community
  • 1
  • 1
Fabulous
  • 735
  • 1
  • 10
  • 28