Static variables in C accessed from another file - c

I have two C-files, each having defined a static int variable sharing the same name.
My understanding is that static variables declared at top-level should be limited to usage within the same file.
However, when I run my program it is obvious that these files affect the value of one another's static variable.
Have I misunderstood how the static keyword works and is there another way to obtain this file-based separation of scopes?
*Edit: Added source code to demonstrate problem. This code is from 3 separate files, as indicated by the comments.
//file 1
static int buffer;
void setter_1(int *input) {
buffer = *input;
}
void getter_1(int *output) {
*output = buffer;
}
//file 2
static int buffer;
void setter_2(int *input) {
buffer = *input;
}
void getter_2(int *output) {
*output = buffer;
}
//main
#include <stdio.h>
#include "buffer_1.c"
#include "buffer_2.c"
int main() {
int int1 = 1;
int int2 = 2;
setter_1(&int1);
setter_2(&int2);
getter_1(&int1);
getter_2(&int2);
printf("%i, %i\n", int1, int2);
return 0;
}
We expected to get two different numbers ("1, 2"), but got two identical numbers ("2, 2").
Thanks in advance
/Frisch

Even though we often talk about the structure of C program in terms of "files", most of the time what is really meant by "file" is translation unit - a source file together with everything that is #included into it.
Now, static variable in C means a variable with internal linkage, i.e. a variable that is not linkable by name between different translation units. Each translation unit is such case gets its own, completely independent variable. Having multiple translation units in this case is absolutely critical: the separation in question is only possible, again, between different translation units.
In your example you have only one translation unit: you included your .c files into a single main.c file, i.e you merged all of your translation units into one translation unit. The title of your question refers to static variable "accessed from another file". In reality there's no "another file" in your example. You have only one "file".
Since you merged everything into a single translation unit, your static variable declarations became repetitive declarations of the same variable inside one translation unit.
Note that your static variable declarations happen to be definitions at the same time. In C++ such repetitive definitions of the same variable would trigger a "multiple definition" error. In C such definitions are treated as tentative definitions (a C-specific feature), which allows them to slip through. But if you add explicit initializers to your static variables (e.g. static int buffer = 0;) the definitions will no longer be tentative and the code will fail to compile even in C.
If you want to maintain different, independent variables in this case, stop including your .c files into your main.c file. Translate each .c file independently, as a separate translation unit, and then link them together into the final program.

One way this can happen is when pointers to those static variables are passed between functions in the two files:
file1.c:
static int i1;
...
foo(&i1);
file2.c:
void foo(int *ip)
{
*ip = 42;
}
Calling foo in file1.c would modify i1 from a function outside file1.c

I suppose by inclusions you have effectively declared the same static global variable twice, which is why you no longer have two separate variables, but one.

The way you did it, by including file 1 and file 2 in main, you effectively have only one "buffer" variable.

Related

Global static variables shared in multiple source files

While learning about extern and static variables in C/C++, I came across this answer.
Maybe I'm missing some point, but this answer raised doubts about a code of mine.
Suppose I have the following files:
header.h
static int global_foo = -1;
void doSomething(void);
source1.c
#include "header.h"
void doSomething(void) {
global_foo = 1;
}
main.c
#include "header.h"
int main(void) {
doSomething();
printf("%d\n", global_foo);
}
What exactly is the output of the printf in the main function? My interpretation is that since global_foo is included two times, there will be two distinct global_foo, and therefore one such change will affect only the global_foo of the file that it belongs to.
Your assessment is correct.
Because global_foo is declared static, each source file has its own distinct variable by that same name, and a change to one does not affect the other.
Because of this, the program will print -1, since the global_foo in main.c is unchanged.
Global variables have static storage duration anyway so there's no need to include the static qualifier to explicitly state its storage duration. When you declare a global variable as static within a translation unit you are just saying that it has internal linkage within that translation unit. This means it can only be identified by its name within the translation unit.
So, if you declare a variable as static in a header file, every translation unit that includes it gets its own copy of the variable that is different from all the others.
If you have a function that returns the address of the variable, i.e.
int *getStaticAddress ()
{
return &static_var;
}
You can use that to access the variable outside the translation unit.

What is the purpose of an external static variable?

K&R c page 83 says the following:
The static declaration, applied to an external variable or function, limits the scope of that object to the rest of the source file being compiled. External static thus provides a way to hide names like buf and bufp in the getch-ungetch combination, which must be external so they can be shared, yet which should not be visible to users of getch and ungetch.
How could any external variable be visible in another file without an extern modifier on the variable in the new file anyway? Is there some type of added protection for variables with the static storage class?
What is the purpose of using static on an external variable? Any simple examples?
Edit:
I think I'm confusing people with my question, so I'm going to write it out as code. I'm expanding the idea to include functions as well:
contents of file 1
void somefunc(void);
int x;
int main()
{
....
}
void somefunc(void)
{
....
}
file 2
int x;
void somefunc(void);
void somefunc(void)
{
....
}
Notice that int x and somefunc() in file 1 are not visible in file 2, and vice versa. That is, unless we include an extern modifier on int x and/or somefunc() in either file, the matching function and variable names from the files will be invisible to one another.
Why would we need to put static on one of these variables or functions to prevent the variable or function from being visible in the other file if we already have to knowingly use an extern to make the function or variable visible in the other file?
The code would need to look like this for contents of file 2 to be visible in file 1:
extern void somefunc(void);
extern int x;
int main()
{
....
}
void somefunc(void)
{
....
}
file 2
int x;
void somefunc(void);
void somefunc(void)
{
....
}
There is a difference of terminology between K&R2 and the C Standard.
K&R2 uses the wording external variable for a file-scope variable, and uses the wording external static to specify a file-scope variable declared with the static storage class specifier. In the C Standard the word external is usually reserved for linkage and not for lexical scope.
Quoting what you quoted:
External static thus provides a way to hide names like buf and bufp in the getch-ungetch combination, which must be external so they can be shared, yet which should not be visible to users of getch and ungetch.
It just means that the static variables are not function scoped static variables. They are external to functions but they are static in the file.
Making them static in a file makes them visible to getch and ungetch but not to other functions in other files.
Update, in response to edited question
You said,
Notice that int x and somefunc() in file 1 are not visible in file 2, and vice versa. That is, unless we include an extern modifier on int x and/or somefunc() in either file, the matching function and variable names from the files will be invisible to one another.
That is an erroneous conclusion.
The line
int x;
equivalent to:
extern int x;
int x;
The line
void somefunc(void);
is equivalent to:
extern void somefunc(void);
If you compile the "file 1" and "file 2" and link the resulting object files to create an executable, you will get linker errors to the effect that int x and void somefunc(void) are multiply defined.
In order to keep them visible only in the respective files, you will have to make them static in the file scope.
static int x;
static void somefunc(void);
What are the uses of the keyword static?
This simple question is rarely answered completely. Static has three distinct uses in C:
(a) A variable declared static within the body of a function maintains its value between function invocations.
(b) A variable declared static within a module1, (but outside the body of a function) is accessible by all functions within that module. It is not accessible by functions within any other module. That is, it is a localized global.
(c) Functions declared static within a module may only be called by other functions within that module. That is, the scope of the function is localized to the module within which it is declared.
Most candidates get the first part correct. A reasonable number get the second part correct, while a pitiful number understand answer (c).
From A ‘C’ Test: The 0×10 Best Questions for Would-be Embedded Programmers
Think about such a situation:
If you have three files, file1 and file 2 both have int x, but with different values, then in file3 you have extern int x. How could the compiler know which x you want? That's when you need extern.

Static 2D array in multiple files in C

I use a 2D array of chars that should be written & read by multiple functions in C.
This is my array:
static char array[3][6];
And let's say I have a function 'Function()' that modify this array.
If the function is defined in the main there is no problem (the the array is correctly written & then read), but if I want to have my Function in an another file, the array is correctly written but when I return in the main is magically empty!
This is my code.
main.c
#include "support.h"
int main(int argc, char *argv[])
{
Function();
unsigned i, j;
for(i = 0; i < 3; i++)
{
for(j = 0; j < 6; j++)
printf("[%c]", array[i][j]);
printf("\n");
}
system("PAUSE");
return 0;
}
support.h
static char array[3][6];
support.c
void Function()
{
char hello[6];
hello[0] = 'H';
hello[1] = 'E';
hello[2] = 'L';
hello[3] = 'L';
hello[4] = 'O';
hello[5] = '\0';
strcpy(array[0], hello);
}
No compilation error nor runtime error. One again, if I try to move everything in main.c all works, if I divide in two files it doesn't (the array is correct as soon as it returns from the Function(), then it is freed), how is it possible?
By declaring array as static in a header file, you give each source file which includes support.h its own copy. You need to change the header to
extern char array[3][6];
and add
char array[3][6];
to a single source file.
The whole point of declaring a file-level entity static is to give it internal linkage, i.e. to ensure that the entity is not visible to the other translation units and that each translation unit gets its own independent copy of such entity. That applies to both functions and objects. In your case each translation unit that includes support.h gets its own independent copy of array object. This is exactly what you achieved by declaring a static array in a header file. And that is why main.c cannot see any modifications made in support.c - these two translation units work with two completely independent arrays.
Now, when I say that the array "is not visible" from other translation units, I mean that you will not be able to refer to it by name from other translation units (i.e. you will not be able to link to it). This is not necessarily a bad thing. If you still want to declare your array as static and access it from other translation units, you can still implement this access "manually": define that array as static in one translation unit and then pass that array to all other functions as a parameter.
(Note, that in most cases passing your array around through function parameters might be a much better idea than introduction of a global variable. And that will even allow you to declare your array as local object in main.)
But if you insist on a genuine global variable, you should stop using static. Declare your arrray in the header file, per #simonc answer, and define it in one of the translation units as an object with external linkage.
Well, you have, actually, two array defined in your program: one in the compile unit main and other in compile unit support. Compiler will compile main.c by one side and support.c by the other side, typically creating object files (usually .obj or .o). The linker put both together, resolving addresses.
Because you are defined the array as static:
static char array[3][6];
you are telling to the compiler the array is private to the compile unit. And because both main.c and support.c are including support.h, both are creating its own private array. Code in main only can see the array defined in main.c, and code in support.c only can see the array defined in support.c. When you call to Function(), that is modifying the array in support.c, not the array visible in main.c.
If you remove the static keyword from the array definition in support.h, you will have a linker error, since that symbol is defined twice (in main.c and in support.c). You must decide where do you want to define the array (in support.c or main.c) and reference to it from the other source file using the extern keyword.

Why is use of an array defined in File1 working in File2 (only declared there),even without "extern"?

Here I have two files externdemo1.c and externdemo2.c.In the first file,I have declared and initialized a character array arr at file scope.But I have declared it in the second file externdemo2.c without the extern keyword and made use of it there in the function display(). Here are my confusions arising from it.Please answer these three:
//File No.1--externdemo1.c
#include<stdio.h>
#include "externdemo2.c"
extern int display();
char arr[3]={'3','4','7'};
//extern char arr[3]={'3','4','7'};
//extern int main()
int main()
{
printf("%d",display());
}
//File No.2--externdemo2.c
char arr[3];
int display()
{
return sizeof(arr);
}
1) Why does the program compile fine even though I have declared arr without the extern keyword in externdemo2.c?I had read that the default linkage of functions is external,but I am not sure if that's so even for variables.I only know that global variables have extern storage class.
2) What is the rigorous difference between extern storage class and extern linkage.I badly need a clarification about this.In the first file,where I have defined the array arr,I haven't used the keyword extern, but I know that it has extern storage class by default.But in the second file, isn't there any default extern ,storage class or linkage,about the global variable arr,ie, in externdemo2.c?
3) Check the commented out line in the first file externdemo1.c.Just to test it, I had used the line extern char arr[3]={'3','4','7'};.But it gives the error 'arr' initialized and declared 'extern'.What does this error mean? I have also mentioned a commented line extern int main(),but it works fine without error or warning.So why can we use extern for a function even though a function is extern by default,but not for a variable,like arr here?
Please take some time to bail me out over this.It will clear most of my lingering doubts about the whole extern thing.It will be immense help if you can answer all 3 bits 1),2) and 3). Especially 3) is eating my brains out
Main questions
Basically, because you've included the source of externdemo2.c in the file externdemo1.c.
This is the big question. Because there is no initializer, the line char arr[3]; in externdemo2.c generates a tentative definition of the array arr. When the actual definition with initialization is encountered, the tentative definition is no longer tentative — but neither is it a duplicate definition.
Regarding extern storage class vs extern linkage...Linkage refers to whether a symbol can be seen from outside the source file in which it is defined. A symbol with extern linkage can be accessed by name by other source files in which it is appropriately declared. To the extent it is defined, extern storage class means 'stored outside of the scope of a function', so independent of any function. The variable defined with exern storage class might or might not have extern linkage.
Because it is not defined with the keyword static, the array arr has extern linkage; it is a global variable.
With the commented out line uncommented out, you have two definitions of one array, which is not allowed.
I observe that you must be compiling just externdemo1.c to create a program — the compiler is including the code from externdemo2.c because it is directly included. You can create an object file from externdemo2.c. However, you cannot create a program by linking the object files from both externdemo1.c and externdemo2.c because that would lead to multiple definitions of the function display().
Auxilliary questions
I have placed both files in the [same directory]. If I don't include the second file in the first, then when I compile the first file it gives the error undefined reference to display. Since I have used extern for that function in the first file, isn't the linker supposed to link to it even if I don't include the second file? Or the linker looks for it only in default folders?
There are a couple of confusions here. Let's try dealing with them one at a time.
Linking
The linker (usually launched by the compiler) will link the object files and libraries that are specified on its command line. If you want two object files, call them externdemo1.obj and externdemo2.obj, linked together, you must tell the linker (via the build system in the IDE) that it needs to process both object files — as well as any libraries that it doesn't pick up by default. (The Standard C library, plus the platform-specific extensions, are normally picked up automatically, unless you go out of your way to stop that happening.)
The linker is not obliged to spend any time looking for stray object files that might satisfy references; indeed, it is expected to link only those object files and libraries that it is told to link and not add others at its whim. There are some caveats about libraries (the linker might add some libraries not mentioned on the command line if one of the libraries it is told to link with has references built into it to other libraries), but the linker doesn't add extra object files to the mix.
C++ with template instantiation might be argued to be a bit different, but it is actually following much the same rules.
Source code
You should have a header, externdemo.h, that contains:
#ifndef EXTERNDEMO_H_INCLUDED
#define EXTERNDEMO_H_INCLUDED
extern int display(void);
extern char arr[3]; // Or extern char arr[]; -- but NOT extern char *arr;
#endif /* EXTERNDEMO_H_INCLUDED */
You should then modify the source files to include the header:
//File No.1--externdemo1.c
#include <stdio.h>
#include "externdemo.h"
char arr[3] = { '3', '4', '7' };
int main(void)
{
printf("%d\n", display());
return 0;
}
and:
//File No.2--externdemo2.c
#include "externdemo.h"
int display(void)
{
return sizeof(arr);
}
The only tricky issue here is 'does externdemo2.c really know the size of arr?' The answer is 'Yes' (at least using GCC 4.7.1 on Mac OS X 10.8.3). However, if the extern declaration in the header did not include the size (extern char arr[];), you would get compilation errors such as:
externdemo2.c: In function ‘display’:
externdemo2.c:7:18: error: invalid application of ‘sizeof’ to incomplete type ‘char[]’
externdemo2.c:8:1: warning: control reaches end of non-void function [-Wreturn-type]
Your program looks a bit err. To me the #include "externdemo2.c" line appears invalid.
Following is the correction I have made and it works.
//File No.1--externdemo1.c
#include <stdio.h>
extern char arr[3];
extern int display();
int main()
{
printf("%d", arr[0]);
printf("%d",display());
}
//File No.2--externdemo2.c
char arr[3]={'3','4','7'};
int display()
{
return sizeof(arr);
}
Please follow the below links for better understanding:
Effects of the extern keyword on C functions
How do I use extern to share variables between source files?
Using #include as shown will make both as one file only. You can check the intermediate file with flag -E, as in:
gcc -E externdemo1.c

Incrementing a global static int in main

Here's my code:
File DataTypes.h
static int count=0;
File TreeOps.h
#include"DataTypes.h"
void printTree(Tree* ptr)
File TreeOps.c
#include"TreeOps.h"
void printTree(pointer){
count++; // incrementing the count;
printf("%d",counter);
}
File TreeMain.c
#include"TreeOps.h"
printTree(pointer); // all the necessary declarations are done.
printf("%d",count);
If in printTree function the printf gives count=1; while in main function it gives me 0.
Why?
static variable in this context means: every c file has its own variable instance. Remove static definition in h-file:
extern int count;
and add this to one of c files:
int count = 0;
extern means: this is forward declaration. By defining a variable as extern, you tell to compiler that count has int type, and this variable is created somewhere. Actually, this variable is created in one and only one c file. You can use it in any c file where DataTypes.h is included. In the file where this variable is created, compiler uses it. In all other file this variable becomes external reference, which is resolved later by linker.
First off, defining data or functions in header files is a bad practice in C programming. In DataTypes.h you don't just declare the count variable, but you define it.
What actually happens is that the count is defined separately in each translation unit and you end up with two variables after linking. The linker doesn't merge them because they are marked static, that means they should be local to the translation unit.
If you want the count variable to be shared between the TreeOps.c and TreeMain.c translation units, you must use extern in the header file which only declares it:
extern int count;
And then define it globally as int count in either of TreeOps.c or TreeMain.c.
You don't have a "global static int" in your program. Entities declared as static cannot possibly be "global". The whole point of declaring something static is to make it local to a specific translation unit. This is exactly what you've done: you have declared two completely independent static variables in two different translation units. Each variable is local to its own translation unit. Then you are modifying one of these variables and printing the other. No wonder that the other remains unchanged.
In this case you have to decide what it is exactly you want. You can either have your variable as a global variable or as a static variable, but not both at the same time. "Global variable" and "static variable" are mutually exclusive concepts. So, what is it you want: global or static?

Resources