can't figure out difference between declaration and definition   - Page 1
Forum section: electronics / general

author: Kittu20
author_rank: Regular Contributor
timestamp: January 05, 2023, 02:16:15 pm
content: I have been trying to understand how definition is different from declaration. I can't figure out difference between declaration and definitionMy codeCode:#include<stdio.h>void main(){int x; // I have declared variable xint y = 10; // I have declared variable y and assigning value 10 to y}

author: Siwastaja
author_rank: Super Contributor
timestamp: January 05, 2023, 02:23:16 pm
content: It's the same as the difference between a car, and a picture / registration sheet of a car.Variable actuallyexistswhen 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:Code:extern int x;

author: Kittu20
author_rank: Regular Contributor
timestamp: January 05, 2023, 03:38:52 pm
content: Quote from: Siwastaja on January 05, 2023, 02:23:16 pmI have define variable x in temp.c file and I am trying to access value of variable in hello.chello.cCode:#include<stdio.h>int main (){int x;printf( "x = %d", x);return 0;}temp.cCode:#include<stdio.h>extern int x;x = 10;outputI get warning and wrong resultgcc -o hello hello.c temp.ctemp.c:5:1: warning: data definition has no type or storage classx = 10;^temp.c:5:1: warning: type defaults to 'int' in declaration of 'x' [-Wimplicit-int]x = 4194432

author: mfro
author_rank: Regular Contributor
timestamp: January 05, 2023, 04:05:54 pm
content: Your code has no (valuable) effect. It just prints the (uninitialized) value of alocalvariable x.That is different from theglobalvariable 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.

author: Kittu20
author_rank: Regular Contributor
timestamp: January 05, 2023, 04:10:40 pm
content: Quote from: mfro on January 05, 2023, 04:05:54 pmmaking global variable is also give the same resultCode:#include<stdio.h>int x;int main (){printf( "x = %d", x);return 0;}gcc -o hello hello.c temp.ctemp.c:5:1: warning: data definition has no type or storage classx = 10;^temp.c:5:1: warning: type defaults to 'int' in declaration of 'x' [-Wimplicit-int]

author: mfro
author_rank: Regular Contributor
timestamp: January 05, 2023, 04:13:54 pm
content: Now you attempt to do an assignment in global scope (outside of any function body).When would you expect that code to be executed?

author: Ian.M
author_rank: Super Contributor
timestamp: January 05, 2023, 04:17:59 pm
content: Change the declaration in temp.c to a definition:Code:int x=10;or even:Code: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 classand add an extern declaration for it in main.c:Code:#include<stdio.h>extern int x; // this one refers to the x defined in temp.cint main (){// int x; // don't declare X here - you don't want a localprintf( "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/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.

author: Kittu20
author_rank: Regular Contributor
timestamp: January 05, 2023, 04:42:11 pm
content: Quote from: Ian.M on January 05, 2023, 04:17:59 pmThank you It workshello.cCode:#include<stdio.h>extern int x; // x defined in temp.cint main (){printf( "x = %d", x);return 0;}x = 10temp.cCode:#include<stdio.h>int x = 10; // x defined in temp.c

author: Kittu20
author_rank: Regular Contributor
timestamp: January 05, 2023, 04:52:29 pm
content: code to access function that is defined in temp.c filehello.cCode:#include<stdio.h>extern int foo(); // foo defined in temp.cint main (){int y = foo();printf( "y = %d", y);return 0;}temp.cCode:#include<stdio.h>int foo(){int z = 1;z++;return z;}y = 2

author: Ian.M
author_rank: Super Contributor
timestamp: January 05, 2023, 05:13:19 pm
content: 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.

author: sokoloff
author_rank: Super Contributor
timestamp: January 05, 2023, 06:08:33 pm
content: Quote from: Ian.M on January 05, 2023, 04:17:59 pmThat 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 theactual definitionof 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.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 05, 2023, 06:21:07 pm
content: C is indeed illogical - keywordexternis used to declare (and not define) avariable. But for a function, one can of course follow the same logic and declareCode: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).

author: golden_labels
author_rank: Super Contributor
timestamp: January 06, 2023, 02:41:38 am
content: 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 functiondoSomethingin compilation unitfun.c, and you wantmain.cto use it,main.cmust be told such a thing exists:Code:// fun.c#include <stdio.h>// Definition of `doSomething`void doSomething(int x) {printf("Do something: %d\n", x);}Code:// main.cvoid 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 calleddoSomething, which accepts a singleintargument 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:Code:// var.cint x = 12; // Definition of `x`Code:// 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 withexternandstaticis 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.

author: Kittu20
author_rank: Regular Contributor
timestamp: January 06, 2023, 11:46:40 am
content: Have I used header file correctly ?hello.cCode:#include<stdio.h>#include"temp.h"int main (){printf( "x = %d", x);return 0;}temp.hCode:extern int x;temp.cCode:#include<stdio.h>int x = 5;outputx = 5

author: sokoloff
author_rank: Super Contributor
timestamp: January 06, 2023, 12:26:20 pm
content: 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.)

author: IDEngineer
author_rank: Super Contributor
timestamp: January 06, 2023, 01:28:49 pm
content: 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.

author: Kittu20
author_rank: Regular Contributor
timestamp: January 06, 2023, 01:50:25 pm
content: I have compiled following program and it gives output.hello.cCode:#include<stdio.h>#include"temp.h"int main (void){printf("%d\n", get_number());return 0;}temp.cCode:#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 filetemp.hCode:#ifndef _OTHER_H_#define _OTHER_H_int get_number(void);#endif

author: sokoloff
author_rank: Super Contributor
timestamp: January 06, 2023, 02:12:30 pm
content: Quote from: Kittu20 on January 06, 2023, 01:50:25 pmThink 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.

author: mfro
author_rank: Regular Contributor
timestamp: January 06, 2023, 04:41:11 pm
content: Quote from: IDEngineer on January 06, 2023, 01:28:49 pmIn 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.

author: mfro
author_rank: Regular Contributor
timestamp: January 09, 2023, 09:07:47 am
content: Quote from: sokoloff on January 06, 2023, 02:12:30 pmwouldn't recommend leading underscores at all as they are reserved to the compiler and standard library implementation.

author: DiTBho
author_rank: Super Contributor
timestamp: January 09, 2023, 10:17:34 am
content: Quote from: mfro on January 06, 2023, 04:41:11 pmLOL, but that's sadly so true

author: sokoloff
author_rank: Super Contributor
timestamp: January 11, 2023, 02:08:38 pm
content: Quote from: mfro on January 09, 2023, 09:07:47 amTIL something about the C standard. Thanks!

author: Siwastaja
author_rank: Super Contributor
timestamp: January 12, 2023, 07:00:40 am
content: And just use#pragma onceat the beginning of the header file.Every compiler has supported it for at least 2-300 years. Write less boilerplate, make fewer typos.

author: sokoloff
author_rank: Super Contributor
timestamp: January 12, 2023, 11:16:52 am
content: #pragma once is not part of the language standard. Whether that matters to you depends on the project circumstances.

author: madires
author_rank: Super Contributor
timestamp: January 12, 2023, 12:07:22 pm
content: Quote from: Siwastaja on January 05, 2023, 06:21:07 pmThere's something called 'forward declaration' (also 'function prototype', required by C99), i.e.:Code: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.

author: newbrain
author_rank: Super Contributor
timestamp: January 12, 2023, 04:00:05 pm
content: Quote from: madires on January 12, 2023, 12:07:22 pmWhat 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 sokoloffsuggests) 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.

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 12, 2023, 04:36:02 pm
content: 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 bydeclaringthe structures before we define them:Code:struct one;struct two;struct one {struct one *ones;struct two *twos;// Contents of the node omitted};struct two {struct two *twos;struct one *ones;// Contents of the node omitted};We can do the same bydefiningthe two node types first (node_oneandnode_two, becoming new variable types) based on as yet undefined structure types, which also declares the structure types:Code:typedef node_one struct one;typedef node_two struct two;struct one {node_one *ones;node_two *twos;// Contents of the node omitted};struct two {node_two *twos;node_one *ones;// 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 asQuote

author: madires
author_rank: Super Contributor
timestamp: January 12, 2023, 05:20:56 pm
content: Quote from: newbrain on January 12, 2023, 04:00:05 pmYes, and no.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.

author: newbrain
author_rank: Super Contributor
timestamp: January 12, 2023, 05:47:04 pm
content: 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.

author: mfro
author_rank: Regular Contributor
timestamp: January 12, 2023, 06:01:24 pm
content: Quote from: madires on January 12, 2023, 05:20:56 pmI 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 theexternkeyword with function declarations: thereisone situation (yet another inconsistency in the C standard) where it matters:externdoes matter in conjunction withinline.Aninlinefunction 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 mentionsextern(or is missinginline), 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).

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: January 12, 2023, 06:35:06 pm
content: Quote from: Nominal Animal on January 12, 2023, 04:36:02 pmWell, 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):Code:/* Types `struct one` and `struct two` are unknown at this point */struct one {struct one *ones;struct two *twos;/* This is fine. In addition to declaring a field it alsointroduces 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;struct one *ones;};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...

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: January 12, 2023, 06:55:04 pm
content: Quote from: madires on January 12, 2023, 05:20:56 pmThat'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 considerthisa 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.)Quote from: madires on January 12, 2023, 05:20:56 pmIt 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.

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 12, 2023, 07:05:39 pm
content: Quote from: TheCalligrapher on January 12, 2023, 06:35:06 pmSshhhh, quiet!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!Quote from: TheCalligrapher on January 12, 2023, 06:35:06 pmYeah, 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.

author: newbrain
author_rank: Super Contributor
timestamp: January 12, 2023, 07:15:15 pm
content: Quote from: TheCalligrapher on January 12, 2023, 06:55:04 pmI'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.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 12, 2023, 07:26:56 pm
content: Completely equivalent, regardless of which file and scope they are in:Code:extern void func(int arg);void func(int arg);Contrary to that, these are not equivalent:Code: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.hCode:extern int module1_public_thing;module1.cCode:int module1_public_thing;module2.cCode:#include "module1.h"void something(){int thing = module1_public_thing + 42; // access it here}But only rarely you directly share globals like this.errnowould be one typical example.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 12, 2023, 07:35:33 pm
content: 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.

author: sokoloff
author_rank: Super Contributor
timestamp: January 12, 2023, 07:36:22 pm
content: The C language standard calls this second one a "tentative definition" (C99 6.9.2/2)Quote from: The C Language Standard

author: Siwastaja
author_rank: Super Contributor
timestamp: January 12, 2023, 07:41:29 pm
content: Quote from: SiliconWizard on January 12, 2023, 07:35:33 pmNot necessarily - one can use the keyword to communicate the intent to a human reader. Then it's more a question abouttastethan 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, addingexternunderlines the fact this weird looking thing in the middle of code is a declaration:Code: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 communicatesthis won't be used anywhere else, because then I would have added it to a header properly.

author: sokoloff
author_rank: Super Contributor
timestamp: January 12, 2023, 09:30:30 pm
content: Quote from: SiliconWizard on January 12, 2023, 07:35:33 pmThis was another TIL.It came in gcc 10:https://gcc.gnu.org/gcc-10/porting_to.html#common

author: IDEngineer
author_rank: Super Contributor
timestamp: January 12, 2023, 09:31:49 pm
content: Quote from: Siwastaja on January 12, 2023, 07:26:56 pmI do exactly this same thing. Eliminates confusion and doesn't rely on one-off compiler quirks.

author: madires
author_rank: Super Contributor
timestamp: January 12, 2023, 09:45:26 pm
content: Quote from: TheCalligrapher on January 12, 2023, 06:55:04 pmI 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.Quote from: SiliconWizard on January 12, 2023, 07:35:33 pmSame preconception as above.Just because you prefer a clutter-free coding style doesn't mean that any other style is a sign of incompetence.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 13, 2023, 10:46:05 am
content: Indeed, one couldalwaysuse 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 publicThink 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!

author: AndyBeez
author_rank: Frequent Contributor
timestamp: January 13, 2023, 12:22:50 pm
content: To the OP, the use of the termsdeclare or defineget somewhat muddy, especially in other languages and with other programmers from different non C backgrounds.I would for argument say that wedeclareanamedspace in memory that is used either a variable or a constant, and thendefineit's type. After-which we useargumentsto initialise the value - or values - for the type. Of course, that declared value hasscopewithin the code. When the value goesout of scope, the space in memory is destroyed; a state which some programmers refer to as the value becomingundeclared.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 13, 2023, 01:09:48 pm
content: Quote from: AndyBeez on January 13, 2023, 12:22:50 pmPlease don't confuse the OP with such BS. Basically everything was wrong with this.

author: sokoloff
author_rank: Super Contributor
timestamp: January 13, 2023, 01:14:10 pm
content: Quote from: Siwastaja on January 13, 2023, 10:46:05 amAgreed. 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.Quote from: Siwastaja on January 13, 2023, 10:46:05 amCertainly not if you're going to go on to learn c++ or switch back and forth between them!

author: DiTBho
author_rank: Super Contributor
timestamp: January 13, 2023, 01:24:30 pm
content: Quote from: newbrain on January 12, 2023, 07:15:15 pmyup, 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 hadtwo minutes of full solid panicon 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

author: AndyBeez
author_rank: Frequent Contributor
timestamp: January 13, 2023, 01:26:22 pm
content: Quote from: Siwastaja on January 13, 2023, 01:09:48 pmExplain? Or are you such an expert that you have never have to?

author: Siwastaja
author_rank: Super Contributor
timestamp: January 13, 2023, 01:29:37 pm
content: Quote from: AndyBeez on January 13, 2023, 01:26:22 pmIt'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.

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: January 13, 2023, 05:17:02 pm
content: Quote from: Nominal Animal on January 12, 2023, 07:05:39 pm"Declarations and definitions" of what specifically? Your original example looks like an attempt to illustrate the difference between "declarations and definitions" forstruct typesin 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 adeclaration, never adefinition:Code: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.

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: January 13, 2023, 05:37:49 pm
content: Quote from: sokoloff on January 12, 2023, 07:36:22 pmTentative definitionsis a perfectly standard feature of C language, which "works" withinonetranslation unit. It is a feature that permits you to specify multiple definitions of the same variablewithin the same translation unitCode:/* File scope */int a;int a;int a, a, a, a, a;The above is OK in C because these aretentative 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 inmultipletranslation 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.)

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 13, 2023, 08:27:58 pm
content: Quote from: TheCalligrapher on January 13, 2023, 05:17:02 pmThe 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 theCollatz conjecture, printing '^' for each up+down step pair (x ← 3x+1followed byx ← x/2) and 'v' for each down step (x ← x/2), plus the number of steps needed to reach 1.Code:#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;} elseif (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 occurredif (out) {fputc('!', out);}return 0;}if (new_x < 2) {return 2;} elseif (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;} elseif (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 && argv) ? argv : "(this)";if (argc < 2 || !strcmp(argv, "-h") || !strcmp(argv, "--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;errno = 0;val = strtoumax(end, &end, 0);if (errno || end == argv || *end != '\0' || val < 1) {fprintf(stderr, "%s: Invalid N.\n", argv);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 examplegcc -Wall -O2above.c-o ex, then running./ex 1161 703outputsCode: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^vvv181 steps703: ^^^^^^v^v^^^^^^v^^v^v^^^vv^vvv^^^^v^^^^^v^^^v^^^vv^v^vv^^^v^^^^vvvv^^^^^vvv^v^vvvvv^vv^^v^^vvv^^vv^^v^vv^vvv170 stepsAlternatively,./ex 1161 703 >/dev/nullshows only the standard output part:1161: 181 steps703: 170 stepsThis is not the most sensible way to implement such a Collatz conjecture exploration program, but it shows howdeclaringthings that aredefinedlater, 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.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 13, 2023, 09:00:48 pm
content: Quote from: Siwastaja on January 12, 2023, 07:41:29 pmAh, 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.

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: January 14, 2023, 03:55:59 am
content: Quote from: Siwastaja on January 12, 2023, 07:41:29 pmCan you describe that intent in more specific terms? What spectifically does it communucate?

author: Siwastaja
author_rank: Super Contributor
timestamp: January 14, 2023, 06:46:55 am
content: Quote from: TheCalligrapher on January 14, 2023, 03:55:59 amI already did in reply #37. It might be bad taste, but there you are, anyway.Or, one could always useexternin 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.

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: January 14, 2023, 07:37:47 pm
content: Quote from: Siwastaja on January 14, 2023, 06:46:55 amI 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. Theonlypurpose 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 adefinition(function or variable):Code:/* 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.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 14, 2023, 07:42:47 pm
content: Quote from: TheCalligrapher on January 14, 2023, 07:37:47 pmI don't find themthatdifferent. Declaring a variableextern 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.

author: IDEngineer
author_rank: Super Contributor
timestamp: January 14, 2023, 08:10:30 pm
content: 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.Usingexternwith 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);...?

author: sokoloff
author_rank: Super Contributor
timestamp: January 14, 2023, 09:04:57 pm
content: I do. I know they’re ignored, but there’s documentation value in including them.

author: madires
author_rank: Super Contributor
timestamp: January 14, 2023, 09:48:42 pm
content: 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.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 14, 2023, 10:11:04 pm
content: Quote from: sokoloff on January 14, 2023, 09:04:57 pmYes, 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.