tags:

views:

199

answers:

7

Hi I'm studying for my test in C and i've encountered a question which i can't figure its answer.

A programmer wrote a program to count number of users (Count.h, Count.c):

/******** FILE: Counter.h ***********/
static int counter = 0;
int getUsersNum ();
/******** END OF FILE: Counter.h ****/

/******** FILE: Counter.c ***********/
#include "Counter.h"
int getUsersNum ()
{
    return counter;
}
/******** END OF FILE: Counter.c ****/

And a tester to test it:

/******** FILE: CounterMain.c ***********/
#include "Counter.h"
#include <stdio.h>
int main ()
{
    int i;
    for (i=0;i<5;++i)
    {
        ++counter;
        printf ("Users num:  %d\n", getUsersNum());
    }
    return 0;
}
/******** END OF FILE: CounterMain.c ****/

Suprisingly the output was:

Users num: 0
Users num: 0
Users num: 0
Users num: 0
Users num: 0

I can't see why with this use of static variable the counter does not advance.. why did they get such input?

thank you all!

+7  A: 

In C, the scope of a static variable is the source file in which its defined.

Since you're loading this header into 2 separate .c files, each file gets a unique variable. Incrementing "counter" in one file does not affect the "static" variable in the other file.

For details, see this description. In order for the variable to be visible and shared in multiple files, it needs to be declared as extern. Otherwise:

Static global variables: variables declared as static at the top level of a source file (outside any function definitions) are visible throughout that file ("file scope").

Which is the case here.

Reed Copsey
"Source file" is confusing because a header file is a source file and the variable is only declared in one source file. _Translation unit_ would be more accurate.
Charles Bailey
`In C, the scope of a static variable is the source file in which its defined`. You might want to replace `source file` with `translation unit`.
Prasoon Saurav
@Charles : Too quick! I was posting exactly the same thing. :D
Prasoon Saurav
@Prasoon and @Charles: That was a direct quotation, not my text. That was why I specifically mentioned ".c files" in my answer..
Reed Copsey
@Prasoon: While "translation unit" is correct, it's not going to be as obvious to the original poster...
Reed Copsey
@Reed: Valid point. Agreed!
Prasoon Saurav
It may not be obvious and it may need explaining but I think that it's necessary to use the concept of a translation unit to explain the behaviour whether or not you use the correct technical term. Using the term "source file" loosely is likely to be more confusing IMHO.
Charles Bailey
+1  A: 

A static variable is only accessible in the file in which it's defined. In this example, Counter.c and CounterMain.c both have their own counter variable.

When ++counter is executed, this updates the variable declared in CounterMain.c. But when getUsersNum() is called, this returns the value of the counter variable from Counter.c, which hasn't been incremented.

If you change getUsersNum() to counter, you'll see the counter variable declared in CounterMain.c has been incremented.

Richard Fearn
+2  A: 

The static keyword means, when used to qualify functions or global variables, that the function or variable should have internal linkage, meaning it shouldn't be visible as a global symbol. Therefore, each compilation unit including Counter.h will have its own local copy of counter which won't clash with others.

In this case, Counter.c and CounterMain.c have different variables count, resulting in what you have described.

The solution is to change the definition of counter in Counter.h to a declaration:
extern int counter;
and to put the definition in Counter.c:
int counter = 0;

Afterwards CounterMain and any other compilation units including Counter.h should be able to access the single instance counter, but you may want to indulge in information hiding and only access it through functions in Counter, resulting in a cleaner interface.

aib
How is `static` different for functions and variabls?
Charles Bailey
@Charles Bailey It's different once you delve down into the generated code, but if you look only at the C level, it's semantically pretty much the same.
zneak
It's not for functions and global variables: It changes the linkage as I've explained (I've edited my answer to read "variable or function"). With simple local variables, it means "static storage and lifetime", i.e. a plain old static variable.
aib
You mean it has a different meaning when applied to objects with block scope as opposed to when applied to functions and objects with file scope. Your first sentence is ambiguous as you don't say what context you are comparing against when saying "has a different meaning".
Charles Bailey
Ah, I see what you mean now. Fixed.
aib
Actually I was planning on just linking to another question which explained the different usages of `static`, but just couldn't find a good one.
aib
I'm going to sound pedantic now and it's not important because it's not central to the original question but saying that `static` applied to local variables makes them `static` is circular and doesn't really explain anything.
Charles Bailey
How did I know you'd say that? :) Removed all references to the other usage.
aib
+3  A: 

Just think of it as ".h files don't exist". What happens is that the .h files are included inside the .c files and only the .c files get compiled (and linked (mixed) together).

In your case you have 2 .c files with

static int counter = 0;

Each counter is specific to the .c file it is in. The counter in CounterMain.c is a different variable than the counter in Counter.c.

You need to have one single definition of counter. You can have several declarations (typically in .h files)

/* .h file */
extern int counter;

/* .c file(s) that use the counter but don't define it */
#include "file.h"

/* .c file that **defines** counter */
#include "file.h"
int counter = 0;

Ohhhhhhhhhhhhhhhhhh and there's the static thing. Don't use it at global scope!

pmg
+1 for posting a simple explaination and solution instead of going through lots of accurate, but gory details.
André Caron
+1  A: 

It's because the static variable is declared in the header. In C, static variables exist only in the .c file in which they're declared. Since your .h is included (#include directives can be seen as nothing more than a copy-paste operation) in two distinct .c files, two static variables named counter are created, one in each file. Your test file increments its local counter variable, but the one returned by getUsersNum is from another C file and is completely independent.

What the question tries to do is access a static variable from a file other than the one in which it was declared. You should know that it's not (directly) possible. To increment the right counter, you will need a function that operates on Counter.c's counter variable.

zneak
+6  A: 

static int counter = 0;

If a variable has been defined using the static storage class specifier the variable has an internal linkage. That means you can use counter inside the same translation unit in which it is defined.

Prasoon Saurav
+2  A: 

A C program is made by combining one or more translation units together to make a program.

A translation unit is, in effect, a preprocessed source file. It contains any included header and source files specified in #include directives, and excludes anything excluded by #if or similar directives.

When a variable at file scope is declared static it gives the variable name internal linkage. This means that the name refers to an object local to the translation unit in which it appears. If the name is used in another translation unit then it cannot refer to the object in this translation unit, it must refer to a different object.

[In contrast, a name with external linkage refers to the same object whatever translation unit the name is used in.]

static int counter = 0;

When you put a declaration such as this in a header file, it means that every translation unit that includes the header file has its own unique object called counter that is distinct from any object called counter in any other translation unit.

In your case there is one counter in the translation unit generated from CounterMain.c and a separate one in the translation unit generate from Count.c. The one in Count.c is never incremented but is returned by getUserNum(), the one in CounterMain.c is incremented in main but never used anywhere else.

Charles Bailey