user3387047
user3387047

Reputation: 19

Static variable behaving like a global variable in C

I have two files. 1.c and 2.c

There is a static variable defined in 1.c. I am able to access and modify the static variable in 2.c by using the keyword extern for the variable in 2.c. How is this possible? If the static variable can be accessed in other files like this, then what is purpose of scope of the static variables.

1.c

static int block_no;

2.c

extern int block_no;
block_no = 5;

Upvotes: 1

Views: 128

Answers (2)

paxdiablo
paxdiablo

Reputation: 882646

The only way you could do that is if you've somehow put both identifiers in the same translation unit (such as if 2.c does a #include "1.c").

The relevant portion of the standard is 6.2.2 Linkages of identifiers (a), starting with /2:

2 In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function. Within one translation unit, each declaration of an identifier with internal linkage denotes the same object or function. Each declaration of an identifier with no linkage denotes a unique entity.

3 If the declaration of a file scope identifier for an object or a function contains the storage-class specifier static, the identifier has internal linkage.

4 For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible,23) if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration. If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

Point 4 would be relevant if you had combined them into a single translation unit. If you're compiling them separately then the two block_no variables would be distinct since the static one would have internal linkage.

That's not to say there isn't a defined block_no somewhere else with external linkage which may be being picked up. Otherwise your compiler should complain about a missing block_no in 2.c since, while that file declares the variable, it doesn't define it ("declare" means declare that it exists somewhere else, "define" means actually create it here).


(a) That's from C99 but a quick glance at C11 shows that it hasn't changed.

Upvotes: 3

Jonathan Leffler
Jonathan Leffler

Reputation: 755054

Counter example:

f1.c

static int block_no = 0;

extern int increment(void);

int increment(void)
{
    return block_no++;
}

f2.c

#include <stdio.h>

extern int block_no;
extern int increment(void);

int main(void)
{
    for (int i = 0; i < 10; i++)
    {
        block_no = 5;
        printf("Extern block_no: %d; incremented block_no = %d\n",
               block_no, increment());
    }
    return 0;
}

When compiled, the code does not link:

$ gcc -std=c11 -o f f1.c f2.c
Undefined symbols for architecture x86_64:
  "_block_no", referenced from:
      _main in ccwFyiSK.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status
$

This is the expected behaviour. You will have to write similarly simple code to reproduce the trouble.

Adding int block_no; to the end of f2.c allows the code to compile and run, producing:

Extern block_no: 5; incremented block_no = 0
Extern block_no: 5; incremented block_no = 1
Extern block_no: 5; incremented block_no = 2
Extern block_no: 5; incremented block_no = 3
Extern block_no: 5; incremented block_no = 4
Extern block_no: 5; incremented block_no = 5
Extern block_no: 5; incremented block_no = 6
Extern block_no: 5; incremented block_no = 7
Extern block_no: 5; incremented block_no = 8
Extern block_no: 5; incremented block_no = 9

Another way to get it to 'work' is this variant of f2.c:

#include <stdio.h>
#include "f1.c"

extern int block_no;
extern int increment(void);

int main(void)
{
    for (int i = 0; i < 10; i++)
    {
        block_no = 5;
        printf("Extern block_no: %d; incremented block_no = %d\n", block_no, increment());
    }
    return 0;
}

The output from this is tedious:

Extern block_no: 6; incremented block_no = 5
…
Extern block_no: 6; incremented block_no = 5

Note that it is not normal to include C source files (file.c) inside other C source files. There can be reasons to do it, but they are special cases, and what you describe isn't one of those special cases.

Upvotes: 1

Related Questions