Reputation: 81
Can someone explain to me what's going on in this code block? Specifically on line 3. I have a hunch the * before ptr is significant. And (uint8_t *)
looks like a cast to a byte... But what's up with the *? It also looks like r, g, and b will all evaluate to the same value.
case TRUECOLOR: { // 24-bit ('truecolor') image (no palette)
uint8_t pixelNum, r, g, b,
*ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS * 3];
for(pixelNum = 0; pixelNum < NUM_LEDS; pixelNum++) {
r = *ptr++;
g = *ptr++;
b = *ptr++;
strip.setPixelColor(pixelNum, r, g, b);
}
I work primarily in C#.
Upvotes: 0
Views: 2072
Reputation: 33982
Used in a variable definition, the *
means ptr
is a pointer. The value it stores is an address in memory for another variable or a part of another variable. In this case ptr
is a pointer to a block of memory inside imagePixels
and from the names of the variables involved it's a line in an image. Since the type is uint8_t
, this is taking whatever imagePixels
is and using it as a block of individual bytes.
Used outside a varable definition, the *
takes on a different meaning: dereference the pointer. Go to the location in memory stored in the pointer and get the value.
And yeah, *
can also be used for multiplication, upping the code-reading fun level.
Incrementing (++
) a pointer moves the address to the next address. If you had a uint32_t *
the address would advance by 4 to point at the next uint32_t
. In this case we have uint8_t
, so the address is advanced one byte. So
r = *ptr++;
A) Get value at pointer.
After A) Advance the pointer.
After A) Assign value to r.
Exactly where the "advance the pointer" stage goes is tricky. It is after step A. In C++17 or greater it is before "Assign the value" because there is now a separation between the stuff on the right and the stuff on the left of an equals sign. But before C++17 all we can say is it's after step A. Search keyterm: "Sequence Points".
g = *ptr++;
b = *ptr++;
Do it again, get and assign the current value at ptr
, advance the pointer.
strip.setPixelColor(pixelNum, r, g, b);
From the naming I presume this sets a given pixel to the colours read above.
You can't just
strip.setPixelColor(pixelNum, *ptr++, *ptr++, *ptr++);
Because of sequencing again. There are no guarantees of the order in which the parameters will be computed. This is to allow compiler developers to make optimizations for speed and size that they cannot if the ordering is specified, but it's a kick in the teeth to those expecting left-to-right resolution. My understanding is this still holds true in the C++17 standard.
OK. So what is this doing?
There is a big block of memory from which you want one and only one line.
*ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS * 3];
pinpoints the beginning of that line and sets it up to be treated like a dumb array of bytes.
for(pixelNum = 0; pixelNum < NUM_LEDS; pixelNum++) {
Generic for
loop. For all the pixels on the line of LEDs.
r = *ptr++;
g = *ptr++;
b = *ptr++;
Get the colour of one pixel on the line in the standard 8 bit RGB format and point at the next pixel
strip.setPixelColor(pixelNum, r, g, b);
writes the read colour to one pixel.
The for
loop will then loop around and start working on the next pixel until there are no more pixels on the line.
Upvotes: 1
Reputation: 21
MildlyInformed, I would need more code to run through it to explain it. One tool I found really, really useful though is the C visualizer. It's an online debug tool that helps you figure out what's happening in code by running you through step-by-step, line by line. It can be found at: http://www.pythontutor.com/visualize.html#mode=edit
Even though the URL talks about python, it can do C and a bunch of languages. I would have commented instead of posting an answer, but my rep isn't high enough. I hope this helps!
(I'm not affiliated with the above website, other than to use it occasionally when I'm baffled)
Upvotes: 0
Reputation: 778
The second and third line can be expressed more cleanly:
uint8_t pixelNum;
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS * 3];
The first four variable declarations should be fairly simple, the fifth one is something C# does not have. It declares ptr
as a pointer to a uint8_t
. This pointer is set to the address of the value which is the imageLine * NUM_LEDS * 3
th element in the imagePixels
array. As this might be a different type (maybe a pointer to a char
, who knows), this value is cast to a pointer to an uint8_t
.
The next occurence of the asterisk (*
) is in the for-loop body, where it is used as the dereference operator, which basically resolves a pointer to get the actual value.
A pointer is like the street address of a house. It shows you where the house is so you can find it, but when you pass it around, you don't pass around the whole house. You can dereference it, meaning you can actually visit the house.
The two operators used in conjunction with pointers are the asterisk (*
) and the ampersand (&
). The asterisk is used in declarations of pointers and to dereference a pointer, the ampersand is used to get the address of something.
Take a look at the following example:
int x = 12;
int *y = &x;
std::cout << "X is " << *y; // Will print "X is 12"
We declare x
as an int
holding the value 12
. Now we declare y
as a pointer to an int
, and set it to point at x
by storing x
's address. By using *y
, we access the actual value of x
, the int
that y
points at.
Since a pointer is a type of reference, modifying the value via the pointer changes the actual value of the thing pointed at.
int x = 12;
int *y = &x;
*y = 10;
std::cout << "X is " << x; // Will print "X is 10"
Pointers are a large topic, and I suggest you take your time to read about them from different sources if necessary.
Upvotes: 2
Reputation: 184
The asterisk(*) is the symbol for a pointer. So the (uint8_t *) is a cast to a pointer that is pointing to a uint8_t. Then within the loop, where the asterisk is prefixed to a symbol (ie *ptr) that is dereferencing that pointer. Dereferencing the pointer returns the data that the pointer is pointing to.
I suggest reading a bit about pointers as they are critical to understanding C/C++. Here is the C++ Docs on Pointers
Upvotes: 0