I have these dummy piece of software made of 3 files:
test.h
int gv;
void set(int v);
test.c
#include "test.h"
void set(int x) {
gv = x;
}
main.c
#include "test.h"
#include <assert.h>
int main() {
set(1);
assert(gv == 1);
}
The code compiles and run fine in both MSVC 2019 and GCC 8, but with clang (clang-cl 11 supplied by Visual Studio 2019) fails at link time complaining about gv already defined:
1>------ Build started: Project: test, Configuration: Debug x64 ------
1>lld-link : error : undefined symbol: gv
1>>>> referenced by ...\test\main.c:6
1>>>> x64\Debug\main.obj:(main)
1>>>> referenced by ...\test\test.c:4
1>>>> x64\Debug\test.obj:(set)
1>Done building project "test.vcxproj" -- FAILED.
I understand that extern is the default storage-class specifier for objects defined at file scope, but if I explicitly specify extern to int gv, it breaks the linkage with every compiler (unless I add a definition for gv in a source file, of course).
There is something that I do not understand. What is happening?
int gv; is a tentative definition of gv, per C 2018 6.9.2 2. When there is no regular definition in a translation unit (the file being compiled along with everything it includes), a tentative definition becomes a definition with an initializer of zero.
Because this tentative definition is included in both test.c and main.c, there are tentative definitions in both test.c and main.c. When these are linked together, your program has two definitions.
The C standard does not define the behavior when there are two definitions of the same identifier with external linkage. (Having two definitions violates the “shall” requirement in C 2018 6.9 5, and the standard does not define the behavior when the requirement is violated.) For historic reasons, some compilers and linkers have treated tentative definitions as “common symbol” definitions that would be coalesced by the linker—having multiple tentative definitions of the same symbol would be resolved to a single definition. And some do not; some treat tentative definitions more as regular definitions, and the linker complains if there are multiple definitions. This is why you are seeing a difference between different compilers.
To resolve the issue, you can change int gv; in test.h to extern int gv;, which makes it a declaration that is not a definition (not even a tentative definition). Then you should put int gv; or int gv = 0; in test.c to provide one definition for the program. Another solution could be to use the -fcommon switch, per below.
The default behavior changed in GCC version 10 (and possibly Clang at some point; my Apple Clang 11 behaves differently from your report). With GCC and Clang, you can select the desired behavior with the command-line switch -fcommon (to treat tentative definitions as common symbols) or -fno-common (to cause a linker error if there are multiple tentative definitions).
Some additional information is here and here.
I understand that extern is the default storage-class specifier for objects defined at file scope
That's true but the linkage breaks because of "redefinition" of the gv symbol, isn't it?
That's because both test.c and main.c have int gv; after the preprocessor includes the headers. Thus eventually both objects test.o and main.o contain _gv symbol.
The most common solution is to have extern int gv; in the test.h header file (which tells the compiler that gv storage is allocated somewhere else). And inside the C file, main.c for example, define int gv; so that the storage for gv will be actually allocated but only once, inside main.o object.
EDIT:
Referring the same link you provided storage-class specifier, which contains the following statement:
Declarations with external linkage are commonly made available in header files so that all translation units that #include the file may refer to the same identifier that are defined elsewhere.
Related
I was wondering why declaring a global variable in a header file which is included in multiple source in C was working
#ifndef INC_MAIN_H_
#define INC_MAIN_H_
int my_var;
#endif
But assigning it a default value does not work:
#ifndef INC_MAIN_H_
#define INC_MAIN_H_
int my_var = 0;
#endif
It shows multiple compiling errors in each files where I include this main.h
multiple definition of `my_var'; app/src/testfile.o:app/inc/main.h:4: first defined here
I know declaring global variables this way is not the best practice, but I can't figure out why adding an assignation would cause compiling errors and I can't find clear answers about it.
With the build tools and switches you are using, int my_var; acts mostly like a declaration that is not a definition. Multiple declarations of an object identifier are allowed.
int my_var = 0; is a definition. There should be only one definition.
Due to the history of C use and development, int my_var; is technically a tentative definition. If there is no regular definition of my_var in the translation unit (the source file being compiled, including all the files it includes), it acts as a regular definition.
However, then when you have this in a header file that is included in multiple source files, you have multiple definitions. When there are multiple definitions of the same object identifier with external linkage, the C standard does not define the behavior.
When there are multiple regular definitions, your build tools report an error. However, they treat definitions that came from tentative definitions differently: They allow them and coalesce them into a single definition. Again, this is due to the history of how C developed and was used. Also, this behavior changed in GCC recently. Prior to GCC version 10, tentative definitions were coalesced by default, as described above. With GCC 10 and later, tentative definitions are not coalesced, and multiple definitions will result in an error. The old behavior can be requested with the switch -fcommon.
To avoid tentative definitions, you should declare the identifier in the header as extern int my_var;, which is just a declaration and not a tentative definition, and you should have exactly one source file containing int my_var = 0;, which is a regular definition and not a tentative definition.
Additional information is here, here, and here.
I'm currently wondering why I don't get an error from GCC during compilation/linking of a small C program.
I declared in version.h the following string:
const char* const VERSION;
In version.c I have set the initialization of the variable:
const char* const VERSION = "0.8 rev 213";
No problem with that. I can use the string in the rest of the program.
If the c file is missing, no error occurs during compilation/linking but the program fails with SIGSEGV (of course) when it tries to access the variable.
Is my way of setting up the variable VERSION correct or is there a better way? Or is there a chance to get an error during compilation/linking?
You have defined (not just declared) a variable in the header.
If you ever include this header from more than one source file, the behaviour is undefined. Here's the relevant quote from the standard:
J.2 Undefined behavior
…
An identifier with external linkage is used, but in the program there does not exist exactly one external definition for the identifier, or the identifier is not used and there exist multiple external definitions for the identifier.
…
You are relying on a GCC-specific (actually common to many compilers, but still non-standard) behaviour here, which is merging of duplicate tentative data definitions. See help for -fcommon and -fno-common GCC compilation flags. Not all compilers behave this way. Historically this is the common behaviour for linkers, because that's how Fortran worked before there was C.
Assuming this language extension, one of the definitions (one that has an explicit initializer) initialises the variable to point to your string literal. But if you omit this definition, it will remain zero-initialised. That is, it will be a constant null pointer. Not very useful.
To make long story short, never ever do that. In order to declare (but not define) a global variable in a header, use extern. If you do, and try to omit a definition elsewhere, you will likely get a linker error (though the standard does not require diagnostic for this violation, all known implementation produce one).
Your example works because of a Fortran-inspired (mis)feature of C (but not C++) called tentative definitions (6.9.2p2) which is commonly but nonstandardly extended to multiple files.
Tentative definitions are variable declarations without extern and with
no initializer. In common implementations (pun intended), tentative definitions create a special kind of symbol which is called a common symbol. During linking, in the presence of a regular symbol of the same name, other common symbols become references to the regular symbol which means all the empty VERSIONs in your translation units created there due to inclusions will become references to the regular symbol const char* const VERSION = "0.8 rev 213";. If there's no such regular symbol, the common symbols will get merged into a single zero-initalized variable.
I don't know how to get a warning against this with a C compiler.
This
const char* const VERSION;
const char* const VERSION = "0.8 rev 213";
seems to work with gcc no matter what I've tried (g++ won't accept it -- C++ doesn't have the tentative definition feature and it doesn't like const variables that aren't explicitly initialized). But you can compile with -fno-common (which is a fairly "common" (and highly recommended) nonstandard option (gcc,clang, and tcc all have it)) and then you will get a linker error if the non-initialized and the initialized extern-less declarations are in different translation units.
Example:
v.c:
const char * VERSION;
main.c
const char* VERSION;
int main(){}
compilation and linking:
gcc main.c v.c #no error because of tentative definitions
g++ main.c v.c #linker error because C++ doesn't have tentative definitions
gcc main.c v.c -fno-common #linker error because tentative defs. are disabled
(I removed the second const in the example for the sake of the C++ example — C++ additionally makes const globals static or something like that which only complicates the demonstration of the tentative definition feature.)
With tentative definitions disabled or with C++, all your variable declarations in headers should have extern in them
version.h:
extern const char* const VERSION;
and you should have exactly one definition for each global and that definition should have an initializer or be without extern (some compilers will warn if you apply extern to an initialized variable).
version.c:
#include "version.h" //optional; for type checking
const char* const VERSION = "0.8 rev 213";
In version.h you should declare VERSION as a extern like
extern const char* const VERSION;
And in version.c you should define the extern variable like
const char* const VERSION = "0.8 rev 213";
EDIT :- Also you need to make sure that only one source file defined the variable VERSION while the other source files declared it extern and you can do it by defining it in a source file VERSION.c and put the extern declaration in a header file VERSION.h.
I have found that I could achieve the desired results without using extern (though I do agree that it gives reader some kind of an hint about the variable). In some cases using extern gave undesired results.
xyz.h
int i;
file1.c
#include "xyz.h"
....
i=10;
....
file2.c
#include "xyz.h"
main()
{
printf("i=%d\n",i);
}
Of course, it was a large project, broke it down for simple understanding. With extern keyword, I couldnt get desired results. In fact, I got linker error for the variable i with "extern" approach.
Code with "extern" approach,
file1.c
int i;
main()
{
i=10;
}
file2.c
extern int i;
foo()
{
printf("i=%d\n",i);
}
This gave linker error. I just wanted to know why it worked in the first case and also the practical case where we cannot do it without using the keyword "extern". Thanks.
Formally, your first program is invalid. Defining a variable in header file and then including this header file into multiple translation units will ultimately result in multiple definitions of the same entity with external linkage. This is a constraint violation in C.
6.9 External definitions
5 An external definition is an external declaration that is also a
definition of a function (other than an inline definition) or an
object. If an identifier declared with external linkage is used in an
expression (other than as part of the operand of a sizeof operator
whose result is an integer constant), somewhere in the entire program
there shall be exactly one external definition for the identifier;
otherwise, there shall be no more than one.
The definition of i in your first example is a tentative definition (as it has been mentioned in the comments), but it turns into a regular full fledged external definition of i at the end of each translation unit that includes the header file. So, the "tentativeness" of that definition does not change anything from the "whole program" point of view. It is not really germane to the matter at hand (aside for a little remark below).
What makes your first example to compile without error is a popular compiler extension, which is even mentioned as such in the language standard.
J.5 Common extensions
J.5.11 Multiple external definitions
1 There may be more than one
external definition for the identifier of an object, with or without
the explicit use of the keyword extern; if the definitions disagree,
or more than one is initialized, the behavior is undefined (6.9.2).
(It is quite possible that what originally led to that compiler extension in C is some implementational peculiarities of tentative definition support, but at abstract language level tentative definitions have nothing to do with this.)
Your second program is valid with regard to i (BTW, implicit int is no longer supported in C). I don't see how you could get any linker errors from it.
There are at least 2 cases where extern is meaningful and not "redundant":
For objects (not functions) at file scope, it declares the object with external linkage without providing a tentative definition; tentative definitions turn into full definitions at the end of a translation unit, and having the same identifier defined with external linkage in multiple translation units is not permitted.
At block scope (in a function), extern allows you to declare and access an object or function with external linkage without bringing the identifier into file scope (so it's not visible outside the block with the declaration). This is useful if the name could conflict with something else at file scope. For example:
static int a;
int foo(void)
{
return a;
}
int bar(void)
{
extern int a;
return a;
}
Without the extern keyword in bar, int a; would yield a local variable (automatic storage).
I have the following three files:
inline_header.h
#ifndef INLINE_HEADER_H
#define INLINE_HEADER_H
inline int func1() {
return 1;
}
#endif
source1.c
#include "inline_header.h"
source2.c
#include "inline_header.h"
int main() {
func1();
}
When I compile just source2.c with gcc source2.c it compiles. However, when I attempt to compile with gcc source1.c source2.c I get the a multiple definition error as follows:
/tmp/cchsOaHF.o: In function `func1':
source2.c:(.text+0x0): multiple definition of `func1'
/tmp/ccEyUW0T.o:source1.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
I am compiling with gcc 4.8.4 on Ubuntu 14.04.
I've tried looking this up and found a similar question multiple definition of inline function. However in his case, the error is caused by a redefinition of his inline function. In my case, I am not redefining it (or at least not explicitly...).
When you compile source1.c into source1.o, it contains a definition of func1. Similarly, when you compile source2.c into source2.o, it also contains a definition of func1. So when you link source1.o and source2.o, you get a multiple definition error.
The reason the include guards don't prevent this is because source1.c and source2.c are each compiled separately. Include guards only help within a single compilation unit.
If this were not an inline function, you'd put a declaration in the header file:
int func1();
Then put the definition in exactly one source file.
However, you're defining the function as inline. So you need to also declare it as static so that each compilation unit gets its own copy of the function.
EDIT:
The multiple definition error is happening because you're compiling in C89 mode by default, and inline isn't part of that version of the standard. As such, it seems that gcc is basically ignoring that keyword.
If you compile in C99 or C11 mode using -std=c99 or =std=c11 with this code, you'll actually get an "undefined reference" error. Section 6.7.4p7 of the C standard states the following:
Any function with internal linkage can be an inline function. For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function,and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition
What this means is that a function with only inline doesn't actually provide a definition of a function that can be called. In your case, you want to add the static storage class specifier to force a local definition in each file.
Interestingly, if you compile this code as is with -O1 and -std=c99, gcc will physically inline the function and it will compile and run cleanly.
If you wish to place this sort of function in a header, it must also be static:
static inline int func1() {
return 1;
}
This will cause the symbol to be local to each compilation unit (file), avoiding any linker errors.
Also, from the gcc manual:
When an inline function is not static, then the compiler must assume
that there may be calls from other source files; since a global symbol
can be defined only once in any program, the function must not be
defined in the other source files, so the calls therein cannot be
integrated. Therefore, a non-static inline function is always compiled
on its own in the usual fashion.
I'm experiencing some weird behaviour (well, i guess it's got an explanation) while trying to cross compile some Files from Debian to an arm-linux target, using:
$ arm-linux-gnueabi-gcc --version
$ arm-linux-gnueabi-gcc (Debian 4.3.5-4) 4.3.5
While compiling I get these error messages:
dsblock1.c:167: error: non-static declaration of ‘HaveEventIterated_’ follows static declaration
ss2dym.c:778: error: previous declaration of ‘HaveEventIterated_’ was here
The corresponding Lines are:
ss2dym.c:778 :
extern long HaveEventIterated_;
"redeclaration" dsblock1.c:167:
long HaveEventIterated_=0;
So here's the thing i don't get:
according to what i thought i knew, variables first declared extern, are non-statics.
BUT: the "extern" declaration is inside a static function.
So my guess is, that this is some kind of inherited behaviour?!
So here are the questions:
- can someone explain the background story, IF my guess is correct?
- is there an easy way to get around that, e.g. with a tricky compiler-flag that allows nested scope-shifting or some other magic?
I know it would most likely possible to create a header and put all those extern declarations into neutral space, but i've got like hundreds of those errors and several files. And i've seen that this code compiles well using MSVC++ (that has other scope constraints, i know, but i have a lot more problems using arm-g++) so there must be some kind of solution, easier than just rewriting all those parts...
BTW: there is an easy way to change the scope of all those "redeclarations" since i have a macro in front of all of them like:
MY_MACRO long HaveEventIterated_=0;
and atm i compile with -DMY_MACRO=
Soooo anyone? :)
The extern keyword gives identifiers the same linkage1 as a previous visible declaration, or external linkage if there is no previous visible declaration. (This is a weird quirk of the language, to be sure.)
Thus, the problem is most likely occurring due to a third (or perhaps I should say "first") declaration of HaveEventIterated_, that the compiler is reaching before it reaches line 778 of ss2dym.c. That third (or first) declaration uses the static keyword to give the identifier internal linkage. The second declaration, with extern, then gives the same variable internal linkage, and the third—with no storage-class keyword to specify linkage—gives the variable external linkage, resulting in the error.
Here's a short example that reproduces the issue (in a different gcc, but the same behavior):
$ cat foo2.c
static int var;
extern int var;
int var = 0;
$ gcc -c foo2.c
foo2.c:3: error: non-static declaration of 'var' follows static declaration
foo2.c:2: error: previous declaration of 'var' was here
1Side note: if the previous visible declaration has no linkage, you get external linkage. This only occurs when re-declaring a variable inside a block where the variable outside that block has automatic duration, i.e., is a "local" or "stack" variable inside a function:
void f(void) {
int v;
{
extern int v;
Here there's a previous visible declaration with no linkage, so the innermost v has external linkage. If there is a v with internal linkage in the same translation unit, the effect is undefined. (Of course no one should write code like f() in the first place, anyway :-) )