0

In this page I cannot understand why Example 3 throws an error:

// CODE 1:

extern int var;
int main(void)
{
  var = 10;
  return 0;
}

There are several answers mentioning that var inside main is a local variable. But why would it not be a global variable at the same time. This code below works tough:

// CODE 2:

int var;        // global
int main(void)
{
  var = 10;
  return 0;
}

In the case of multiple files...

... the code below works:

// CODE 3:

// File 1
extern int var;
int main(void)
{
  print("%d",var);
  return 0;
}
----------------

// File 2
var = 4;

... while this one does not:

// CODE 4:

// File 1
extern int var;
int main(void)
{
  print("%d",var);
  return 0;
}
----------------

// File 2
int func(int a) {
    extern int var;
    var = 3;
    return 0;
}

So I could not find a meaningful explanation to the behavior of the extern keyword. How do you explain/ interpret that keyword? Also what should I change in the codes to make them work as intended?

Xfce4
  • 557
  • 5
  • 28
  • 1
    "There are several answers mentioning that var inside main is a local variable." Huh? Where? It is not. And what exactly is `v` in the 3rd example? It is never declared anywhere This is pseudo code, please post the actual C code. – Lundin Mar 04 '22 at 07:32
  • Does this answer your question? [How do I use extern to share variables between source files?](https://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files) – Visual Studio Mar 04 '22 at 07:46
  • @4386427 Dude, I can also read as you do. Thank you. What I am asking is why CODE 2 works while CODE 1 does not. Why it is declared and defined (accepted) in CODE 2 but not in CODE 1 ? – Xfce4 Mar 04 '22 at 07:53
  • Because CODE 2 creates an `int` object `var` as global variable. CODE 1 only tells the compiler that somewhere else there is some `int` object `var` but it is not here. Then the linker will try to find where this "somewhere else" might be and fails. – Gerhardh Mar 04 '22 at 08:26
  • Roughly: `extern int var;` means: `var` is declared somewhere else. So the code will compile, but then the linker will fail because it can't find `var` anywhere. – Jabberwocky Mar 04 '22 at 08:27
  • @RyanR Thank you. The link seems to be providing quite beneficial information (not only about `extern` keyword, but also about C in general). – Xfce4 Mar 04 '22 at 08:36
  • @Gerhardh & Jabberwocky, I think I understand the difference now. Looks like `extern int` is **not even a declaration**, although it looks like a declaration. The keyword implies that there should be an `int` variable declared in some file. So before using `var`, I need to declare/ define it first, either in the same file or in another file. – Xfce4 Mar 04 '22 at 08:49
  • 2
    Not exactly. It is a *declaration*. It tells the compiler about type and name of some data object. What is missing is the *definition* of that thing. You seem to use definition/declaration as synonyms which they are not. – Gerhardh Mar 04 '22 at 08:51
  • @Gerhardh But then I would expect CODE 1 to work as `var` would be both declared and defined. – Xfce4 Mar 04 '22 at 08:53
  • 1
    @Xfce4 no, `extern int var;` just tells the compiler that `var` is defined somewhere else (there is a `int var;` somewhere else either in the same .c file or in anothe rone), which is not the case here. Read this: https://stackoverflow.com/questions/496448/how-to-correctly-use-the-extern-keyword-in-c – Jabberwocky Mar 04 '22 at 08:56
  • @4386427 If a variable is 'declared', it can be initialized/ defined inside the main function, which would be the case in CODE 1. But CODE 1 does not work (while CODE 2 does). So, to me it looks like extern is not even a decleration. – Xfce4 Mar 04 '22 at 09:03
  • @4386427 So, why does CODE 2 work but CODE 1 throws an error? I would say the same thing with CODE2: `int var;` is a declaration and `var = 10;` is an initialization, completing the definition. – Xfce4 Mar 04 '22 at 09:09
  • @4386427 "For example consider the following [declaration](https://www.geeksforgeeks.org/difference-between-definition-and-declaration/#:~:text=Declaration%20of%20a%20variable%20is,where%20the%20variable%20gets%20stored.):" – Xfce4 Mar 04 '22 at 09:14
  • @4386427 My mistake. The paragraph says for variables, decleration and definition is the same thing. I missed that part and considered variables would be the same as the functions in that sense. – Xfce4 Mar 04 '22 at 09:38
  • @4386427 I meant "for a variable, decleration and definition are the same thing (can't be separated), while they can be separated for functions". I think prejudice prevents people understand me at times. This happens to me often. Maybe I do the same to others. – Xfce4 Mar 04 '22 at 11:16
  • 1
    @4386427: At file scope, `int var;` is a *tentative definition*, not a *definition*. In spite of its name, a tentative definition is not a type of definition in the C standard, although it can result in a definition being created, depending on circumstances. – Eric Postpischil Mar 04 '22 at 12:13

1 Answers1

1

In C, every definition is a declaration. Some declarations are definitions, and some are not.

The page you link to, has mistakes. One is it says that “Declaration of a variable or function simply declares that the variable or function exists somewhere in the program, but the memory is not allocated for them.” Actually, that is two mistakes, at least. Per C 2018 6.7 5, “A declaration specifies the interpretation and attributes of a set of identifiers…” This does not necessarily mean any object or function with the declared name actually exists in the program. For example, if we include the <stdio.h> header, the fputc function is declared, but, if we never use it, it might never be linked into the program, and so no such function might exist in the program, even in the file executable file. A declaration that is not a definition only tells the compiler about the name (which we call the identifier); it does not convey information about whether something exists.

A second mistake in that sentence is that it says memory is not allocated for the declared variable or function. This is incorrect because each definition is a declaration. C 2018 6.7 5 continues “… A definition of an identifier is a declaration for that identifier that: — for an object, causes storage to be reserved for that object; — for a function, includes the function body…” For example, int x = 3; is a declaration of x, and it is a definition of x, and it causes memory to be allocated for x, but the page you link to says a declaration does not cause memory to be allocated. So the page is wrong.

The rules regarding extern and what is or is not a definition are complicated because C was developed by multiple people doing different things with it. When C was standardized, the committee had to reconcile different practices. As a consequence, there is no single rule for extern.

A note about terminology: A variable is actually two things: an identifier (its name) and an object (the memory that stores a representation of its value).

In your CODE 1, the linked page’s Example 3, extern int var; is a declaration that is not a definition. There is no definition. If an identifier with external linkage is used in an expression, there must be exactly one external definition of it in the program (by C 2018 6.9 5). Since there is no external definition, the linker complains.

There are several answers mentioning that var inside main is a local variable.

If an identifier is declared inside a block, such as the { … } that forms the body of a function, then effects of the declaration are local to that block. If an identifier is declared outside of any function, the effects of the declaration continue through the rest of the file being compiled, except where they are hidden by a nested declaration.

If var is declared outside of any function and before main, and then we use that identifier inside main without declaring it again, that use of var refers to the previous declaration. It does not create or refer to a new local object named var.

But why would it not be a global variable at the same time.

At any one point in source code, an identifier refers to at most one thing.

.. the code below works:

// File 2 var = 4

var = 4 is not proper C code because it does not have a semicolon. If it were var = 4;, some compilers might accept this outside a function, but it is archaic. In modern C, it should have a type, such as int var = 4;. That would then be a proper definition of var. If your compiler accepted var = 4; outside a function, you should be aware it is accepting old code, and it would be preferable to use switches to tell the compiler to require modern C code.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Thank you very much for your detailed answer. By the way I think what they mean in the sentence "memory is not allocated for the declared variable or function" is "a variable or function which is declared only and not defined". – Xfce4 Mar 04 '22 at 13:47
  • By the way, I had thought that the compiler accepted `var = 4;` because the variable was already declared in another file with `extern`. So the reasonthat the compiler accepted is something different, i.e. due to the old standards of C? – Xfce4 Mar 08 '22 at 09:55
  • 1
    @Xfce4: First, even if `var` is declared in another file, a compiler generally does not know that when it is compiling this file. Unless there is a declaration of `var` in the file being compiled or a file it includes with `#include`, the compiler does not know about it. Second, in the modern C grammar, `var = 4;` is a statement, not a declaration. `int var = 4;` would be a statement. And, outside functions, the grammar only allows declarations, not statements. So a compiler accepting only modern C would not accept `var = 4;` outside a function even if `var` has been declared. – Eric Postpischil Mar 08 '22 at 12:01