The Arrow Operator in C++
Вставка
- Опубліковано 14 вер 2017
- Patreon ► / thecherno
Twitter ► / thecherno
Instagram ► / thecherno
Discord ► thecherno.com/discord
Series Playlist ► • C++
Thank you to the following Patreon supporters:
- Samuel Egger
- Dominic Pace
Gear I use:
-----------------
BEST laptop for programming! ► geni.us/pakTES
My FAVOURITE keyboard for programming! ► geni.us/zNhB
FAVOURITE monitors for programming! ► geni.us/Ig6KBq
MAIN Camera ► geni.us/t6xyDRO
MAIN Lens ► geni.us/xGoDWT
Second Camera ► geni.us/CYUQ
Microphone ► geni.us/wqO6g7K
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.
Cheers, that was confusing me
i guess im randomly asking but does any of you know of a method to get back into an Instagram account?
I stupidly lost the login password. I appreciate any tricks you can give me
@Augustus Zain i really appreciate your reply. I got to the site through google and I'm trying it out atm.
Seems to take a while so I will get back to you later with my results.
@Augustus Zain it did the trick and I finally got access to my account again. I am so happy!
Thank you so much you saved my ass :D
@Dominick Ty no problem :D
4:58 did you notice that ? XD
yeah, nice easter egg Cherno
You focus on tutorial really well. :D
Yes I did! Damned auto-complete!
Freudian slip
This is a scam "Matteo Roberto" and "Zaire Rodney" are both bot accounts going around videos and commenting this, and then the seconded bot account comments this under it to make it seem more legitimate
The last part is amazing.
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 . It would look like this:
#include
uint64_t offset = ( (uint64_t)&((Vector3*)0)->z );
Great video, thanks Cherno!
pretty "Simple"
yes, this is simply:
member address = object address + offset, since object address is equal to 0
member address = offset
all we need to do is get a pointer to the member then cast it to int to get the offset
(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.
Finally a good explanation! Thank you!
I think you could also create an actual object and just subtract. The method with zero just avoids the subtraction (after all, subtracting zero doesn't change the result). This is a bit easier to understand??
Vector3 myV;
int offsetZ = &(myV.z) - &(myV);
It doesn't work like that in g++; getting cant convert float* to int
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!
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
4:57 Cherno wants us to shut the f*** up... O_o
Hahahah
lol
lol
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)
That last trick was super cool and super useful, thanks for showing us that Chern!
You're so good and selfless. I've learned many tricks so far.
This literally helped me deliver my project on time at school... best youtuber ever hahahha
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;
}
Love your videos! Thanks
(int)&((Vector3*)0)->z
...yeaahhh this is starting to look like proper C++ :D
actually more like low level shit, more like C
04:57 You definitely type stfu more often then struct
Thank you very much for this interesting video
pretty cool stuff!
"bonus section for getting the offset" omg, brilliant!!
Woah that last trick. I'm curious how this works behind the scenes now.
here's my take on it:
you cast a 0 into a Vector3*. now, since a pointer is a variable that stores a memory adress, it translates that into memory adress 00000000, so that's the adress it's now pointing to. now, when you try to use the -> operator to get the value of one of the variables of the object that the pointer is supposed to be pointing to, you get some kind of an error since there's actully no object being pointed to and therefore there's no value at the memory adress.
however, even though there's no underlying object, a pointer to a class/struct still knows how an object of that class/struct is supposed to be structured in memory. so when you use the & to get the memory adress the variable is supposed to be stored at, instead of trying to get the data stored there, it actually returns a valid value .
Since the only pieces of data this particular Vector3 object needs to store in memory are member variables X, Y and Z, the first variable, X is stored at the adress the pointer is pointing to , 00000000. Since all 3 data members are floats, and a float is 4 bytes, that means that X takes up memory adresses 00000000 , 00000001 , 00000002 and 00000003. Then 00000004 through 00000007 is Y and 00000008 through 00000011 is Z. But when you're returning the adress of a variable, you only get the adress where the 1st byte is stored and the rest of it is stored in the following adresses. And then you just cast it into an int to convert the adress value from hexadecimal to decimal and send to cout.
might be a mistake somewhere in there so take it with some reserve but that's what I think
Thanks for the reply! I have a few questions for you :D
If you could answer any of them, I'd be grateful!
1. If we were to use the number 1 instead of 0 (nullptr) when casting and retrieve the offset of Z, would we get 9 instead of 8?
2. Does this mean we can also get the offset of a member function /method/, perhaps some other stuff such as the vtable?
3. The way this works looks very similar to how arrays do. You have a starting address and an offset:
int array[10];
array[0] = 5; // same
*(array + 0) = 5; // same
*array = 5; // same
Does this mean that we can also use the square brackets when accessing class members? For ex:
Vector* v = new Vector;
v[2] = 360.0f; // would that be the same as
v->z = 360.0f; // this?
Let me try to answer the 3rd question myself.
If the array is of type int, accessing the 2nd element of it would shift the offset by 4 bytes, because an int is 4 bytes and that's how the array is spread out in memory.
However, an object may have members of different sizes, chars, floats, doubles, even other objects, so when using [ ] the compiler wouldn't know by how many bytes to shift in order to get to a "valid?" member.
If it's possible to do, it would probably be designed to shift by 1 byte. Still don't know though haha
about the 1st question - yes , you would get a 9
2nd one - I have no idea, since I'm actually fairly new to programming myself and didn't even touch upon function pointers yet
3rd - I don't think you can use the bracket operator like that with class objects. you can however overload it to do what you want on a case by case scenario but there's the question of how much sense that would make and the overload would have to be different for each class, and even more importantly, the only way I can think of to achieve such behaviour is to perform a bunch of if tests in the operator overload definition , and that would just slow your program down needlesly when there's a perfect functional and simple solution, the -> operator
but the real answer to all these questions is - when you have a question that you understand well enough to put it into actual code, just open your Visual Studio ( or whatever you're using) and try putting it into code and see what happens. That's how I get to the bottom of most things I don't understand at first. When checking out how things work in memory just use & to get the adress back and also you can monitor the variables using memory view in the debugger. check out cherno's episode on debugging if you haven't yet. that's the one I found to be the most useful when it came to developing my own understanding of how memory works
@@h.hristov Although it's been 3 years, I just wanna clarify the third question, maybe for other people. In the case of int array[10], this array when getting evaluated or passed to function decays to a pointer to int of the first element.
array[0], I think, calls the opertor[] version of pointer type. Idk, something like
int& operator[](const int index) {
return *(&m_pointedInt + (index * sizeof(m_pointedInt));
}???
For the next line, array + 0 I think calls the operator+ version of pointer type which works similarly to that of my "made up" operator[] for pointer type but it doesn't dereference at the final step.
And for the last line, *array just deref the array which decays to an int pointer that points to the first element.
For v[2], I think you would get a compile error since there is no overloaded operator[] for Vector type.
I answered this myself with no documentaion lookup, feel free to correct me.
nicely done. I haven't done C in a very long time. Gave me some good insight.
I love this!
What the heck? My dude that is awesome! I am inspired by these skills
This is awesome...really awesome
keep up the good job
Thank you!
Solution to precision error
#include
struct vector3
{
float x,y,z;
};
int main()
{
int offset = (long)&((vector3*)nullptr)->x;
std::cout
That was weird, thanks.
That was great and what I exactly needed. Thank you very much.
I didn't understand many things but the video is awesome!
the last part is so cool
woah stunned again!! :o
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 z
Super creative at the end with the pointer operator.
Didn't work in my compiler. Even directly copy pasted!
If the last example gives you precision loss error, try changing the type from int to long, like this:
long offset = (long)&((Vector3*)nullptr)->z;
Basically, the pointer is too big for the int type.
Thanks
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
I have the same problem. I know how to use it, but logically the function signature doesn't make sense to me.
You are right to be confused... it's a compiler thing that interprets that as A.operator->()->B; I don't know since when this is the case but it seems this behavior was added for smart pointers so you can use them as normal pointers. Not sure why they decided to do it this way.
I have read a bit more about this a few weeks ago. operator-> applies itself recursively as often as it can.
I kept getting error messages from that part.
the Entity* operator->() returns a pointer to an Entity object ( it is basically a function). when you call entity->print(); what actually happens is the entity-> is a function call
so the entity-> returns an Entity pointer which you can use to access the print() function of the object the pointer is pointing to. it is similar to entity.GetObject()-> (the function cherno defined earlier in the video). both return an Entity pointer.
I hope one day I will understanding programming as well as you so that I will be able to talk about it as fast as you. lol This content was super useful though, thank you.
Offset example was awesome
The arrow operator is just a shortcut so you don't have to manually dereference functions and it can also be used to make your code cleaner.
Beautiful last trick.
That STFU easteregg at 4:57 lol
thanks alot Cherno, now I understand what to do with callback pointers, before I rly confuse ... 😧
Even after 5 years. It's pure GOLD
That last trick tho...we know we are all thinking "Damn hes good" **mind blown**
You are the best
It's a concise C++ revelation
Your last trick of derefrencing the nullpointer was nice
you're very fast!
Maybe someone wants to find this: For the part "int offset = (int)&((Vector3*)nullptr)->x", you may want to change "int" to "long" (or other types that have enough space for interpreting an addr) on Linux OS because of the difference between (32- 64-).
4:33 That was loud. I felt that.
Hey man, just a suggestion. I know it has nothing to do with this particular video, but before you start with OpenGL , do you think you could make a video explaining the differences between a library and an API?
I find it interesting that the GL in OpenGL stands for graphics library yet OpenGL is classified as an API. Would be cool if you could talk about this a bit
Aman Saxena oh cool, I think I understand what the basic idea is now.
I tried watching a few videos on the topic online and people would just go on about it for like 15 mins throwing all kinds of terminology left and right and I'd be like wtf. thx
A library has an API - that's how you use it - by consuming it's Programming Interface. API can even be used to refer to single functions - so you might say that your new library provides 500 API's. And you can talk about the API surface that some library or platform provides.
You left out the left arrow operator, , which is what I clicked on this video for. I remember these being useful & you do a great job of covering all sorts of things.
left arrow operator and long arrow are not real operators. They are a trcik where you mix multiple operators to generate what looks like a new operator. Left arrow mixes the minus operator with the less than operator.
4:57 Muscle memory kicked in
you didn't mention using with this->x.
Omg I didn't turn on notifications but I was scrolling through my subscriptions and guess what, your video was posted 1 minute ago
Pradyumn Kejriwal wow
Iohannes Isaac I'm not on patreon
very good Video
Interesting. The arrow notation is how all classes in Perl are used, IIRC.
great
The last part is what offsetof does behind the scenes.
what that was way too fast i think i have to go back a few dozen videos
Same :( I feel less bad now seeing I am not the only one who does not understand at first go
Now I understand meaning of 2 in vector2 😅
for anyone having error "cast from float to int losses precision"...you are on 64 bit system while cherno have 32 bit..so use-
long offset=(long)&((Vector3*)nullptr)->x;
it would work fine
in the following lines of code, we can remove the '&' without any change in the functionality apparently in 'Entity& entity = *ptr;' line. Is there any reason to keep it as is or could it be useful in any case?
Entity e;
Entity* ptr = &e;
Entity& entity = *ptr;
entity.Print();
Is it posible to access/modify arrays by using arrow operator?
I tried the last trick but keep getting this message: "error: cast from ‘float*’ to ‘int’ loses precision "
Edit: after a little searching, this works: *auto theOffset = (intptr_t) &((Vector*) nullptr) -> y;*
mee too same problem
This may help:
int offset = (long)&((vector3*)nullptr)->x;
Is the last example in Memory Address 0 (nullptr)?
Why do you use the pointer to reference the Entity class?
When do you start the OpenGL series?
Maybe 23 hours ago?
Why do yo use Visual Assist X? It is better than JetBrainsResharper C++? Do you recommend using one?
Thanks for your time, the C++ serie is awesome and the ones that are coming look really good
brain.exe deleted itself
how about standard macro offsetof defined in ?
Usually I think your videos are way too long and unnecessarily detailed, but this one was very satisfactory. Good job
Can someone explain what does ((Vector3*)0) does here?
(last trick) if you're using Xcode : use uintptr_t instead of int
How does this work with classes?
Гений
Hey @The Cherno how come the output of the third element in a float struct is 8 bytes when a float can only store 4 bytes?
@Peterolen Thank you so much
How do you make a smart pointer for an object array? like Classname* obj = new Classname()[3];
Why did you declare Entity& entity = *ptr ? I mean if the *-mark here is reverse for pointer, to get the value in ptr, then isn't "Entity entity = *ptr;" more understandable ?
We need a guitar tutorial...
I keep getting errors in the Last part
what is the meaning of Entity& entity?
cool
How does the last trick work considering the Vector3 struct has not been instantiated anywhere?
May be, because it's not calculating the actual address, just Offset which compiler can calculate using their dataype
Thank you Cherno your C++ Video series is so much help for me. One question I have, you type ScopedPtr entity = new Entity () ; , but we know new always returns a pointer but entity is not defined as pointer, how that's working. Could you answer that?
His ScopedPtr class has a constructor which takes an Entity pointer, so thats used to create his object.
why doesn't it give nullpointerexception
You can do the arrow like this
Struct vertex
{
Int x, y, z;
};
Vertex *vertex;
Vertex->x;
Etc..
devin edwards you cannot access x in your example because vertex is not pointing to a valid memory address.
@@stewartzayat7526 #include
struct vector3
{
float x,y,z;
};
int main()
{
vector3* vec;
int offset = (long)&vec->x;
std::cout
If arrow operator has an implicit dereference shouldn't the -> on the null pointer cause error?
observe the ((Vector3)nullptr) part.. it's casting the nullptr to Vector3 pointer hence ->x is ok
Whats the const before the body of the function already? I forgot
It means that the called function cannot modify the class members at all. Except if you tag such members as mutable (this is the only exception) but members without the mutable keyword is still remaining unmodifiable for a const function.
why do you have to cast the address into an int again? The address of the pointer is already an int, so what is casting doing actually there? Also, I wrote the same code and I got an error that casting from pointer type to int loses information so I think this is really related to compiler settings.
try this instead:
#include
uintptr_t offset = (uintptr_t)&((Vector3*)nullptr)->z;
shouldn't it be like
entity->-> x; //??
since (entity->) returns (Entity*) witch is a pointer
Hello. I have a question. Let's say that we have:
int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int c = 0;
and then we do:
a[++c] = b[++c];
What will be array a and how and when is c incremented?
I am asking this because I if run this in different versions of C++ I get {1, 2, 5} in C++ 17 and {1, 2, 6} in C++ 14 or C, but in Java is {1, 6, 3}. Also, there are other combos like a[++c] = b[c], a[++c] = b[c++].
Please respond anyone. Thanks!
Can you make the stack and heap video
He did, it's called "Instantiating objects in C++".
ScopedPtr entity = new Entity();
could someone explain what is going on here? :( So far ive known you need a pointer for heap allocation (in other words to use "new" keyword) but here i am really suffering, declaring an object with "ScopedPtr" type and obviously it is not a pointer, also there is an "Entity" on the left side... :D *noobvibes*
The code will be compiled error in my system. error cast from ‘float*’ to ‘int’ loses precision [-fpermissive] on the Ubuntu64. And must be added -fpermissive and get the right answer.
int offset = (int)& ((vector3*) 0)->x; so fun
a->b == *(a).b ; when "a" is a pointer of a data(object), we use "->" operator.
actually (*a).b
6:27 ..........wha
this is horrifying.
תודה רבה ממש עוזר