Skip to main content
gamedev.net
🔒 Locked

Pointer Insomnia

Started by blanky 19 replies 2.6k views
Original Post
blanky
blanky
Okay guys, I'm a C/C++ 'programmer', the quotes because I'm great at everything but pointers, even though I can do, still kinda confuse me, and since pointers are practically what those languages are all about, I can't really call myself a programmer :(. I've read tutorials/articles, read books, googled my head off, and all I find is the occasional

int myint = 9;
int *pmyint = &myint
*pmyint = 4; // now myint is equal to 4
Okay, I understand that, absolutely, I understand pointer arithmetic as well (Iterating and the like), however, I get confused when someone does (I'm making this up, not that it actually works, but you'll get the idea) something like (*(char)&something), so they use them all weird, &(*thing(&something)), remember, I made that up, but that's what it looks like to me, I think it has something to do with casting, I might be able to understand that. However, where the big confusion for me comes in, is when people create, say, functions that take in either pointers or references, which ones and why? Like, someone takes in an int *thing, but inside the function, treats it as &thing, or the other way around, I think that means that now the function can have direct control over the variable which thing points to, but, man it's hard to explain. Like, basically, they pass one thing (say, a pointer), and but treat it another way, how do you know why to treat it that way? Er, nevermind, in the process of typing this up I think I get it, but if you guys can help out I'd really appreciate it, this thing's driving me crazy. I know that: int *thing *thing = the actual thing it points to thing = the address? So if: int mynum = 9; int *thing = &mynum *thing = 9 thing = address of mynum ? And basically you pass references or variables to not duplicate variables and therefore speed things up? But why pass references/pointers, which one? And when? function(int *thing) or function(int &thing)? Thanks, I hope I didn't confuse anybody, I'm so confused myself, really would appreciate any help :'( EDIT: For example, what's that all about, when you do something like (*WORD)&something, or something like that, which supposedly makes the first WORD sized part of variable something equal to whatever, or something like that? [Edited by - blankdev on April 16, 2006 3:37:21 PM]
CTar
CTar
Quote:
Original post by blankdev
int *thing

*thing = the actual thing it points to
thing = the address?

So if:


int mynum = 9;
int *thing = &mynum


*thing = 9
thing = address of mynum ?

And basically you pass references or variables to not duplicate variables and therefore speed things up?

Yes this is correct, except that "or" should be an "of". If you meant or then no, with variables you just pass by value (in C and C++ at least) so you will still be doing the copying.


Quote:
But why pass references/pointers, which one? And when? function(int *thing) or function(int &thing)? Thanks, I hope I didn't confuse anybody, I'm so confused myself, really would appreciate any help :'(


You pass references if you just want to avoid the unnecessary copy, an reference can be though of as just an alias. The interresting thing about a reference is this:
int mynum = 9;
int& mynum_ref = mynum;
mynum_ref = 9;
&mynum_ref = address of mynum
So it's not a copy, just another name for mynum. So if you did this:
mynum_ref = 10
then mynum would also be 10, since they refer to the same object. You can think of a reference as a pointer with simplified syntax, but the compiler can implement them in any way it wants. The reason some people pass by pointer to avoid copying data is that C doesn't support references, so code with a C interface can't use references (think Win32). Another reason to pass by pointer (which is not related to copying) is that dynamic polymorphism is generally achieved with pointers.
iMalc
iMalc
The difference between function(int *thing) and function(int &thing) is precisely that in the former NULL is a valid parameter value, but in the later, it is not. References are preferred in C++, but if you want NULL to be a valid parameter, then use a pointer.

Here is a simple example which continues on from your first example:
int myint = 9;int *pmyint = &myint*pmyint = 4; // now myint is equal to 4int &rmyint = *pmyintrmyint = 7; // now myint is equal to 7
What these last two lines do: First we declare a reference, as noted by using & instead of *. A reference is like a pointer, but operates at a different level of indirection than pointer, to basically hide the fact than a pointer is even involved.

When you assign to a pointer you give it the address of something, which usually means you have to get the address of that thing, using & (your line 2 above)

When you assign to a reference (which you can only do once) you give it the actual thing, and it internally gets the address of that thing all by itself. So in order to give it the int that pmyint points to, we have to dereference the pointer to get back to the actual value. We then assign this back to the reference, which internally gets the address of this int. We could instead have done
int &rmyint = myint;

You may have been scared off by something like
long foo = 0xFEDCBA98;*((char*)&foo) = 0x12;
Okay, lets work out this left hand side.
Start at the variable, foo. It has an & in front of it. That means we're taking its address. So that means we get a long* or a pointer to a long. However, just in front of that we're telling the compiler to cast it to a char* or pointer to a char. This is fine, all pointers are just memory addresses, so it will quite happily pretend that the memory address now points to a char. The memory address is of course the same size, regardless of what it points to. Next it takes that whole thing and puts a star at the front. Now just like your line 3 above it means that we're now looking at what that all points to. It is of course still pointing to foo, but it no longer sees foo as a long, it thinks it is looking at a char. So the assignment now only causes it to overwrite a char.
The result is that foo now equals 0xFEDCBA12 (little endian machine here).
LessBread
LessBread
Quote:

...something like (*(char)&something), so they use them all weird, &(*thing(&something)), remember, I made that up, but that's what it looks like to me, I think it has something to do with casting...


Yep, that has to do with typecasting. iMalc explains it well, I just wanted to add that typecasting requires using a typename. A variable name will produce a compile time error.

So, if thing is the name of a variable (as in the examples),

&(*thing(&something))

won't compile. The leading asterisk would dereference the thing variable and you can't typecast one variable as another variable. If thing is the name of a type, it won't compile either because a typename isn't a variable and can't be dereferenced. The asterisk must follow the typename to indicate that the typecast is to a pointer to that datatype eg. (typename*)&something.

What is happening here can be thought of as "overlaying" a new type onto the underlying memory.

"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
blanky
blanky
Lol thanks, yeah I made that up, since that's what it all looks like to me. Anyways, thanks all three of you, very helpful, I really really appreciate it.

Quote:

Okay, lets work out this left hand side.
Start at the variable, foo. It has an & in front of it. That means we're taking its address. So that means we get a long* or a pointer to a long. However, just in front of that we're telling the compiler to cast it to a char* or pointer to a char.


Ah okay, so instead of treating it like a pointer to a long, we're treating it like a pointer to a char? Okay (I hope) I got it.

Quote:

This is fine, all pointers are just memory addresses, so it will quite happily pretend that the memory address now points to a char. The memory address is of course the same size, regardless of what it points to.


Okay :)

Quote:

Next it takes that whole thing and puts a star at the front. Now just like your line 3 above it means that we're now looking at what that all points to. It is of course still pointing to foo, but it no longer sees foo as a long, it thinks it is looking at a char. So the assignment now only causes it to overwrite a char.
The result is that foo now equals 0xFEDCBA12 (little endian machine here).


Ohhh! I think I get it! So instead of making a pointer variable, and making it equal to a reference of another variable and all that, we do that all in one line? Ohh I get it now! I was gonna ask 'how is 0x12' a char? But I'm guessing it's its ASCII equivalent in hex? I get it now, thanks sir! And since you have a little endian machine (or whatever) it adds it to the end? And it doesn't add it but overwrites it, I get it now! (Hope I didn't misinterpret anything), thanks guys! Thanks iMalc!
LessBread
LessBread
Quote:
Original post by blankdev
I was gonna ask 'how is 0x12' a char? But I'm guessing it's its ASCII equivalent in hex?


A char variable doesn't necessarily equate with an ascii character.

"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
blanky
blanky
Oh, yeah I think I knew that, I just figured. :D I'm so happy I know this now, thanks guys.
xeddiex
xeddiex
Blandev, I would like to add that, a pointer of type X is just a variable like any other variable, except that, it stores a different kind of value: an intregal address (to another variable). Passing by address/reference as oppose to value, in a function call, will usually provide lesss overhead, make the function call more efficient, reflect changes to the variable in the calling function and, potentially speed up the function call.

So, if you had an object (e,g.: struct, class - a declaration of any of these is also a variable) which consisted of 30 data members, of which each were at least 1 byte long, your object would be at least 30 bytes long, and, passed it by value to function Foo, the functions stack/data frame would create space for 30 bytes on every call to it - on the stack (temporary storage) - copy the original objects data members values over and, any changes made to it would not reflect the object (variable) in the calling function. If the object was passed by address or reference, function foo's stack frame would create space (on every call) for the size of a pointer/reference. The size of a pointer is typically 4 bytes on a 32-Bit system and, any changes you make to this object via the pointer/reference would reflect on the object in the calling function. See the potential benefit pointers/references can be?

Hasta entonces =)
- xeddiex
one..
blanky
blanky
Ah, thanks :) But then why not always do it by reference/pointer instead of variable? Just wondering.
xeddiex
xeddiex
Quote:
Original post by blankdev
Ah, thanks :) But then why not always do it by reference/pointer instead of variable? Just wondering.


Sometimes, there's no need to. For example, if you don't care about modifying an object passed into a function, then pass by value, else pass by address/reference.

Clarification: I say address and not pointer because the type and identfier in the actual argument of the function definition is the actual declaration of *a* pointer to type X, whereas, the address being yielded in the formal argument (function call) is usually not an address being yielded from a pointer, but an address of a normal variable such as int.

- xeddiex
one..
moeron
moeron
If you have no need to modify a variable that is not a POD(int,char,float,etc..) type your generally better off passing it by a constant reference than simply by value.
moe.ron
BeerNutts
BeerNutts
Quote:
Original post by moeron
If you have no need to modify a variable that is not a POD(int,char,float,etc..) type your generally better off passing it by a constant reference than simply by value.



However, there are times when it is better to pass something by it's address when it's a big structure or something. Take this example:
struct{   int a;   int b;   int c;   char *cSomeString;} SomeStruct;//look at these two functions prototypesvoid SomeFunction(SomeStruct StructVariable);void SomeFunction(SomeStruct *StructPointer);


with the 1st functions, the compiler will have to copy the whole structure to SomeFunction, whereas, with the 2nd function, it will only have to pass the pointer address, which is just a register (or 1 space on the stack).

So, basically, if you have a large structure, sometimes you should pass by pointer (or reference) to save on performance.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)
blanky
blanky
Okay guys, I really appreciate the help you've given me, I've finally (almost) learned what I've never come around to learning. But anyways, I seem to be having some problems with the following, I might not be able to recreate the situation entirely 100 percent correct, but I think you'll catch on. So it's got to do with functions, well, not really. Watch, say we have:

void function(int *apples, int &oranges, int bananas){   // stuff}


So we're accepting three arguments, a pointer to an int, a reference of an int, and an integer called bananas. So what's the problem? Well, it's pretty complicated. So, we know that we can (Can we?) pass other things besides that, for example

function(&var1, *var2, var3);


Right? So basically what we're doing is allowing the function to modify var1 and var2's values right? Anyways, we could also (Er, can we? Would it be different?) pass this stuff:

function(*var1, &var2, var3);


Er, could we? I have the slightest feeling that var1 would be a pointer to a pointer, cause it's accepting a pointer in the first place, er, so would you do this? (If var1 is already a pointer (int *var1)).

function(var1 /*etc*/);


So basically, I'm confused as to what to pass how. And it gets more confusing to me when we're actually using the variable within the function, I've seen source that for example, you pass a pointer, and treat it as a pointer within the function

void function(int *apples, int &oranges, int bananas){   *apples = *apples++;   // etc}


However, at the same time, I've seen this happening

void function(int *apples, int &oranges, int bananas){   apples blah blah blah // instead of *apples   // etc}


Hmm...apples without the little pointer operator would mean it's address right? and *apples is the actual value of apples? But then, what would &apples be? Isn't that also the address of apples? Or what it points to I mean, unless one is the address of the pointer and one is the address of what it points to? Is there even such thing? I don't think a pointer has an address but then again what do I know.

Then there's the

void function(int *apples, int &oranges, int bananas){   &apples blah blah blah // instead of *apples or apples   // etc}


So, here we're modifying apples' value? That is, the value of the variable it points to? Man, I'm very confused and I know that you guys are getting this a different way than what I mean, if I could only find that one thread...but I have no idea of what forum of what site it'd be in. It was something like, 'if one of the function's arguments is a pointer, and you pass a pointer, then it'd be a pointer to a pointer', something like that, obviously I'm not saying that's right, but it was something like that, a chart, so I could know what to pass and how to treat each thing. I'm sorry guys, I have a feeling I don't make sense and this is kind of confusing, so, nevermind :'(

The forum thread said something like this, I don't remember correctly at all so this is most likely wrong:

If you have

int *thing;

and you have function prototype

function(int *arg1);

If you pass it like so

function(int *thing);

It'd treat it X way

If you pass it like so

function(int thing);

It'd treat it Y way

Sorry for the confusion guys
xeddiex
xeddiex
Quote:
Original post by blanky
Sorry for the confusion guys


Yeah, you are very confused.

A pointer (e.g.: int *apples) is a variable. All variables and anything that holds data, such as the above, has to itself have some memory associated with it to be *able* to hold other data. apple has an address (== memory) for itself, aswell as what it currently holds - an address of another variable. When you use the ampersand on apple (&apple) you are producing the address of the variable apple. When you use the variable apple itself (apple), you are exposing the address of the variable it currently holds (or points to; the pointee). When you use the dereference operator on apple (*apple), you're applying the operator not to the apple variable, but to the address (of another variable) apple currently holds, essentially, you're accessing the final-value (an int) of the variable that apple currently holds, or points to.

I rec you watch this video: Binky Pointer Fun Video [grin]

EDIT: The following is a group of links that teach-up on pointers:

more Pointers
some more Pointers
and even more Pointers

The later one is one that I read through a couple years ago when I was learning pointers too.

- xeddiex
one..
blanky
blanky
:'( I have a feeling I'll never get this, thanks for being so kind though, I'll keep trying :'( Thanks for the links and help :')
xeddiex
xeddiex
Quote:
Original post by blanky
:'( I have a feeling I'll never get this, thanks for being so kind though, I'll keep trying :'( Thanks for the links and help :')


Don't feel that way man. I too, was once in your situation. I thought I would never, ever learn to use or understand how pointers work. I just kept on trying and never gave up. I posted questions on it, here (under another name), and one day, it just finally clicked. Pointers are VERY easy to work with once you know them. They are also extremely powerful. I cannot imagine working without them in C, and maybe even C++. I started with C - for 3 years, and now I'm currently learning C++. I can happily skip the pointers part though, hehe, thanks to my previous experience in C.

Just don't get discouraged, it's normal to feel like this is an extereme topic to comprehend and that you'll never get it, but, trust me, if you don't give up, you'll eventually get it too. It's not a matter of IF, it's a matter of WHEN.

- xeddiex
[p.s, Don't be afraid to ask questions - someone will always be willing to help you out]
one..
BeerNutts
BeerNutts
Pointer are one of those things that, once you finally get it, it all clicks into place. When you understand it, you'll be like, "Ahh, it all makes sense now" and you'll feel like you can do anything. Understanding pointers leads to a deeper understanding of computers in general as well.

As for your examples, there's a few problems. Let me show some examples that might or might not help.

int Var1 = 1;  int Var2 = 2;int Var3 = 3;// I use 'p' in front to denote a pointer, and 'r' to denote a referencevoid func1(int *pParam1, int &rParam2, int Param3);// to call func1, the 1st parameter must be a pointer to int type, the other two must be int types// this is validfunc1(&Var1, Var2, Var3);// you must take the address of one of the variables (Var1) //and pass it as a pointer.  Now, you are literally passing the //address of Var1 to func1// The program will also pass the address of Var2 as the second parameter since it is a reference, but it is all hidden from you by the compiler.// Var3 is just passed by value, so it is actually sending 3 as the value to func1void func1(int *pParam1, int &rParam2, int Param3){// to change the value passed by pParam1, you have to dereference it with the * operation    *pParam1 = Param3;    // now you can change the actual value of rParam2 as well    // but you don't need to dererefernce it    rParam1 = Param3;    // this next line does nothing, since the actual value of Param3 won't change when you exit func1    Param3 = 0;}// the below will ***NOT*** workfunc1(*Var1, &Var2, Var3);// to use the * operator, the variable must have been a pointer, but Var1 is an int// &Var2 won't work because you must pass an int to the function, but you are now passing a pointer to an int, and the compiler will barf// Var3 is OK

now, what will the following program do?

int main (void){    int Var1 = 1;    int Var2 = 2;    int Var3 = 3        printf("Variables are %d %d %d\n", Var1, Var2, Var3);         func1(&Var1, Var2, Var3);    printf("Variables are %d %d %d\n", Var1, Var2, Var3);    }


I hope I helped.
My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)
Xai
Xai
One of the most important ideas for understanding poitners ... a picture.

DRAW the layout of you objects in memory on a piece of paper ... using a fairly standard size box for each byte, inside the box, write what is stored IN the memory, above the box write the address of that memory.

so if I have:

int x = 3;
char text[] = "123";

I might draw it like:

0x80284852
---------------------
| 03 | 00 | 00 | 00 |
---------------------

0x80284856
---------------------
| 31 | 32 | 33 | 00 |
---------------------

And I usually don't worry about endianness until it matters. In the above, I wrote the hex values seperate, which I only do in real life when I'm starting out, or very concerned about exact byte placement ... normally I would just do:

0x80284852
-------------
| 3 |
-------------

0x80284856
-------------
| '123\0' |
-------------

And then we add the idea of a pointer and a reference to this system:

int x = 3;
char text[] = "123";
int *currentValue = &x
char *currentLetter = text; // an array name by itself is the same as a pointer to the first item.

0x80284852
-------------
| 3 |
-------------

0x80284856
-------------
| '123\0' |
-------------

0x80284860
-------------
| 0x80284852|
-------------

0x80284864
-------------
| 0x80284856|
-------------

See how I very clearly distinguish which are pointers, by putting strange numbers that start with 0x80 in the box (that's just an arbitrary fake address, because rarely in your program do you use ints over 2 billion).

I also typically draw arrowed lines from the pointer box to the box it points to ... showing the logical relationship. I use pencil for these lines when debugging an algorithm (as I do for the values in the boxes) because they may change while running).

References get a little harder if you are trying to correctly model them in terms of compiler treatment, but when it comes to function passing, I model them EXACTLY as I would const pointers in the diagram.

0x80284864 (ref)
-------------
| 0x80284856|
-------------

When using both pen and pencil (I always use pen as personal habit, unless I need rewriting), I write const values and reference lines in pen ... since they don't change.
blanky
blanky
Thanks guys! I'm sorry I just realized that you guys replied to this lol, thanks thanks thanks.

EDIT: I really appreciate it. See, my problem's not with the, er, 'actual' pointers themselves, I mean, of course it is but, not completely. I know what they are, etc. It's just that I get confused with things such as, say, the complicated pointer casts and what to pass when.

So when it accepts a reference, you pass a normal variable type, not a pointer to a variable. When it accepts a pointer, you pass a reference to a variable right? Is there more to this? Thanks.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.