I'm trying to understand on how to link two files with a variable. I know that the standard way is through extern keyword as shown below.
File 1
#include<stdio.h>
int i;
void func1();
int main()
{
i = 10;
func1();
printf("i value with method 1: %d\n", i);
}
File 2
extern int i;
void func1()
{
i = i 1;
}
Compile and execute => gcc *.c ./a.out
Output=> i value with method 1: 11
One thing I found is through headers as shown below:
common.h
#ifndef __COMMON_H
#define __COMMON_H
int i;
#endif
File 1
#include<stdio.h>
#include"common.h"
int main()
{
i = 10;
func1();
printf("i value with method 2: %d\n", i);
}
File 2
#include"common.h"
void func1()
{
i = i 1;
}
Compile and execute => gcc *.c ./a.out
Output=> i value with method 2: 11
My doubt is, How method2 works?
CodePudding user response:
At file scope, int i;
is a special kind of declaration called a tentative definition. In spite of its name, it is not a definition. However, it can cause a definition to be created.
A clean way to declare and define an object that is used in multiple translation units is to declare it in a header that is included in each unit that uses the object by name:
extern int i;
and to define the object in one translation unit:
int i = 0;
At file scope, int i = 0;
is a definition; the initialization with = 0
makes it a regular definition instead of a tentative definition.
Ideally, all source code would use clean declarations and definitions. However, C was not completely planned and designed in advance. It developed through experiments and different people in different places implementing things differently. When the C committee standardized C, they had to deal with different practices and implementations. One common practice was that of declarations such as int i;
in multiple units that were intended to create a single i
. (This behavior was inherited from FORTRAN, which had common objects as a similar feature.)
To accommodate this, the committee described int i;
at file scope as a special kind of declaration, a tentative definition. If there is a regular definition in the same translation unit that defines the same identifier, the tentative definition acts as a plain declaration, not a definition. If there is no regular definition, the compiler (or other part of C implementation) creates a definition for the identifier as if it had been initialized with zero.
The C standard leaves reconciling of multiple tentative definitions to each C implementation; it does not define the behavior when int i;
is used in multiple translation units. Prior to version 10, the default behavior of GCC was to use the “common symbol” behavior; multiple tentative definitions would be reconciled to a single definition when linking. (To support this, the compiler marks tentative definitions differently from regular definitions when creating object modules, so the linker knows which is which.) In version 10, the default changed, and GCC now treats the definitions resulting from tentative definitions as regular symbols instead of common symbols.
This is why you will see some people report they get an error when linking sources with tentative definitions while you and others do not. It is simply a matter of which version of which compiler and linker they used.
You can explicitly request either behavior with the GCC switch -fcommon
for the common symbol behavior or -fno-common
for the regular symbol behavior.
Generally, you should use the clean method above; declare identifiers with extern
in headers, and put exactly one definition in each identifier in one source file.