If you declare something with an explicit length (
uint8_t,
uint16_t, etc.) it will take that many bytes in memory (i.e
uint8_t will take 1 byte,
uint16_t will take 2 bytes, etc.). Note, anything larger than 1 byte may be subject to padding, depending on the underlying architecture, and the ABI (application binary interface) that the compiler uses. Some processors will generate an exception if you use a 2/4/8 byte memory instruction and the address is not aligned properly. Other processors will execute the load/store more slowly if the address is unaligned. You can even get systems that some memory accesses can deal with unaligned data, and some can't (typically this would be uncached memory). IIRC, on the Teensy 3.2, there are logically two banks of memory, and unaligned accesses work everywhere
except when the access crosses that boundary, but you only see this if you are explicitly converting
char * pointers into
int *.
For example, consider this example structure:
Code:
struct foo {
uint8_t a;
uint16_t b;
uint8_t c;
uint16_t d;
};
On many systems, there would be a 1 byte hole between the fields
a' and '
b' and a 3 byte hole between '
c' and '
d'.
The ISO C/C++ standards demand that the compiler behave as if the types smaller than
int be converted to
int or
unsigned int before doing arithmetic/logical operations on it. Some processors have 8/16/32/64-bit arithmetic operations, some only have 1 or 2 sets and the other sizes must be done by multiple instruction sequences. Typically processors will have memory operations that take a 1, 2, or 4 byte item and load it into a 32-bit or 64-bit register to do arithmetic/logical operations. It is rare for a processor to provide 3, 5, 6, or 7 byte memory operations (except for special string handling instructions).
Thus if you declare something to be
uint8_t, it can save memory if you have a large array. When it is being processed in the registers, it is likely to be done in a 32-bit register (on the Teensy) using instructions that take 32-bit inputs and produce 32-bit outputs. It is then converted back to 8-bit when you store it to memory. If the value is used in another calculation before storing it to memory, the compiler may need to do a conversion from 32-bits down to 8-bits.
This storage to memory (or an in-register conversion) will eliminate the upper bits. So if you have an
uint8_t value that has 255 in it, and you add 1 to the value, it will be 0 instead of 256, if you read the value.
For example, consider this case:
Code:
uint8_t a = 250;
// ...
if (a + 10 > 251) {
println "yes";
else {
println "no";
}
This will print "
yest" because the a is logically converted to
int and 250 + 10 is 260, which is valid.
But if you wrote:
Code:
uint8_t a = 250;
// ...
a += 10;
// ...
if (a > 251) {
println "yes";
else {
println "no";
}
It would print "
no" because storing the value back into
a truncates the value.