linkage in c  - Page 1
Forum section: electronics / general

author: BlogRahul
author_rank: Regular Contributor
timestamp: December 11, 2021, 10:10:16 am
content: I'm trying to understand what is the role of the linker in my code. What is internal and external linkage?Here is my code that conatians multiple filesshared.hCode:extern int x;void foo();foo.cCode:int x;void foo() {x++;}main.cCode:#include <stdio.h>#include "shared.h"int main() {x = 37;printf("%d\n", x);foo();printf("%d\n", x);return 0;}

author: magic
author_rank: Super Contributor
timestamp: December 11, 2021, 10:53:04 am
content: Compiler is what turns source code files into object files (foo.o, main.o on UNIX or .obj on Windows).Linker is what puts those object files together into the final executable.(Some compilers support going from C files straight to executable, they run the linker automatically behind your back).Linking permits code in one file (main) to refer to functions and variables from other files (foo).Theexternkeyword appears in main.c when shared.h is included into it and it instructs the compiler not to create thexvariable in main.o but to reference a yet-nonexistent external variable and leave it to the linker to stitch things together.Meanwhile in foo.o, an actual integer variable is created.This is analogous to "void foo() {...}" versus "void foo();" - one creates a function, the other enables calling a function created elsewhere. It should probably also haveexternin it for logical consistency, but C syntax never made much sense

author: BlogRahul
author_rank: Regular Contributor
timestamp: December 11, 2021, 02:35:13 pm
content: Quote from: magic on December 11, 2021, 10:53:04 am@magic Thanks I am working with GCC compiler on PCThere are three types of linkage: external linkage, internal linkage, and no linkage.What changes have to made in the uploaded code if want to check these three types of errors?

author: newbrain
author_rank: Super Contributor
timestamp: December 12, 2021, 04:48:57 pm
content: Quote from: BlogRahul on December 11, 2021, 02:35:13 pmThe (correct) terms you are using are the ones in the standard.For the standard 'linkage' means (very very simply) 'the behaviour when an identifier is declared more then once in a different or the same scope'.This is a bit different, but has of course a strict relationship with what a linker does: putting together compiled translation units (~= source files) and making surethat identifier names correspond to the right object(the relevant part is underlined).The definition of the three linkage type are (summarizing from the C11 standard, Ch 6.2.2):All the declaration of an identifier withinternallinkage represent the same object or function in the same translation unit.All the declaration of an identifier withexternallinkage represent the same object or function across all the same translation units.All the declaration of an identifier withnolinkage represent the same entity.Note the shift of language between internal/external and no linkage: entity is used instead of object or function, as also typedef names, struct tags, function parameters etc. are identifiers with no linkage.Now, all of this is nice, formally sound and consistent, but some complications (and confusion) are brought by the fact that in C linkage is signified by the keywordsexternandstatic(or their absence) that have other semantics related to the storage duration (= time of life) or by where a declaration is found.Ojects or functions declared with "static" will have internal linkage.Objects or functions declared with "extern" will have external linkage, if no previous declaration is found.Objects at file scope, and functions, default to extern.Objects at block scope default to no linkage.Example file A:Code:static int i; /* i has internal linkage */extern int i; /* Fine, i is still static - compiler will not complain - as the previous declaration takes priority */File B:Code:extern int i; /* This is i is not the same as the one above, as that one has internal linkage! */Other example:Code:extern int i; /* no previous declaration, i has external linkage */static int i; /* loud complaining from the compiler: static means internal linkage, but the object has external linkage! */Note the difference with file A above!No linkage:Code:void f(int i); /* i has no linkage */void f(int i){int j; /* Also j ha no linkage */extern int k; /* but k has external linkage */static int n; /* and n has internal linkage */}

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: December 14, 2021, 01:08:38 am
content: Quote from: newbrain on December 12, 2021, 04:48:57 pmWell, that's a bit misleading. The language standard explicitly states that "Eachdeclaration of an identifier with no linkage denotes auniqueentity." This is completely different from what you stated above.In fact, it is impossible to produce multiple declarations of the same entity with no linkage: any attempts to do so would represent constraint violations, i.e. they would be flagged aserrors.Quote from: newbrain on December 12, 2021, 04:48:57 pmThis is also incorrect. `n` has no linkage. Even though it is declared with `static`, local `static` declarations produce entities withno linkage(!).

author: magic
author_rank: Super Contributor
timestamp: December 14, 2021, 06:36:31 am
content: Not sure about k too.I tried similar code and anexternlocal variable is accepted and does appear in the symbol table, but differently from a global and it can't be linked against by normal means.I guess one would have to dig into standards/manuals to understand what happens here

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: December 14, 2021, 06:56:25 am
content: Quote from: magic on December 14, 2021, 06:36:31 amI don't see anything difficult about `k`. The above `k` is a non-defining declaration. It is not a definition. You can't "link against it" since there's nothing to link against.

author: magic
author_rank: Super Contributor
timestamp: December 14, 2021, 07:52:26 am
content: Right, it's a way of pulling external globals into the local scope.I didn't even realize it's a thing.

author: brucehoult
author_rank: Super Contributor
timestamp: December 14, 2021, 09:32:57 am
content: Quote from: magic on December 14, 2021, 06:36:31 amCode:#include <stdio.h>int k;void foo(){int k = 42;printf("k = %d at start of foo\n", k);{extern int k;++k;printf("k = %d in middle of foo\n", k);}printf("k = %d at end of foo\n", k);}int main(){k = 100;printf("k = %d at start of main\n", k);foo();printf("k = %d at end of main\n", k);return 0;}When run ...Code:k = 100 at start of maink = 42 at start of fook = 101 in middle of fook = 42 at end of fook = 101 at end of mainIt gives access to whatever k is at global scope in the program (not necessarily the same source file)

author: newbrain
author_rank: Super Contributor
timestamp: December 14, 2021, 09:39:40 am
content: Quote from: TheCalligrapher on December 14, 2021, 01:08:38 amNope - it's completely and utterly wrong, as you later correctly point out.I plead death by copy-paste, though that does not excuse me, I should have re-read.Each entity with no linkage isunique.Quote from: TheCalligrapher on December 14, 2021, 01:08:38 amIn re-reading §3 and §6 of 6.2.2, I think you are right here too. This was a real misunderstanding on my side (I though it resolved to the expected behaviour due to the scope, rather than linkage).I always appreciate your well reasoned and documented posts, so:Thanks al lot for your corrections, TheCalligrapher!

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: December 14, 2021, 03:47:15 pm
content: Quote from: brucehoult on December 14, 2021, 09:32:57 amIt is true, but the underlying mechanism is a bit more elaborate. Asnewbrainstated above, this is legalCode:/* `k` is not declared yet */static int k;extern int k;The `extern` declaration "inherits" internal linkage from the previous declaration of `k`.`extern` actually performs a name lookup and tries to find a prior declaration of the same identifier, as stated in6.2.2/4. And, per 6.2.2/4If 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 declarationIf no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.Point 1 is what makes the above example with "inherited" linkage work.Point 2 states that in this (reversed order) exampleCode:/* `k` is not declared yet */extern int k;static int k;the first declaration declares `k` with external linkage and the second one declares it with internal linkage. This isundefined behaviorper6.2.2/7, but most С compilers will immediately flag it as an "error".Finally, we can concoct the following more elaborate (and strange) exampleCode:static int k; /* 1 */int main(void){int k; /* 2 */{extern int k; /* 3 */}}In this case the declaration #3 finds the previous declaration #2. Since the declaration #2 has no linkage, declaration #3 declares `k` with external linkage (per 6.2.2/4 above). And, according to 6.2.2/7 the behavior is undefined: declaration #3 conflicts with the declaration #1.The point I'm making here is that declaration #2 is not ignored by #3. It plays its role in the whole process. Declaration #2hidesdeclaration #1 from declaration #3, which is what ultimately makes this example undefined. If we comment out declaration #2, this behavior will become defined: #3 will refer to #1 and "inherit" internal linkage from #1.It is interesting to note that GCC flags this as an error as well:http://coliru.stacked-crooked.com/a/e701023e51333227Meanwhile Clang is quiet about it:http://coliru.stacked-crooked.com/a/4569f4092ad26c06Clang has no trouble flagging the more trivial example of the same UB:http://coliru.stacked-crooked.com/a/fb5a86047cf262ba

author: magic
author_rank: Super Contributor
timestamp: December 14, 2021, 05:39:06 pm
content: Those rules are very bizarre.Is there any situation where usingexternas anything other than "forward declaration" actually makes sense?

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: December 14, 2021, 06:52:27 pm
content: Quote from: magic on December 14, 2021, 05:39:06 pmIn "classic" C (C89/90) non-redundant uses of `extern` boil down to:1. "Forward declaration" of a variable.2. Unhiding a file-level variable hidden by a local variable, as shown in the examples above (this is rare).In modern C (C99 and later) you have one more use:3. Combination `extern inline` emits external definition for an inline function.---In C++ there's one more prominent use of `extern`: overriding default internal linkage for `const` objectsCode:extern const int k = 42;The above `k` would have internal linkage (since `const` implies internal linkage in C++), but thanks to the explicit `extern` it receives external linkage in this declaration.

author: magic
author_rank: Super Contributor
timestamp: December 14, 2021, 09:36:25 pm
content: Well, you omitted the third possibility of importing global objects from other TUs into an individual block scope.I'm just not quite sure who would want to use that block-scopeexternfor any reason IRL and how it ended up in the standard.I have never seen such thing and I'm pretty sure it wouldn't pass code review in most places. Just rename the variables if they clash.

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: December 14, 2021, 09:49:08 pm
content: Quote from: magic on December 14, 2021, 09:36:25 pmI assumed this to be covered by "forward declaration". I understand "forward declaration" as a declaration that basically says that "the definition is located elsewhere", not necessarily in this very translation unit. Where this declaration is made - in block scope or in file scope - makes no tangible difference.

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 14, 2021, 10:25:39 pm
content: Quote from: magic on December 14, 2021, 09:36:25 pmThis is more than absolutely HORRIBLE. It's a doubly pathological case of variable shadowing.How it ended up in the standard? No clue, but a couple hypotheses. The main one I have in mind is that it might have been allowed, *not necessarily willingly*, by older C compilers, and the committee had to keep that feature for legacy reasons. (This is very common throughout the standard.)

author: westfw
author_rank: Super Contributor
timestamp: December 15, 2021, 03:02:05 am
content: QuoteI'm a little worried because it seems that handling C's "linkage" concepts seems to be only a very small part of what "the linker" does.(in fact, for functions with "internal (static) linkage", the linker might not be involved at all.)

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: December 15, 2021, 03:24:05 am
content: Quote from: westfw on December 15, 2021, 03:02:05 amThat's the intent. In a typical С implementation internal linkage is handled entirely by the compiler proper. Linker knows nothing about it.