The Arrow Operator in C++

preview_player
Показать описание


Thank you to the following Patreon supporters:
- Samuel Egger
- Dominic Pace

Gear I use:
-----------------

Рекомендации по теме
Комментарии
Автор

For anyone having trouble with the last part don't forget that The Cherno is using a 32bit system, so size of a pointer is 4, because of that he can cast it to an int which has a size of 4 as well. If you have 64 bit system you'll need to cast it to a long or size_t (a more correct way in my opinion) type because the size of your pointer is 8.

ikatsu
Автор

The final trick is actually pretty simple. He's casting a base arbitrary address to a Vector3 pointer type and then retrieving the address of a specific field from the struct. Since the base arbitrary address is 0, the returned address actually corresponds to the offset!

In other words, if a base address other than 0 was used, the code could be rewritten as

Vector3* p = new Vector3;
int offset = ( (int)&((Vector3*)p)->z ) - ((int)p);

Still, pretty cool stuff :)

As a bonus, you actually don't need to use the arrow operator for this. It can also be written as,

int offset = (int)&(*(Vector3*)0).y;

Which looks ugly(er) AF.

As an extra bonus: as offset is of type int in the example, it can be either a 32-bit or a 64-bit integer, depending on compiler settings. Pointer types in 64-bit systems as always 64-bit, thus if int is a 32-bit variable and is assigned a 64-bit value, it loses its most significant bits. g++ picks this up and throws an error. To avoid this, you can make sure offset is a 64-bit integer by using uint64_t, which is a 64-bit unsigned integer type defined in <cstdint>. It would look like this:

#include <cstdint>
uint64_t offset = ( (uint64_t)&((Vector3*)0)->z );

Great video, thanks Cherno!

jhfoleiss
Автор

(int) &((Vector3*)0)->x

C++ treats addresses as pointers, then this line of code means:
- Cast 0 into an valid Vector3 address (pointer).
- Returns x's address (remember the Vector3 address is 0, so x's address (the first member of Vector3) should be also 0).
- Cast this address into int.

If we would like to return y's address, then we should jump the bytes with addresses 0, 1, 2 and 3, because this is x's bytes, and the byte with adress 4 is where y begins.

yurishimoki
Автор

for the last -> operator trick just an fyi in case you don't already know... there is pre-defined macro for you to use like: FIELD_OFFSET, UFIELD_OFFSET aswell as offsetof

pagefault_in_nonpaged_area
Автор

The arrow operator can also be used with "trailing return type" syntax (c++11).

For example:

auto function(int value) -> int
{
return value;
}

which is the same as:

auto function(int value) -> decltype(value)
{
return value;
}

This is the exact same thing as: (C++14)

decltype(auto) function(int value)
{
return value;
}

grabbenskarve
Автор

04:57 You definitely type stfu more often then struct

cranealways
Автор

(int)&((Vector3*)0)->z

...yeaahhh this is starting to look like proper C++ :D

CacheTaFace
Автор

A little explanation of final trick :
- First, we create a *Null Pointer*
- Then, we cast it into *Vector3* Type. To change it's properties. It's just like, we put the *soul of Vector3* in the *Null Pointer*
- Now, We have to ask the *Memory Address* from the *null pointer* of that variable, pointed by *->
*

- The *Null Pointer* says that it knows nothing about it. That's y, returns the error. But, the *Vector3* class knows! (I mean, it's soul knows)
- We have to ask from the *soul* of the *Pointer*, to which position, will it put the variable, pointed by *->
*
- So, we use *&*, to get the address of that pointer. Yes, pointer also has address of it's own., as everyone knows
- That address is surely the *Soul*, we put in that pointer, and Yes! it knows where to put the variable in memory block.

- We then get the position, or u may say, the *offset*
- To show that on screen, we cast it into *int. (It's optional)
*
- In VS, it will return a *Magnitude of offset* .
- In MinGW, or any other Compiler, it will return offset in Memory's Language. Which can't be dereferenced, due to size issues.
That's y, we have to use the alternative way :
*_cout << (int*) &((Vector*)nullptr) -> z << endl;_*
or
*_cout << (intptr_t) &((Vector*) nullptr) -> z << endl;_*
Thanks for TheCherno for this trick!
And all the Commenters for Detailed Explanation.

TheMR-
Автор

For those wondering why the arrow operator returns a Entity* and not an Entity:

The cpp reference says:
"The overload of operator -> must either return a raw pointer, or return an object (by reference or by value) for which operator -> is in turn overloaded."

In English, cpp will recursively call the arrow operator until it gets back a raw pointer, which it will then deref for you (or at least that is my understanding)

thememesarealive
Автор

You always coming in the cut when Im studying for my classes. Amazing videos, very clearly explained. No negative comments from me. Keep up the great work!

hello
Автор

4:57 Cherno wants us to shut the f*** up... O_o

Braindrain
Автор

Well done, the nullptr offset portion exploded half my brain 👍

bengamedev
Автор

You're so good and selfless. I've learned many tricks so far.

Zxba
Автор

Solution to precision error
#include <iostream>
struct vector3
{
float x, y, z;
};
int main()
{
int offset =
std::cout<<offset;
return 0;
}

mkmabdelkawy
Автор

Woah that last trick. I'm curious how this works behind the scenes now.

h.hristov
Автор

This literally helped me deliver my project on time at school... best youtuber ever hahahha

UrThysis
Автор

how is it possible that overloaded operator -> for ScopedPtr returns Entity* but you're able to call "entity->Print()"?
entity is a ScopedPtr, the arrow operator returns Entity* and then you call Print()? I've played that part multiple times but it's not clear to me how this works :S

gnpb
Автор

"bonus section for getting the offset" omg, brilliant!!

ucmRich
Автор

Super creative at the end with the pointer operator.

abhisheksa
Автор

Thank you very much for this interesting video

danielphd