I'm surprised why this compiles and behaves as expected for an AVR based board (Nano). I actually did not expect it to work correctly.
struct TOKEN
{
float val;
char op;
};
const uint8_t maxTokens = 20;
struct LIST
{
// number of tokens currently in list
uint8_t size;
// array of tokens
TOKEN tokens[maxTokens];
};
LIST l1, l2;
void setup()
{
Serial.begin(115200);
l1.tokens[0] = { 3, '+' };
l1.size++;
l2.tokens[0] = { 4, '/' };
l2.size++;
Serial.println(F("l1"));
Serial.print(F(" "));
Serial.print(l1.tokens[0].val);
Serial.print(", ");
Serial.println(l1.tokens[0].op);
Serial.println(F("l2"));
Serial.print(F(" "));
Serial.print(l2.tokens[0].val);
Serial.print(", ");
Serial.println(l2.tokens[0].op);
l1 = l2;
Serial.println(F("l1 after 'copy'"));
Serial.print(F(" "));
Serial.print(l1.tokens[0].val);
Serial.print(", ");
Serial.println(l1.tokens[0].op);
}
void loop()
{
}
This is a stripped down version of a far larger program (that was thrown at me) to show what happens. I would expect the compiler to complain about the line l1 = l2 but it does not.
I would normally use memcpy() to copy structs so the question is if l1 = l2 is correct and why.
I am 99.99999% sure I did this when I was a noob C programmer a veeeeeerrrrrry long time ago.
Print the address of l1 and l2, print the contents of l1 and l2. Do that before and after the assignment (l1 = l2). What do the prints tell you.
BTW @camsysca was the first to give you the answer.
It is impossible I think to change the address of a variable during execution with 'normal' code. It may be doable, but why?
I apologize if I am being too simplistic, nor intent to insult/embarass.
l1 and l2 are 4 (I didn't check but seems more likely than not) bytes of memory that CONTAIN the memory location of a LIST variable.
Copying one LIST variable to another is as simple as l1 = l2; That staement says the 4 bytes of memory we call l1 now contains the 4 bytes of memory we call l2. Only 4 bytes are copied, not the entire list variable.
I think so as well. My point is why @camsysca mentioned pointers; for me the variables are normal variables, not pointers to memory locations. Although they might be under the hood.
As mentioned, I never used an assigment to copy one struct to another, I always used (for the last 35 years or so) memcpy().
Don't worry, that's not that easy I'm only embarrassed because I don't understand how it works.
I only set "compiler warnings" to "All" and there are no indicators. There is however no difference with verbose on and off.
Do I understand it correctly that the variable l1 is not the actual struct but, under the hood, contains a pointer to the actual struct that is somewhere else in memory; same for l2. That would explain why assignment works.
YES, in every language I ever used but maybe some are different.
Next is array's. Or structs with arrays, or arrays of structs.
If I was to guess, during the time when I got paid to write code, probably 1/3 to 1/2 of the lines of code involved pointers.
Teensy tiny correction, I would not say l1 contains a pointer, l1 IS a pointer to the actual struct.
If the struct is at memory location 1000 and the variable l1 is at 2000 then memory location 2000 (for 4 bytes) contains 1000.
Is that clear?
it's all OK. struct instances behave like variables in some way and copy a struct into another one is fine.
The assignment l1 = l2 performs a shallow, member-wise copy of all the fields in l2 to l1 , which is valid here because no pointers or dynamic allocations are involved.
Since tokens is a fixed-size array inside the struct, it gets copied element by element as part of that member-wise copy ➜ so the array content is safely and entirely copied.
If you use raw char* pointers in the structure pointing somewhere into the heap, then that would be an issue as the shallow copy would copy the pointer and not the data pointed.
One would hope that if the above were the case the compiler would throw an error of some severity, otherwise a run time error or wose still no error but memory being modified unintentionally will happen.