Declaration vs. Definition vs. Instantiation

Even people will yell at me like: "what are you talking about, why do you post such a crab...",
just a brainstorming about the differences when talking about declarations, definitions etc. on "any" coding language (C or C++ or any other).

The main question to ask: which statements in your source code generates really (binary, executable) code?

  • Declaration
    A declaration does not generate any code. It is just there to "declare" a type, interface or reference (e.g. as external)
extern void myFunction(void);

does not generate any code. It just tells the compiler: "hey, there is an external function which has this "signature". And compiler can check if you call this myFunction properly (here, with no arguments and not expecting a return).

  • Definition
    You can also define new types, new class, new structures etc. - but it will also not yet create code (space in memory).
    Example:
class MyClass {
  public:
     MyClassFunction();
}

MyClass::MyClassFunction() {
    //... do something - have code here
}

Even you provide already code for MyClassFunction() - it is not necessarily generated (not yet available, no space yet in memory).

  • Instantiation
    Just if you instantiate a class, you create an object, e.g. via
MyClass myObject;

you will generate code.

A compiler can have several reasons to "complain":

  • a declaration mismatches with an existing declaration, e.g. the parameter list of a function is not identical (not the same "signature")

  • you define something which is already defined: the type, class, variable etc. is already there.
    It can also happen, compiler accepts your definition but it finds a similar one in a library (LIB) used: it has no idea which one to use.

  • it cannot be instantiated
    In object oriented languages, like C++ there is a concept of "virtual": it can force you as: "OK, you can reuse me, but you have to provide the implementation for some stuff, e.g. member functions".
    So, all might look OK, but you cannot create an object (an instance) of a class. It has still "undefined" references, e.g. virtual methods.

So, at the end:
You have to make your compiler happy: mainly it starts with following the "prototypes" (the declarations). You can only use what was declared, in the same way as declared.

You cannot use anything what was not defined. Obvious: using a variable which does not exist (was not defined) cannot be used.

And objects do exist just after instantiation: a defined class does not create yet an instance. Objects come from classes (or structures) and need an instantiation, even the definition is OK.

Just to think about what "happens behind the scene" (for a compiler). Like in human communication:

  • let's declare some naming, acronyms, types, syntax before we keep talking

  • let's define what we want to have, using our declarations (nothing can be defined if attributes of it were not declared, e.g. a type)

  • let's generate code: just instances of defined objects can generate code. We had to create an instance of something defined.

This might help you to understand where a compiler comes from: a declaration is missing (or conflicts witch an existing one), a definition was missing (before creating and using an instance) or the instantiation fails (due to a missing definition). Like a three-level-stages to understand a complete sentence in the same correct way: "have same syntax, have same semantic, have same resulting action".

There is no need to understand how compilers work (behind the scene). But there is a need to understand to be unambiguous, clear, precise and "complete" in order to let the compiler do what you want to get. If an error is fired: it was not clear for the compiler "what do you mean or want to do."

Just start to think like:

  • was a type wrong? (a mismatch with existing prototypes, declarations or compiler expectations)

  • was there a use of something undefined, unknown, not able to resolve via #include?

  • is there a statement which generates really code?
    e.g. using a function which cannot be found, using an object which was not created?

No you declare them just in the same way you were telling the compiler about a signature you tell the compiler what the type looks like

No not yet. If the instance is not used the optimizer will get rid of it. The compiler makes a note that there is a need for memory and will remember the association between the name and the address and possibly later will generate code for calling the constructor and also code for all the member functions that are used. Unused methods won’t be in the final binary - nor the unused member variables.

There is probably a better forum category for this topic if it's not directly a question. An executable program starts at a start vector and continues procedurally from there. In C and C++, that start vector is a call to int main(void). The following is the "Blink Sketch" without too much abstraction from the hardware.

volatile unsigned int thSecs;
ISR(TIMER1_COMPA_vect) {
  thSecs++;
}
int main(void) {
  TCCR1B = TCCR1A = TCNT1  = 0x0;
  OCR1A = 0x7CF;
  TIMSK1 |= 0x2;
  TCCR1B = 0xA;
  DDRB |= 0x20;
  cli();unsigned int blinkTimestamp = thSecs;sei();
  while(1) {
    cli();
    if (thSecs - blinkTimestamp >= 0x3E8UL) {
      blinkTimestamp += 0x3E8UL;
      PORTB ^= 0x20;
    }
    sei();
  }
}

how does this relate to the thread ? (Declaration vs. Definition vs. Instantiation)

Again, taking my words out of context. If it's not a question does it belong in Programming Questions?

my question was how does your code sample relate to the current thread (which might be in the wrong place but that's a distinct thing - or did I fail to understand your point ?)

Maybe I fail to understand the OPs point, but the way I interpreted this was

So, I presented a stripped version of the blink sketch as an example.

I definitely did not read it like this.

I took this as a memo on how to distinguish what's declaration and what's definition in a program and he is conjecturing that the best way to differentiate is to ask oneself that question (ie what is information just for the compiler and what does actually make it to actual code / memory allocation)

Maybe file under tutorial, I don't really know. If my posts confuse the already confusing thread, I can delete them.

let's wait for OP to clarify

A declaration is an attribute list of a data item. No memory is allocated (i.e., no lvalue)
A definition is also an attribute list of a data item, but memory is allocated for it. The instantiation of an object is also its definition.

For example, if we have two files that both want to access a variable name x:
File 1 File 2
int x; extern int x;

The declaration of x in File 2 is saying: "Variable x is an int with global scope and, even though it is defined somewhere else, let me use it as an int in this file and let the linker supply the missing lvalue (memory location) for x." The statement in File 1 defines x and creates an attribute list and defines its memory location. The linker will supply the missing memory address for x in file 2.

I dunno. If your function is NOT in a class, then the code is generated at the time the function is defined. (there is no separate "instantiation" step. (Subject to the same "optimization" details that other people have mentioned.)

For that matter, instantiating a class only "generates code" for the first instantiation. If you instantiate the class several times, you still only get one copy of the code. That's pretty important to understand, and I think this tutorial implies otherwise...
I think I'd prefer a statement to the effect of "functions create code when they are defined, but they are subject to being eliminated if they are never used", which applies to both functions inside and outside of classes. Of course, it's easier for the COMPILER to figure out that a function is not used if the function is part of a class, and no instance of the class is instantiated...

Do the following declarations/definitions generate executable codes?

1.  const byte y1 PROGMEM = 0x35;
2.  const byte y2 = 0x67;

What is the definition of an executable code/instruction?
The code/instruction that has an opcode (operation code) -- correct?

Yes that’s the point - the function is "instantiated" (loosely said) if you call it otherwise it’s just useless and it does not make it to the final generated code

You defined the variable
Code generation is something different that depends (nowadays) on whether you use said variables in your code or not.

For a const variable the compiler might decide to only replace it inline an never allocate specific memory for it to read the value from

As you assigned a default value to the variable (outside PROGMEM) there would be code generated to initialize the SRAM

if you do:

const byte y1 PROGMEM = 0x35;

on a global (or static) level ( a global variable defined): there is no real code involved,
Except: the startup code (in startup.s) will copy the data from FLAH to RAM: the copy of your data initialization sits in FLASH ROM and will be copied to your RAM location.

But if you do:

void my function(void) {
   char myVar = 0x10;

this is real code: the stack variable myvar has to be written with value 0x10.
The reason is: any (auto), local, variable has to be written before first use: So, local variables in functions, which reside on stack region (which are "uninitialized") must be initialized with code.
So, here, it generates code.

Just think about:
"global" or "static" can be initialized during startup (just copy data from ROM to RAM),
but "local" variable initialization needs code to do so (the local memory has garbage and is uninitialized, needs instructions).

For a const byte you have likely nothing at all. The compiler will likely in-line the data directly in the assembly parameters

1. Which action does involve code?
The action that involves the association of operation code(opcode) which is a command for the CPU. In the given declaration (const byte y1 PROGMEM = 0x35;), the data transfer activity is involved which must execute the mov/spm opcode. In this situation, the Compiler must generate codes.

2. Is Flash memory is of ROM technology?
As far as I know, the flash is of EEPROM technology.

const, (global) static variable are usually initialized during the startup.S code executed (right after reset, the first part of startup, way before your main() function is called.

Sure, there is code to initialize such variables. But often it is just to copy a block of data from ROM to RAM, not writing any single variable. It is often just to "move" the pre-initialization values to the RAM memory location where the variables are and used from.

And often, when real ROM data is supported (as const): there is really nothing: your user code would read the value from ROM directly (where it is stored).

If ROM as Flash is EEPROM, NOR, NAND Flash does not matter: ROM is always Read-Only for code: in order to overwrite, update content on (any) ROM - you need special instructions (erase, write again) which is never part of startup, regular FW cannot alter the ROM content via a single instruction.

What I tried to "tell":
static or const does not need any particular instruction as part of your function. Their initialization will be done during startup, at any time before you enter your function or never when it sits as const on ROM and can be read from there.

const var = VALUE;

can be used directly from ROM: just know the address where the VALUE is stored. There is never this assignment as dedicated code. The code generated "knows" where to find VALUE (via address).

static var VALUE;

will be done anytime way before you use it. Most of the time it is just a copy of a data block from ROM to RAM (and function uses the RAM location with this copy).
The assignment is not an instruction which needs to be done as part of your function call (entry, it is already done). And it is done only ONCE! (never again in this way as "written").