-
I have been trying to understand how definition is different from declaration. I can't figure out difference between declaration and definition
My code
#include<stdio.h>
void main()
{
int x; // I have declared variable x
int y = 10; // I have declared variable y and assigning value 10 to y
}
-
It's the same as the difference between a car, and a picture / registration sheet of a car.
Variable actually exists when it's put in memory somewhere. This is called definition.
You can tell the compiler that variable exists somewhere, and tell about its type, so compiler knows how to use it. This is called declaration.
In your example, both are definitions (x and y are actually created there), so no wonder you can't figure it out.
An example of declaration only:
extern int x;
-
Variable actually exists when it's put in memory somewhere. This is called definition.
You can tell the compiler that variable exists somewhere, and tell about its type, so compiler knows how to use it. This is called declaration.
I have define variable x in temp.c file and I am trying to access value of variable in hello.c
hello.c
#include<stdio.h>
int main ()
{
int x;
printf( "x = %d", x);
return 0;
}
temp.c
#include<stdio.h>
extern int x;
x = 10;
output
I get warning and wrong result
gcc -o hello hello.c temp.c
temp.c:5:1: warning: data definition has no type or storage class
x = 10;
^
temp.c:5:1: warning: type defaults to 'int' in declaration of 'x' [-Wimplicit-int]
x = 4194432
-
Your code has no (valuable) effect. It just prints the (uninitialized) value of a local variable x.
That is different from the global variable x (that the compiler is allowed to optimize away since its never accessed) that you defined in the other file.
That is even an errror since referencing a variable with no value yields UB.
-
Your code has no (valuable) effect. It just prints the (uninitialized) value of a local variable x.
That is different from the global variable x (that the compiler is allowed to optimize away since its never accessed) that you defined in the other file.
making global variable is also give the same result
#include<stdio.h>
int x;
int main ()
{
printf( "x = %d", x);
return 0;
}
gcc -o hello hello.c temp.c
temp.c:5:1: warning: data definition has no type or storage class
x = 10;
^
temp.c:5:1: warning: type defaults to 'int' in declaration of 'x' [-Wimplicit-int]
-
Now you attempt to do an assignment in global scope (outside of any function body).
When would you expect that code to be executed?
-
Change the declaration in temp.c to a definition:
int x=10;
or even: extern int x=10;
get rid of that illegal attempt at an assignment outside of any function, which the compiler is flagging: warning: data definition has no type or storage class
and add an extern declaration for it in main.c:
#include<stdio.h>
extern int x; // this one refers to the x defined in temp.c
int main ()
{
// int x; // don't declare X here - you don't want a local
printf( "x = %d", x);
return 0;
}
and it will 'just work'
It gets more complex when you want to create a header to declare all your variables - you probably should read: https://www.gamedev.net/tutorials/_/technical/general-programming/organizing-code-files-in-c-and-c-r1798/ (https://www.gamedev.net/tutorials/_/technical/general-programming/organizing-code-files-in-c-and-c-r1798/)
Edit: AAGH! Gamedev.net have borked all their sourcecode formatting, by dropping the <pre> tags, which of course strips the newlines. Unfortunately Archive.org don't seem to have a non-borked version.
-
and it will 'just work'
Thank you It works
hello.c
#include<stdio.h>
extern int x; // x defined in temp.c
int main ()
{
printf( "x = %d", x);
return 0;
}
x = 10
temp.c
#include<stdio.h>
int x = 10; // x defined in temp.c
-
code to access function that is defined in temp.c file
hello.c
#include<stdio.h>
extern int foo(); // foo defined in temp.c
int main ()
{
int y = foo();
printf( "y = %d", y);
return 0;
}
temp.c
#include<stdio.h>
int foo()
{
int z = 1;
z++;
return z;
}
y = 2
-
All function definitions in C are externally visible by default, and all function declarations are implicitly extern. Unlike variables etc. you don't need to use the extern keyword for them. The only type of function that isn't externally visible is one defined as static, which is limited to the scope of the single file where it is defined.
The rules in C++ are rather different and far more complex, but the vanilla 'C' subset of it is essentially the same.
N.B. use of extern in a declaration doesn't mean that the definition cant be in the same source file.
-
get rid of that illegal attempt at an assignment outside of any function, which the compiler is flagging: warning: data definition has no type or storage class
That is not illegal to have [what looks like] an assignment outside of a function [but is actually a definition-with-initializer].
That ends up being the actual definition of the storage for the variable (in the DS segment), which the linker will resolve all global variables defined without an initializer (tentative definitions) with at most one global variable definition with an initializer to be a single storage location, more simply said "they will be the same variable." (If multiple definitions exist with an initializer, it's a linker error, even if all of them are the same initial value.)
If all [whether one or more] definitions are tentative definitions (without a value specified), the variable will be created with a 0 value (in the BSS segment) and the linker will collapse the one or more of them to the same storage location.
-
C is indeed illogical - keyword extern is used to declare (and not define) a variable. But for a function, one can of course follow the same logic and declare
extern void function(int arg);
... but this extern keyword is optional, because the language knows it's only a declaration (not a definition) from the lack of function body ({ ... }) (there is semicolon instead).
-
If you are declaring a variable inside a function, the question makes no sense. The distinction exists only if the item can be used in a place where it is not defined. I would suggest not thinking about that as some opposing concepts either. Those are not two ends of a single idea, but two separate concepts that are only related to each other.
First, you can define something. By that you are providing a concrete definition, a concrete instance. Using a car analogy someone mentioned earlier, you are building an actual car.
Now imagine you must tell about that car to someone, who has never seen it and the car is not anywhere around. That is the declaration. You are telling the compiler that there exists something with such and such features. So the compiler knows it exists, and you can refer to it. But it is not actually creating anything.
A situation like that arises commonly in two cases. First, if there are multiple compilation units and they must share some definitions. Only one compilation unit contains the definition. But others, which use it, must somehow know that definition exists and what it is. For those units you provide a declaration. For example if you have function doSomething in compilation unit fun.c, and you want main.c to use it, main.c must be told such a thing exists:// fun.c
#include <stdio.h>
// Definition of `doSomething`
void doSomething(int x) {
printf("Do something: %d\n", x);
}
// main.c
void doSomething(int); // Declaration of `doSomething`
int main(void) {
doSomething(12);
return 0;
}
The declaration tells compiler, while it compiles “main.c”, that there is some function called doSomething, which accepts a single int argument and returns nothing. It doesn’t tell what it does, how it is implemented or where to find it. It just provides minimum information required for the later invocation to be syntactically valid.
Similarly, for variables:// var.c
int x = 12; // Definition of `x`
// main.c
#include <stdio.h>
extern int x; // Declaration of `x`
int main(void) {
printf("x = %d\n", x);
return 0;
}
Another case is a single compilation unit, where e.g. functions are referencing each other. Obviously the earlier one will not know about the latter one. For this reason the latter one must be declared earlier, so it is known to the compiler.
The situation with extern and static is a bit complicated, because of C’s history. Basically it’s a mix of shorthand notation, where linkage is implied, and assumptions from decades ago. It’s not illogical: it’s just permitting things to be not explicitly stated, with the defaults being defined by ancient conventions.
-
Have I used header file correctly ?
hello.c
#include<stdio.h>
#include"temp.h"
int main ()
{
printf( "x = %d", x);
return 0;
}
temp.h
extern int x;
temp.c
#include<stdio.h>
int x = 5;
output
x = 5
-
Yes, though it’s typical practice to also include temp.h from temp.c as well. (Not needed for this toy example, but extremely common in actual code.)
-
Probably just me, but I've always felt that C uses the keywords "definition" and "declaration" backwards. Every time I read one, it automatically feels like the intent of the other. To "define" something doesn't necessarily create it, but "declaring a variable" sure sounds like you're calling it into existence. At least to me.
-
I have compiled following program and it gives output.
hello.c
#include<stdio.h>
#include"temp.h"
int main (void)
{
printf("%d\n", get_number());
return 0;
}
temp.c
#include "temp.h"
int get_number(void)
{ int x = 5;
return x;
}
I understand both hello.c and temp.c file but I don't understand what's happens in temp.h file
temp.h
#ifndef _OTHER_H_
#define _OTHER_H_
int get_number(void);
#endif
-
I understand both hello.c and temp.c file but I don't understand what's happens in temp.h file
temp.h
#ifndef _OTHER_H_
#define _OTHER_H_
int get_number(void);
#endif
Think of that expression as saying, "Hey compiler, there is or will be a function <somewhere> that takes no parameters '(void)', returns an integer 'int', and is called 'get_number'. So, when you see someone calling a function get_number, ensure that they pass it no parameters."
Then, in a later stage, after all the files have been individually compiled and are ready to be assembled together into a single program by the linker, the linker has to find the function get_number(void) somewhere, but that's at a later stage.
One thing to put into your mental model is that all of the .c files are compiled individually* and the compiler can only "see" and "use" information that is in those .c files (after the pre-processor runs, which is what includes the text of the included .h files). So, it has to know the declaration of the get_number function in order to compile the call to it in main in hello.c.
Minor tip: I would change _OTHER_H_ to _TEMP_H_.
* This is a slight hand-wave, but it's sufficient for this thread.
-
Probably just me, but I've always felt that C uses the keywords "definition" and "declaration" backwards. Every time I read one, it automatically feels like the intent of the other. To "define" something doesn't necessarily create it, but "declaring a variable" sure sounds like you're calling it into existence. At least to me.
In C, keywords are more often than not doing something else than what their names imply. Static isn't static most of the time, const isn't const (it can even be volatile), there's nothing automated with auto, restricted pointers aren't restricted at all (but even more free than all others) and if register lives in a register is solely the compiler's choice (not yours).
You just need to live with that.
-
Minor tip: I would change _OTHER_H_ to _TEMP_H_.
wouldn't recommend leading underscores at all as they are reserved to the compiler and standard library implementation.
-
In C, keywords are more often than not doing something else than what their names imply. Static isn't static most of the time, const isn't const (it can even be volatile), there's nothing automated with auto, restricted pointers aren't restricted at all (but even more free than all others) and if register lives in a register is solely the compiler's choice (not yours).
You just need to live with that.
LOL, but that's sadly so true
-
Minor tip: I would change _OTHER_H_ to _TEMP_H_.
wouldn't recommend leading underscores at all as they are reserved to the compiler and standard library implementation.
TIL something about the C standard. Thanks!
-
And just use
#pragma once
at the beginning of the header file.
Every compiler has supported it for at least 2-300 years. Write less boilerplate, make fewer typos.
-
#pragma once is not part of the language standard. Whether that matters to you depends on the project circumstances.
-
C is indeed illogical - keyword extern is used to declare (and not define) a variable. But for a function, one can of course follow the same logic and declare
extern void function(int arg);
... but this extern keyword is optional, because the language knows it's only a declaration (not a definition) from the lack of function body ({ ... }) (there is semicolon instead).
There's something called 'forward declaration' (also 'function prototype', required by C99), i.e.:
void Function2(void);
void Function1(void)
{
...
Function2();
...
}
void Function2(void)
{
...
}
An "extern void Function2(void);" would tell the compiler/linker that Function2() is in another file.
Edit: Since most compilers take any function declaration with an implicit 'extern' the statement above can be ignored. Because this contradicts the explicit use of 'extern' for variables some software developers consider it a flaw.
-
An "extern void Function2(void);" would tell the compiler/linker that Function2() is in another file.
What makes you think that?
This time I'll refrain from quoting the standard.
Just consider this: when you include a file, its text is for all purposes inlined in the including file.
If what you say were true, the common practice of including xxx.h from xxx.c (as sokoloff suggests (https://www.eevblog.com/forum/programming/cant-figure-out-difference-between-declaration-and-definition/msg4621408/#msg4621408)) would not work!
It is, indeed, common practice because it helps with catching errors such as having incompatible declarations and definitions of an object or function: as translation units are compiled separately, it's the only way the compiler can tell you that (e.g.) the function declaration in the .h has a different signature than the one in its definition in the .c.
In general, linkers will happily ignore the problem, unless link time optimization is used.
-
Consider this thought experiment, to clarify the difference between declaration and definition:
You have a data structure, a network or graph, consisting of two different types of nodes.
Each node type can refer to four nodes of the same type, and two nodes of the different type.
How do you define the structures? When you define the first one, the other one is still undefined, so how do you refer to that?
We do that by declaring the structures before we define them:
struct one;
struct two;
struct one {
struct one *ones[4];
struct two *twos[2];
// Contents of the node omitted
};
struct two {
struct two *twos[4];
struct one *ones[2];
// Contents of the node omitted
};
We can do the same by defining the two node types first (node_one and node_two, becoming new variable types) based on as yet undefined structure types, which also declares the structure types:
typedef node_one struct one;
typedef node_two struct two;
struct one {
node_one *ones[4];
node_two *twos[2];
// Contents of the node omitted
};
struct two {
node_two *twos[4];
node_one *ones[2];
// Contents of the node omitted
};
Similarly, we can declare a function before we define it, so that two functions can call each other.
The C standard defines declaration and definition in for example C11 6.5p5 as
A declaration specifies the interpretation and attributes of a set of identifiers.
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 an enumeration constant, is the (only) declaration of the identifier;
— for a typedef name, is the first (or only) declaration of the identifier.
-
An "extern void Function2(void);" would tell the compiler/linker that Function2() is in another file.
What makes you think that?
This time I'll refrain from quoting the standard.
Just consider this: when you include a file, its text is for all purposes inlined in the including file.
If what you say were true, the common practice of including xxx.h from xxx.c (as sokoloff suggests (https://www.eevblog.com/forum/programming/cant-figure-out-difference-between-declaration-and-definition/msg4621408/#msg4621408)) would not work!
Yes, and no. ;D Most C variants/versions/compilers take any function declaration with an implicit 'extern' (this was mentioned before). To override this you'd have to add a 'static' to make a function local only. However, some people think that this auto-extern is a flaw as it contradicts the explicit use of 'extern' for variables. So there are also C compilers following that idea. For better portability and readability I prefer to add an explicit 'extern' to function declarations for functions outside of the local file.
-
Well, no standard compliant compiler should do that.
Then if if we are talking about a language that is almost, but not quite, entirely unlike C all bets are off.
BTW, I also use extern for function declarations in .h files, but this is just my personal taste for consistency.
-
... So there are also C compilers following that idea...
I would suppose these compilers (never came across such) must be pretty old as such behaviour would be violating the standard.
Regarding the (apparently) superflous use of the extern keyword with function declarations: there is one situation (yet another inconsistency in the C standard) where it matters: extern does matter in conjunction with inline.
An inline function by default does *not* have extern linkage, i.e. it can't be called from outside the current translation unit. If there is an additional function declaration visible that mentions extern (or is missing inline), an additional incarnation of the function is generated that *can* be called from other translation units (note that gcc does not strictly adhere to the standard here).
-
We do that by declaring the structures before we define them:
struct one;
struct two;
struct one {
struct one *ones[4];
struct two *twos[2];
// Contents of the node omitted
};
struct two {
struct two *twos[4];
struct one *ones[2];
// Contents of the node omitted
};
We can do the same by defining the two node types first (node_one and node_two, becoming new variable types) based on as yet undefined structure types, which also declares the structure types:
typedef node_one struct one;
typedef node_two struct two;
struct one {
node_one *ones[4];
node_two *twos[2];
// Contents of the node omitted
};
struct two {
node_two *twos[4];
node_one *ones[2];
// Contents of the node omitted
};
Well, for the sake of completeness, in cases like that one doesn't have to pre-declare struct types in C. When you refer to a yet-unknown struct type in C, the language always treats it as a declaration of a new type. So, in the first version of the code you don't need to make those initial `struct` declarations at all. The following is valid and will work as intended as well (i.e. it is equivalent to your version):
/* Types `struct one` and `struct two` are unknown at this point */
struct one {
struct one *ones[4];
struct two *twos[2];
/* This is fine. In addition to declaring a field it also
introduces an incomplete file-scoped type `struct two` */
};
/* The following is a complete declaration of the same `struct two`
introduced above */
struct two {
struct two *twos[4];
struct one *ones[2];
};
When introducing new types on-the-fly one has to pay attention to scoping rules, but in this example everything is fine. I personally consider it a good practice to pre-declare types (and follow the style from your second example), but still...
-
However, some people think that this auto-extern is a flaw as it contradicts the explicit use of 'extern' for variables. So there are also C compilers following that idea.
That's a rather strange way to perceive it.
Everywhere in C external linkage is a default for file-scope variables and functions. There's never a need to use `extern` keyword, aside from a strange niche case with turning a variable definition into a non-defining declaration when declaring global variables. Moreover, most C compilers support a popular historical non-standard extension, when it is OK to provide multiple definitions of a global variable without an initializer scattered across different translation units. This extension basically makes explicit `extern` completely unnecessary.
And, on the contrary, some people consider this a flaw: that this latter detail is non-standard and that we still have to sometimes use `extern` with variables. And even though it is a non-standard extension, it does emphasize the fact that the language is tailored towards avoiding explicit `extern`. And that is what most people normally do: avoid using `extern` entirely.
(Note: since C99 explicit `extern` has another niche usage: with `inline` functions. But that is a fairly different topic.)
For better portability and readability I prefer to add an explicit 'extern' to function declarations for functions outside of the local file.
It has zero effect on portability. And it has a pronouncedly negative impact on readability, by introducing a lot of unnecessary clutter.
I do see habitual use of redundant `extern` on function declarations in some code from time to time. But most of the time it just an ugly cargo-cult followed by incompetent people: some people simply don't know that it is unnecessary. It it very similar to other popular cargo-cults, like casting results of memory allocation functions.
-
Well, for the sake of completeness, in cases like that one doesn't have to pre-declare struct types in C.
Sshhhh, quiet! ;D
The only other example I could come up with was one using two functions to experiment with the Collatz conjecture, but it was nowhere as simple. (And the single recursive function one is also much cleaner.)
Because you broke my believability, I now demand you to come up with a good example of the difference between declarations and definitions! 8)
When intoducing new types on-the-fly one have to pay attention to scoping rules, but in this example everything is fine. I personally consider it a good practice to pre-declare types (and follow the style from your second example), but still...
Yeah, and it's not like any compilers I've used in the past required the declarations beforehand.. It's only for us humans that I do things like that.
-
Moreover, most C compilers support a popular historical non-standard extension, when it is OK to provide multiple definitions of a global variable without an initializer scattered across different translation units.
[...]
I do see habitual use of redundant `extern` on function declarations in some code from time to time. But most of the time it just an ugly cargo-cult followed by incompetent people: some people simply don't know that it is unnecessary. It it very similar to other popular cargo-cults, like casting results of memory allocation functions.
I'm glad gcc has finally stopped doing that since version 10 (still available using an option, for old - wrong - code).
This makes using extern in with variable declarations mandatory in case one's accessing them from another translation unit.
On the second point: yes, I know perfectly well that it's not needed for functions.
But, given the above, I prefer consistency - the visual clutter does not annoy me (in this case) - call it a (bad) habit, but now I'll climb back in my bamboo control tower.
-
Completely equivalent, regardless of which file and scope they are in:
extern void func(int arg);
void func(int arg);
Contrary to that, these are not equivalent:
extern int val;
int val;
Former is 100% surely only a declaration. Latter is usually also a definition, but as explained by TheCalligrapher above, most compilers allow globals to be "defined" multiple times, combining them into just one definition. I don't like it, so I use this pattern:
module1.h
extern int module1_public_thing;
module1.c
int module1_public_thing;
module2.c
#include "module1.h"
void something()
{
int thing = module1_public_thing + 42; // access it here
}
But only rarely you directly share globals like this. errno would be one typical example.
-
For sure, a function prototype just declares the prototype. It doesn't require any kind of 'extern' qualifier, and adding one usually shows a poor understanding of the C language.
As to global variables, multiple definitions are horrible, and recent versions of GCC (and I guess Clang has followed) make them deprecated by default (you need to set a specific compiler option to enable them.) So only one definition (ideally in the source file where it makes most sense) and then 'extern' declarations to access them elsewhere is the way to go IMHO.
-
The C language standard calls this second one a "tentative definition" (C99 6.9.2/2)
A declaration of an identifier for an object that has file scope without an initializer, and
without a storage-class specifier or with the storage-class specifier static, constitutes a
tentative definition. If a translation unit contains one or more tentative definitions for an
identifier, and the translation unit contains no external definition for that identifier, then
the behavior is exactly as if the translation unit contains a file scope declaration of that
identifier, with the composite type as of the end of the translation unit, with an initializer
equal to 0.
-
For sure, a function prototype just declares the prototype. It doesn't require any kind of 'extern' qualifier, and adding one usually shows a poor understanding of the C language.
Not necessarily - one can use the keyword to communicate the intent to a human reader. Then it's more a question about taste than understanding of the language.
I use it from time to time. When function prototype is in a header, or in the global file scope of a .c file, obviously there will be no extern keyword added. But then, if I need to call one odd function from somewhere as a quick hack, and don't feel like adding the declaration into a relevant .h file, I might just declare the function right before calling it, at the function scope / block scope. In such case, adding extern underlines the fact this weird looking thing in the middle of code is a declaration:
void func()
{
if(asdf == 0x42)
{
extern void self_destroy();
self_destroy();
}
}
Not suggesting anyone to do this, but it communicates to the human reader: this exists somewhere and I forgot where; call it right now anyway. It also communicates this won't be used anywhere else, because then I would have added it to a header properly :).
-
As to global variables, multiple definitions are horrible, and recent versions of GCC (and I guess Clang has followed) make them deprecated by default (you need to set a specific compiler option to enable them.) So only one definition (ideally in the source file where it makes most sense) and then 'extern' declarations to access them elsewhere is the way to go IMHO.
This was another TIL.
It came in gcc 10: https://gcc.gnu.org/gcc-10/porting_to.html#common
-
I don't like it, so I use this pattern....
I do exactly this same thing. Eliminates confusion and doesn't rely on one-off compiler quirks.
-
For better portability and readability I prefer to add an explicit 'extern' to function declarations for functions outside of the local file.
It has zero effect on portability. And it has a pronouncedly negative impact on readability, by introducing a lot of unnecessary clutter.
I do see habitual use of redundant `extern` on function declarations in some code from time to time. But most of the time it just an ugly cargo-cult followed by incompetent people: some people simply don't know that it is unnecessary. It it very similar to other popular cargo-cults, like casting results of memory allocation functions.
I love to add lots of clutter, like comments explaining things. Makes long term maintenance much easier. The argument that clutter implies incompetence is a preconception and simply nonsense. In many cases it's done intentionally to convey a hint or message, either for myself or for someone else to help writing or maintaining the code. Not long ago this practice was considered a good coding style and was tought to students.
BTW, when I'm dealing with clutter-free code I often see poor algorithms and other strange things, just my personal funny experience.
For sure, a function prototype just declares the prototype. It doesn't require any kind of 'extern' qualifier, and adding one usually shows a poor understanding of the C language.
Same preconception as above. :( Just because you prefer a clutter-free coding style doesn't mean that any other style is a sign of incompetence.
-
Indeed, one could always use extern keyword for all declarations (not making difference between variables and functions); that would be highly logical. I don't agree following a logical style rule is incompetence. Of course, random sprinkling of extern here and there suggests either incompetence or some kind of attention problem.
The only problem is the poor name choice for the keyword, but same can be said with many other keywords; static is even worse. We just need to live with these and learn the true meanings, and assume others working with C also learn them.
And I don't suggest anybody do this:
#define declare extern
#define private static
#define public
Think how nice it would be if we had a microcontroller-oriented language with no reserved words so anyone could come up with their own keywords in their own language! :D
-
To the OP, the use of the terms declare or define get somewhat muddy, especially in other languages and with other programmers from different non C backgrounds.
I would for argument say that we declare a named space in memory that is used either a variable or a constant, and then define it's type. After-which we use arguments to initialise the value - or values - for the type. Of course, that declared value has scope within the code. When the value goes out of scope, the space in memory is destroyed; a state which some programmers refer to as the value becoming undeclared. ???
-
I would for argument say that we declare a named space in memory that is used either a variable or a constant, and then define it's type. After-which we use arguments to initialise the value - or values - for the type.
Please don't confuse the OP with such BS. Basically everything was wrong with this.
-
The only problem is the poor name choice for the keyword, but same can be said with many other keywords; static is even worse. We just need to live with these and learn the true meanings, and assume others working with C also learn them.
Agreed. There are a few things in any professional field where "that's how it's been for 50+ years and you're not going to be the one we change it for, so you better just learn it." (That's not to say anything bad about OP or the thousands of other devs who initially struggle with this, but realistically, it's something that takes an hour or so to learn and then is entirely second nature by the second month you're working on any codebase larger than a solution to an academic problem set.
And I don't suggest anybody do this:
#define declare extern
#define private static
#define public
Certainly not if you're going to go on to learn c++ or switch back and forth between them!
-
I'm glad gcc has finally stopped doing that since version 10 (still available using an option, for old - wrong - code)
yup, otherwise you cannot re-compile gcc-v2.95(1) (the last one that *actually* works for 88k) with host_gcc >= { v10, v11, v12, ... }
Yesterday I had two minutes of full solid panic on my HPPA, before reading the manual and finding that "holy retro-option".
(1) the same apply for all the old part of Haiku still based (don't ask my why?) on gcc-v2.95, hence even modern gcc-v10 toolchains are multi-library-multi-cc1/cc1pp (c,c++) hybrid.
Life is ... not easy :-//
-
I would for argument say that we declare a named space in memory that is used either a variable or a constant, and then define it's type. After-which we use arguments to initialise the value - or values - for the type.
Please don't confuse the OP with such BS. Basically everything was wrong with this.
Explain? Or are you such an expert that you have never have to?
-
I would for argument say that we declare a named space in memory that is used either a variable or a constant, and then define it's type. After-which we use arguments to initialise the value - or values - for the type.
Please don't confuse the OP with such BS. Basically everything was wrong with this.
Explain? Or are you such an expert that you have never have to?
It's already all in this thread. Declaration does not reserve space in memory, definition does, that went the wrong way. Declaration does give the type, that's the whole point. Definition does not define any more type than declaration already does. And initializer is not argument.
If you swap definition and declaration, you are closer, but still not there.
When every point made is completely wrong, it is easier just to state this fact instead of enumerating every point separately, but here you are anyway.
-
Because you broke my believability, I now demand you to come up with a good example of the difference between declarations and definitions! 8)
"Declarations and definitions" of what specifically? Your original example looks like an attempt to illustrate the difference between "declarations and definitions" for struct types in C. However, terminologically this is not applicable to C at all. C language does not support the concept of "definition" for struct types. So, I can't really come up with an example for struct types. Pedantically speaking, it is impossible,
In C the term "definition" is only encountered in conjunction with: object definitions (i.e. definitions of variables), function definitions, type definitions (refers to `typedef` specifically) and macro definitions.
A declaration of a struct type is always a declaration, never a definition:
struct A;
/* A declaration of incomplete type `struct A`.
Sometimes informally referred to as "forward declaration" */
struct B /* A declaration of complete type `struct B` */
{
int x, y;
};
It was C++ that decided to change things drastically, and separated such class declarations into "declarations" and "definitions". In C++ the first declaration above is a "declaration" ("just a declaration"), while the second one is a "definition". But this is C++ specific.
-
The C language standard calls this second one a "tentative definition" (C99 6.9.2/2)
Tentative definitions is a perfectly standard feature of C language, which "works" within one translation unit. It is a feature that permits you to specify multiple definitions of the same variable within the same translation unit
/* File scope */
int a;
int a;
int a, a, a, a, a;
The above is OK in C because these are tentative definitions. There's nothing non-standard about the above.
However, this feature is, again, completely isolated within each translation unit. It has no "external" effects. It has nothing to do with the fact that some compilers permit you to define the same variable in multiple translation units. This permission has nothing to do with tentative definitions. This permission is a non-standard extension. It is popular mistake to just explain this behavior as an example of "tentative definitions".
(One can probably suggest that this extension is a "natural side effect" of tentative definitions... But in reality it is really not as "natural" as it might seem at the first sight.)
-
Because you broke my believability, I now demand you to come up with a good example of the difference between declarations and definitions! 8)
"Declarations and definitions" of what specifically?
The target here is to provide OP with an intuitive understanding of the difference between 'declaration' and 'definition'.
In my post, I provided the short snippet from the C11 standard that best describes my own intuitive approach.
I was hoping you could think of something both technically correct, and easily understood. (As you know, we tend to have a bit different approach to these things. The different viewpoints might yield different ideas for what is a suitable example here.)
Here's the Collatz conjecture example:
We want to experiment with the Collatz conjecture (https://en.wikipedia.org/wiki/Collatz_conjecture), printing '^' for each up+down step pair (x ← 3x+1 followed by x ← x/2) and 'v' for each down step (x ← x/2), plus the number of steps needed to reach 1.
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
// We declare the two functions so that in their definition, they can recursively call each other.
// It is common to include the types of parameters, but omit the parameter names.
static uintmax_t collatz_up(FILE *, uintmax_t);
static uintmax_t collatz_down(FILE *, uintmax_t);
// The actual function.
uintmax_t collatz(FILE *out, uintmax_t x)
{
uintmax_t n;
if (x < 2) {
// x == 0 is invalid, and x == 1 is zero-length sequence.
n = 0;
} else
if (x & 1) {
// x is odd, so we go up.
n = collatz_up(out, x);
} else {
// x is even, so we go down.
n = collatz_down(out, x);
}
if (out) {
fputc('\n', out);
}
return n;
}
static uintmax_t collatz_up(FILE *out, uintmax_t x)
{
// Since x is odd, (3*x) is odd, and (3*x+1) is even.
// Thus, each ^ step is always followed by a v step,
// and we actually have (3*x+1)/2 = x + (x+1)/2.
if (out) {
fputc('^', out);
}
uintmax_t new_x = x + (x + 1)/2;
if ((uintmax_t)((2*new_x - 1) / 3) != x) {
// Overflow occurred
if (out) {
fputc('!', out);
}
return 0;
}
if (new_x < 2) {
return 2;
} else
if (new_x & 1) {
return 2 + collatz_up(out, new_x);
} else {
return 2 + collatz_down(out, new_x);
}
}
static uintmax_t collatz_down(FILE *out, uintmax_t x)
{
if (out) {
fputc('v', out);
}
uintmax_t new_x = x / 2;
if (new_x < 2) {
return 1;
} else
if (new_x & 1) {
return 1 + collatz_up(out, new_x);
} else {
return 1 + collatz_down(out, new_x);
}
}
int main(int argc, char *argv[])
{
const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
fprintf(stderr, " %s N [ N ... ]\n", arg0);
fprintf(stderr, "\n");
fprintf(stderr, "This outputs the number of Collatz conjecture steps from N to 1\n");
fprintf(stderr, "to standard output, after printing a ^ for each up+down step pair\n");
fprintf(stderr, "and v for each down step, to standard error.\n");
fprintf(stderr, "\n");
return (argc == 2) ? EXIT_SUCCESS : EXIT_FAILURE;
}
for (int arg = 1; arg < argc; arg++) {
uintmax_t val, n;
char *end = argv[arg];
errno = 0;
val = strtoumax(end, &end, 0);
if (errno || end == argv[arg] || *end != '\0' || val < 1) {
fprintf(stderr, "%s: Invalid N.\n", argv[arg]);
return EXIT_FAILURE;
}
printf("%ju: ", val);
fflush(stdout);
n = collatz(stderr, val);
fflush(stderr);
printf("%ju steps\n", n);
fflush(stdout);
}
return EXIT_SUCCESS;
}
If we compile that into ./ex, using for example gcc -Wall -O2 above.c -o ex, then running
./ex 1161 703
outputs
1161: ^v^^^v^^^^^^v^^^^^^v^vvv^v^^v^^vvv^vv^vv^^v^v^vv^^^^^vvv^^v^^^v^^^^v^vv^^^v^^v^^^^^^vv^^^^vvv^v^v^vvv^vv^^^vvvv^vvv
181 steps
703: ^^^^^^v^v^^^^^^v^^v^v^^^vv^vvv^^^^v^^^^^v^^^v^^^vv^v^vv^^^v^^^^vvvv^^^^^vvv^v^vvvvv^vv^^v^^vvv^^vv^^v^vv^vvv
170 steps
Alternatively, ./ex 1161 703 >/dev/null shows only the standard output part:
1161: 181 steps
703: 170 steps
This is not the most sensible way to implement such a Collatz conjecture exploration program, but it shows how declaring things that are defined later, works.
The declaration itself does not generate any code or reserve any memory, it only tells the compiler enough to know how to refer to/access/call that thing.
The definition is what generates code or reserves memory.
-
For sure, a function prototype just declares the prototype. It doesn't require any kind of 'extern' qualifier, and adding one usually shows a poor understanding of the C language.
Not necessarily - one can use the keyword to communicate the intent to a human reader. Then it's more a question about taste than understanding of the language.
Ah, coding style. It's like colors. Very subjective.
To me it doesn't convey any additional meaning, as a prototype with or without 'extern' is exactly the same thing (as opposed to variables!). But I can understand why it could for some other people. Just down to style and habits.
The problem I was pinpointing, as far as style goes, is that it can be either a sign of poor understanding of C (so a kind of cargo cult programmig), or something intentional (as with what you said here, although the conveyed intention, while clear to you, may not be to others.) So any coding style that can ambiguously show either cargo cult or explicit intention is not really my favorite. But that again has a pretty subjective side to it.
-
For sure, a function prototype just declares the prototype. It doesn't require any kind of 'extern' qualifier, and adding one usually shows a poor understanding of the C language.
Not necessarily - one can use the keyword to communicate the intent to a human reader. Then it's more a question about taste than understanding of the language.
Can you describe that intent in more specific terms? What spectifically does it communucate?
-
Can you describe that intent in more specific terms? What spectifically does it communucate?
I already did in reply #37. It might be bad taste, but there you are, anyway.
Or, one could always use extern in all function declarations. Then it would communicate: "I'm a logical thinker and prefer explicit over implicit, even when language rules are clear about the implicit. Because variables need to be explicitly declared using extern, I follow the exact same logic for functions, using the exact same keyword." I don't do that myself, but I would totally understand if someone did that.
-
Or, one could always use extern in all function declarations. Then it would communicate: "I'm a logical thinker and prefer explicit over implicit, even when language rules are clear about the implicit. Because variables need to be explicitly declared using extern, I follow the exact same logic for functions, using the exact same keyword." I don't do that myself, but I would totally understand if someone did that.
I don't understand this "implicit vs. explicit" logic when it comes to "import" semantic of `extern` keyword.
When it is used with variable declarations, it is not about "implicit vs. explicit" at all. It is used to turn a variable definition into a non-defining declaration. It is a fundamental change, not a case of "implicit vs. explicit". C does not offer "prototypes" for variables. That's why we are forced to use `extern` with variables when we want to make a non-defining declaration.
When it is used with function prototypes, it changes nothing, but I still don't get the "implicit vs. explicit" part. There's nothing "implicit" about function prototype. The only purpose function prototype serves is to provide a non-defining declaration for a function. It is already as explicit as it gets. Adding `extern` does not make it more explicit.
The above also illustrates that variables significantly different from functions w.r.t. pre-declaration methods offered by the language. Why would one want to "follow the exact same logic" for such drastically different things? Using `extern` in both contexts does not bring about any kind of uniformity: function declarations and variable declarations are still fundamentally different.
---
The only context where `extern` can be used to make something explicit is its "export" semantics, i.e. when you apply `extern` to a definition (function or variable):
/* File scope */
extern int a = 42;
/* This is a variable definition. We explicitly give `a` external linkage, even though it'd have it anyway */
extern void foo(void) {}
/* This is a function definition. We explicitly give `foo` external linkage, even though it'd have it anyway */
The above would be an example of "implicit vs. explicit". But that's apparently not what you are talking about.
-
Or, one could always use extern in all function declarations. Then it would communicate: "I'm a logical thinker and prefer explicit over implicit, even when language rules are clear about the implicit. Because variables need to be explicitly declared using extern, I follow the exact same logic for functions, using the exact same keyword." I don't do that myself, but I would totally understand if someone did that.
But C does not offer "prototypes" for variables. That's why we are forced to use `extern` with variables when we want to make a non-defining declaration. Meanwhile, functions in C do have prototypes. So, this already makes variables significantly different from functions w.r.t. pre-declaration methods offered by the language. Why would one want to "follow the exact same logic" for such drastically different things? Using `extern` in both contexts does not bring about any kind of uniformity: function declarations and variable declarations are still fundamentally different.
I don't find them that different. Declaring a variable
extern volatile float var;
tells the reader (and compiler) that somewhere exists a variable with this name, this type and these qualifiers.
Declaring a function
(extern) int func(const char arg);
tells the reader (and compiler) that somewhere exists this function with this name, this return value type and these argument types with these qualifiers.
Same logic could be followed even if functions and variables of course are not the same thing. The use of word "prototype" for functions but "extern" for variables is just an arbitrary name choice.
-
I continue to agree with Siwastaja's approach. But this is approaching pedantry. As many friends will attest, I'm often a big fan of pendantry {grin} but in this case I favor clarity, even if it violates some notion of purity.
Using extern with variables and functions communicates what's important to both humans and compilers: There's an entity out there with this name and these characteristics, and you'll learn its actual location at link time, but for now you can generate code based on the attributes described in the extern. Yes, according to K&R one is considered a prototype and the other is not. But when you see "extern" in a header file, you (and the compiler) know exactly what is being communicated. That's worth a lot.
YMMV, just my $0.02, etc. Neither approach is broken.
EDIT: Here's another detail likely to generate controversy. When prototyping a function, do you leave in the parameter names? Do you write:
extern void myFunction(int iValue1,float fValue2);
...or:
extern void myFunction(int,float);
...?
-
I do. I know they’re ignored, but there’s documentation value in including them.
-
In my case I add 'extern' to a function declaration to indicate that the function is in some other file. When the function declaration is meant to be a forward declaration I keep it as it is. This helps me tell them apart more easily.
-
I do. I know they’re ignored, but there’s documentation value in including them.
Yes, indeed. Plus, unless you're a masochist, you usually copy&paste prototypes from your function definitions, so in order to have no parameter name, you'll need to manually remove them, extra pain for making things less readable. To each their own, as they say, I suppose.