C++ / Arduino understanding the usage of uint8_t and * - c++

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#.

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 * 3th 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.
Pointers 101
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 102
Pointers are a large topic, and I suggest you take your time to read about them from different sources if necessary.

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.

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

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)

Related

C++ adding 4 bytes to pointer address

I have a question about pointers, and memory addresses:
Supposing I have the following code:
int * array = (int *) malloc(sizeof(int) * 4);
Now in array im storing a memory address, I know that c++ takes already care when adding +1 to this pointer it will add 4 bytes, but what If I want to add manually 4 bytes?
array + 0x004
If im correct this will lead to add 4*4 (16) bytes, but my Idea is to add manually those 4 bytes.
Why? Just playing around, i've tried this and I got a totally different result from what I expected, then i've researched and i've seen that c++ takes already care when you add +1 to a pointer (it sums 4 bytes in this case).
Any idea?
For a pointer p to a type T with value v, the expression p+n will (on most systems anyway) result in a pointer to the address v+n*sizeof(T). To get a fixed-byte offset to the pointer, you can first cast it to a character pointer, like this:
reinterpret_cast<T*>(reinterpret_cast<char*>(p) + n)
In c++, sizeof(char) is defined to be equal to 1.
Do note that accessing improperly aligned values can have large performance penalties.
Another thing to note is that, in general, casting pointers to different types is not allowed (called the strict aliasing rule), but an exception is explicitly made for casting any pointer type to char* and back.
The trick is convert the type of array into any pointer-type with a size of 1 Byte, or store the pointer value in an integer.
#include <stdint.h>
int* increment_1(int* ptr) {
//C-Style
return (int*)(((char*)ptr) + 4);
}
int* increment_2(int* ptr) {
//C++-Style
char* result = reinterpret_cast<char*>(ptr);
result += 4;
return reinterpret_cast<int*>(result);
}
int* increment_3(int* ptr) {
//Store in integer
intptr_t result = reinterpret_cast<intptr_t>(ptr);
result += 4;
return reinterpret_cast<int*>(result);
}
Consider that if you add an arbitrary number of bytes to an address of an object of type T, it no longer makes sense to use a pointer of type T, since there might not be an object of type T at the incremented memory address.
If you want to access a particular byte of an object, you can do so using a pointer to a char, unsigned char or std::byte. Such objects are the size of a byte, so incrementing behaves just as you would like. Furthermore, while rules of C++ disallow accessing objects using incompatible pointers, these three types are excempt of that rule and are allowed to access objects of any type.
So, given
int * array = ....
You can access the byte at index 4 like this:
auto ptr = reinterpret_cast<unsigned char*>(array);
auto byte_at_index_4 = ptr + 4;
array + 0x004
If im correct this will lead to add 4*4 (16) bytes
Assuming sizeof(int) happens to be 4, then yes. But size of int is not guaranteed to be 4.

Obtaining an int from a void pointer which points to a short

I have a return value from a library which is a void pointer. I know that it points to a short int; I try to obtain the int value in the following way (replacing the function call with a simple assignment to a void *):
short n = 1;
void* s = &n;
int k = *(int*)s;
I try to cast a void pointer that points to an address in which there is a short and I try to cast the pointer to point to an int and when I do so the output becomes a rubbish value. While I understand why it's behaving like that I don't know if there's a solution to this.
If the problem you are dealing with truly deals with short and int, you can simply avoid the pointer and use:
short n = 1;
int k = n;
If the object types you are dealing with are different, then the solution will depend on what those types are.
Update, in response to OP's comment
In a comment, you said,
I have a function that returns a void pointer and I would need to cast the value accordingly.
If you know that the function returns a void* that truly points to a short object, then, your best bet is:
void* ptr = function_returning_ptr();
short* sptr = reinterpret_cast<short*>(ptr);
int k = *sptr;
The last line work since *sptr evaluates to a short and the conversion of a short to an int is a valid operation. On the other hand,
int k = *(int*)sptr;
does not work since conversion of short* to an int* is not a valid operation.
Your code is subject to undefined behavior, as it violates the so-called strict aliasing rules. Without going into too much detail and simplifying a bit, the rule states that you can not access an object of type X though a pointer to type Z unless types X and Z are related. There is a special exception for char pointer, but it doesn't apply here.
In your example, short and int are not related types, and as such, accessing one through pointer to another is not allowed.
The size of a short is only 16 bits the size of a int is 32 bits ( in most cases not always) this means that you are tricking the computer into thinking that your pointer to a short is actually pointing to an integer. This causes it to read more memory that it should and is reading garbage memory. If you cast s to a pointer to a short then deference it it will work.
short n = 1;
void* s = &n;
int k = *(short*)s;
Assuming you have 2 byte shorts and 4 byte ints, There's 3 problems with casting pointers in your method.
First off, the 4 byte int will necessarily pick up some garbage memory when using the short's pointer. If you're lucky the 2 bytes after short n will be 0.
Second, the 4 byte int may not be properly aligned. Basically, the memory address of a 4 byte int has to be a multiple of 4, or else you risk bus errors. Your 2 byte short is not guaranteed to be properly aligned.
Finally, you have a big-endian/little-endian dependency. You can't turn a big-endian short into a little-endian int by just tacking on some 0's at the end.
In the very fortunate circumstance that the bytes following the short are 0, AND the short is integer aligned, AND the system uses little-endian representation, then such a cast will probably work. It would be terrible, but it would (probably) work.
The proper solution is to use the original type and let the compiler cast. Instead of int k = *(int*)s;, you need to use int k = *(short *)s;

How are pointers actually made to increment by the type their type

How are pointers made to increment by their type.
For example if we have
int *ptr;
ptr++; //would point to the next integer i.e. it would increment ptr by 4bytes in 32 bit system
I wanted to know that how is this done internally.
The compiler compiling the code knows the base type of the pointer, and it puts the code to increment pointer (offset) appropriately. For example:
int* p = new int[10];
*(p+2) = 100;
The second line will be like:
p + sizeof(int) * 2 ... // Not C/C++
And, similary:
p++;
Would mean:
p = p + sizeof(int); // Not C/C++
If type of p is something else (like float or some structure), the calculations would be performed appropriately. There is no magic. Types are compile time defined - a type of variable don't change at runtime, hence the calculations.

Can we have operations between int* and unsigned int?

If I declare
int x = 5 ;
int* p = &x;
unsigned int y = 10 ;
cout << p+y ;
Is this a valid thing to do in C++, and if not, why?
It has no practical use, but is it possible?
The math is valid; the resulting pointer isn't.
When you say ptr + i (where ptr is an int*), that evaluates to the address of an int that's i * sizeof(int) bytes past ptr. In this case, since your pointer points to a single int rather than an array of them, you have no idea (and C++ doesn't say) what's at p+10.
If, however, you had something like
int ii[20] = { 0 };
int *p = ii;
unsigned int y = 10;
cout << p + y;
Then you'd have a pointer you could actually use, because it still points to some location within the array it originally pointed into.
What you are doing in your code snippet is not converting unsigned int to pointer. Instead you are incrementing a pointer by an integer offset, which is a perfectly valid thing to do. When you access the index of an array, you basically take the pointer to the first element and increase it by the integer index value. The result of this operation is another pointer.
If p is a pointer/array, the following two lines are equivalent and valid (supposing the pointed-to-array is large enough)
p[5] = 1;
*(p + 5) = 1;
To convert unsigned int to pointer, you must use a cast
unsigned int i = 5;
char *p = reinterpret_cast<char *>(i);
However this is dangerous. How do you know 5 is a valid address?
A pointer is represented in memory as an unsigned integer type, the address. You CAN store a pointer in an integer. However you must be careful that the integer data type is large enough to hold all the bits in a pointer. If unsigned int is 32-bits and pointers are 64-bits, some of the address information will be lost.
C++11 introduces a new type uintptr_t which is guaranteed to be big enough to hold a pointer. Thus it is safe to cast a pointer to uintptr_t and back again.
It is very rare (should be never in run-of-the-mill programming) that you need to store pointers in integers.
However, modifying pointers by integer offsets is totally valid and common.
Is this a valid thing to do in c++, and if not why?
Yes. cout << p+y; is valid as you can see trying to compile it. Actually p+y is so valid that *(p+y) can be translated to p[y] which is used in C-style arrays (not that I'm suggesting its use in C++).
Valid doesn't mean it actually make sense or that the resulting pointer is valid. Since p points to an int the resulting pointer will be an offset of sizeof(int) * 10 from the location of x. And you are not certain about what's in there.
A variable of type int is a variable capable of containing an integer value. A variable of type int* is a pointer to a variable copable of containing an integer value.
Every pointer type has the same size and contains the same stuff: A memory address, which the size is 4 bytes for 32-bit arquitectures and 8 bytes for 64-bit arquitectures. What distinguish them is the type of the variable they are poiting to.
Pointers are useful to address buffers and structures allocated dynamically at run time or any sort of variable that is to be used but is stored somewhere else and you have to tell where.
Arithmetic operations with pointers are possible, but they won't do what you think. For instance, summing + 1 to a pointer of type int will increase its value by sizeof(int), not by literally 1, because its a pointer, and the logic here is that you want the next object of this array.
For instance:
int a[] = { 10, 20, 30, 40 };
int *b = a;
printf("%d\n", *b);
b = b + 1;
printf("%d\n", *b);
It will output:
10
20
Because b is pointing to the integer value 10, and when you sum 1 to it, or any variable containing an integer, its then poiting to the next value, 20.
If you want to perform operations with the variable stored at b, you can use:
*b = *b + 3;
Now b is the same pointer, the address has not changed. But the array 10, 20, 30, 40 now contains the values 13, 20, 30, 40, because you increased the element b was poiting to by 3.

Some basic C++ questions (noob!)

I'm just beginning to learn C++ through my university course.
I'm revising for a C++ exam and I was hoping I could just ask some simple questions about things I'm not so sure on.
First of all, if I have this:
int i = 4;
What is the difference between these two lines:
int* p = &i;
int& r = i;
By my understanding, at this point both p and r are like conduits to i; changing their value will change the value of i...
Secondly, about assembly, I have a question in a past exam that is fooling me:
mov eax, dword ptr[b]
push eax
mov eax, dword ptr[a]
push eax
call printCode (411186h)
add esp,8
Q: Which of the following prototypes
when dissassembled, best match the
assembly code?
A: int printCode(int a, int b)
My question is: If the assembly is moving ptr[a] and ptr[b]... Why is the prototype not int* a, int* b?
Thank you.
The difference is that changing p's value won't change i, it will change what p points to:
p = &j;
With p, you can access, and change, i via indirection:
*p = 5;
Whereas with r, the indirection is not required, since r is an alias for i. On the flip side, this means that there is no way to change r to reference a different variable. i.e., there is no equivalent to p = &j;.
You should split the second question into a second question (if you get my drift).
int* p = &i;
p is a pointer pointing to the address of i. p will be a variable stored in your memory that contains the (virtual) memory address of i.
int& r = i;
r is a reference to i. You should think of references as aliases: you could think that r is an alias for the name i
I'd suggest you to read C++Faq chapter about references.
int* p = &i;
int& r = i;
The first statement means assigning the address of variable i to a pointer p. Here later you can change the value of the pointer p.
The second statement is "create a reference or alias r for the variable i". It is just another name for the same variable. You cannot change the reference later to point to a different variable in c++.
For the assembly snippet. I sense the snippet points to calling code. the call might have been like
printCode(*a,*b); // Here a and b are pointer to integers
So the prototype is
int printCode(int a,int b)
is right.
Ferose Khan J
What is the difference between these two lines:
int* p = &i;
int& r = i;
By my understanding, at this point both p and r are like conduits to i; changing their value will change the value of i...
The sentence is not true if taken literally. Changing p value will only make it point to a different int, changing the memory pointed to will change i:
int j = 0;
*p = 5; // equivalent to r = 5, or i = 5 (semantically, can be different in assembler)
p = &j; // changes p to point to j instead of i
You should think of pointers as proxy objects that refer you to a real instance, but that can be modified to lead to a different place. References are best understood as aliases to the object on the right hand side of the definition. You never operate on the reference, you operate on the referred object, they are the same.
References are more or less only syntactic sugars, but there are few differences between them and pointers which are described very well here.
For disassembly question.
When you say:
mov eax, dword ptr[b]
You are asking CPU to move the value from a specified address (b). So it depends what is b. If b is a int* then the answer is correct.