I cannot figure out pointers. This snip of code works, but only because it's mostly copied from another post where the member tried to explain how to send data over a serial connection.
My problem is, I don't understand the use of the asterisk. It isn't multiplication, nor is it a dereference (maybe). I do understand the use of the ampersand for reference. But if it's referencing a variable address, shouldn't the storage variable for that address be an integer? I think "b" is a byte. If I change it to integer, lcd.print really hates it. So, what does "byte * b = (byte *) &temp ;" do?
No, it's a pointer to a byte. That means that the pointer increments in one byte increments. A pointer to an int increments in 2 byte increments. A pointer to a float increments in 4 byte increments.
But if it's referencing a variable address, shouldn't the storage variable for that address be an integer?
No, the storage type for a pointer is USUALLY of the same type as the pointed to memory, but not always. Sometimes you want to access the high order and low order bytes of an int, as in this case. You do that using a pointer to byte, but pointing to int-sized memory.
So, what does "byte * b = (byte *) &temp ;" do?
The temp variable points to the 3rd element in the alltemps array (of some mysterious type and size, but presumably int). The byte pointer is then being used to get the high order byte and then the low order byte of alltemps[3].
Not at all the way I'd write that code. Using pointers in that way is rather convoluted. The highByte() and lowByte() "functions" do the same thing in a far more understandable way.
Now, if you were iterating over several elements of the array, that would be a different story.
it creates a pointer to a byte that contains the address of the int temp. The address of temp is cast to a byte to tell the compiler that you know what you are doing.
It will basically let you pull apart the in temp into its individual bytes.
Thanks. The "ah ha" is that it's a byte pointer. But "b" must an integer (or some kind of 16-bit entity). Paul, your post indicates that pointers, when created, retain the byte, word, or long word characteristic of the variable pointed to. Are there different creation words that do this? What is the asterisk doing here: byte * b = (byte *) &temp ; ?
Keith, you said "it creates a pointer to a byte that contains the address of the int temp". I don't understand how you can cast a 16-bit address to a byte. I thought cast required the same width in the source and the copy, if that makes sense.
Yes, convoluted code. Eventually it will be sticking received data into, and picking data out of, a 128 element integer array. The application is a radiant heat control system with one central controller and 9 distributed Arduino display and sensing units, for 18 radiant loops.
Keith, you said "it creates a pointer to a byte that contains the address of the int temp". I don't understand how you can cast a 16-bit address to a byte. I thought cast required the same width in the source and the copy, if that makes sense.
All pointers are the same size - large enough to hold an address. What they point to is of different sizes. A pointer of type pointer to byte is the same size as a pointer to float, though what is pointed to, and how pointer arithmetic works, is different.
b is a pointer to a byte. It's size totally depends on the address space of the processor. It could be 8 bits wide or 64 bits wide. Or it could have a segment and address like the old intel processors.
Think of it this way: A byte is one cell in an array of 2000 elements. An int is two elements in that same array. both use the exact same size address - a number between 0 and 1999. The only difference is that the compiler knows that the byte contains one element and the int contains two elements.
It is not casting a "16 bit address" to a byte. It is casting an address that points to a 16 bit object to an address that contains an 8 bit object. The addresses are the same width - though that does not have to be the case.
You could conceptually have a 4 bit (16 elements) address space that contains bytes and ints, too. In this case the pointer would be a single nibble, while the data could still be 8 and 16 bits.
Every variable lives in memory somewhere. That memory address is its lvalue (old assembler term for "location value"). You can retrieve a variable's lvalue using the address-of operator, &. The value associated with that variable is its rvalue (assembler for register value). Now consider the following data definitions:
int val = 10;
int temp;
int *ptr;
temp = val;
and suppose val is at memory address 1000 and temp is at address 2000. The assignment statement of temp = val is an rvalue-to-rvalue assignment expression so temp now equals 10. Now consider:
ptr = &val;
This says: "Don't do a normal rvalue-to-rvalue assignment. Instead, take the lvalue of val and assign it into the rvalue of ptr." Think about this: ptr now knows where val lives in memory. Look at the next statement:
*ptr = 20;
The asterisk is the indirection operator. You should not have trouble differentiating it from the multiplication operator because multiply uses two operands (e.g., op1 * opt2) where indirection is a unary operator and only has one operand (*ptr). The indirection operator says: "get the rvalue of ptr, which is 1000 and also the lvalue of where val lives in memory, and assign the value of 20 at that memory location." Because ptr is a pointer to int, the compiler knows it must use 2 bytes for the value 20. Therefore, the indirection operator is like a jump instruction to the memory address being stored in ptr. It is the type specifier for ptr that determines how many bytes (its scalar is 2) are involved in the assignment. Note because of indirection, we have changed the value of val to 20.
My Bucket Analogy might help: the lvalue is where the data bucket resides in memory. The rvalue is what you see when you peek inside the bucket. The size of the bucket is determined by the size of the data. A char has a 1-byte bucket, an int a 2-byte bucket, a long a 4-byte bucket, etc. If you try to assign a long into an int, you're pouring a 4-byte bucket into a 2-byte bucket, and risk spilling 2 bytes of information on the floor. If that could happen, use a cast, which serves to shrink the 4 bytes of information to fit into a 2-byte bucket.
Thanks to all. I'm fully aware of address space pointers--Forth is much closer to machine language and a number is a number (byte, word, double). You don't have strong typing like char, signed, and unsigned.
The confusion on my part was "it's a byte pointer" means to me an 8-bit pointer, not a 16-bit pointer pointer to an 8-bit byte's address.
The indirection * will take more thought. All the examples show it immediately preceding the variable. It's just the grammar of C, but whether or not spaces make a difference for certain operators.
Regardless, my code is working and is much cleaner with your help.
Forth "changed my (programming) life" back in 83 but the last Forth I wrote was in 88 or 89, I forget.
When I picked up C++ I found that to be a viable (though restrictive) substitute and I wrote what I could get payed for which for many years was Basic (1 job doing Forth, the rest was because it was fun) so getting work in C++ was a huge blessing!
Still I don't know the newer changes to Forth and my stack words skills are old memories.
There are some versions of Forth for AVR chips with one seeming the clear best to me with an available version for one of the ATmega644 chips and docs to port to others like the 1284P. None of those have a bootloader to lock out flash programming, you add words to flash memory and run through RX/TX serial and access to pins/ports.
I have links somewhere but it's been a few years and there may be new developments. Perhaps if FIG is still a thing they would have better info.
Would you believe that there are people who don't believe that Forth is OOP?