Advertisement

Pointers have tangled my mind

Started by June 22, 2000 09:24 PM
8 comments, last by gimp 24 years, 5 months ago
It had to happen eventially, my relatively ''light'' understanding of how to talk to points had run dry... Here is the problem: I have a linked list within a class. The linked list holds a single data structure per node. On this struct I have a a bunch of properties line name, players etc. The class exports bits of data like name, players etc. The way I''d like to do this is by making these pointers that point to the ''current item'' in the linked list. And here is some code where I''ve lost the plot: Port = CurrentFullListNode->Data->Port; where(in order) : Port is a pointer to an integer CurrentFullListNode is the linked list node Data is a structure Port is an integer I''m betting this is a C pointers 101 lession that I''ve completely missed the point on and this is probably easy as putting a * in the right place... Thanks(this time for your patients) Chris
Chris Brodie
Ive had something like that before. This should work.

int *port = &CurrentFullListNode->Data->Port;

OneEyeLessThanNone

i breathe to live, i live to breathe
Advertisement
hahahahah Many thanks....

You know I just deleted a paragraph of myself responging about thinking that this could be done somehow with a reference to a pointer or something like that so I don''t have to keep reassigning..... Then I reread you mail a little more closely...

again.. many thanks for the help... This is a nice solution.
Chris Brodie
Whoops, spoke too soon...

char Name = &CurrentFullListNode->Data->Name;

Where name is a pointer to an array of characters. I get

C:\Program Files\Microsoft Visual Studio\VC98\myprojects\ListServerClient\Message.cpp(37) : error C2440: ''initializing'' : cannot convert from ''char ** '' to ''char''
This conversion requires a reinterpret_cast, a C-style cast or function-style cast

I''ve tried casting the right side a few different ways. I can''t think of how else to solve this...


gimp
Chris Brodie
Let''s see:

&CurrentFullListNode->Data->Name; - since Name is already type (char *), this is an expression with type (char **) [pointer to pointer to char]

char Name; - this is a data type with type (char)

So, your assignment is illegal because it tries assigning (char **) to (char). A char is only good for storing one byte, so for that assignment to make any sense you''d have to do this:

char Name = CurrentFullListNode->Data->Name[0]; - Both types are (char) so this is fine.

Sorry for adding to the confusion...
alllmost there....

I think the char ** thing worked .. thanks for that

Serverlist->Address however doesn't actually point to anything.

I might be stupid but here is what I'm doing (at least trying).

I create these :
        	unsigned int	*Port			= &CurrentFullListNode->Data->Port;	unsigned long	*Address		= &CurrentFullListNode->Data->Address;	INT32			*PingAverage	= &CurrentFullListNode->Data->PingAverage;	INT32			*Uptime			= &CurrentFullListNode->Data->Uptime;	INT32			*PlayersMax		= &CurrentFullListNode->Data->PlayersMax;	INT32			*PlayersCurrent	= &CurrentFullListNode->Data->PlayersCurrent;	char			**Name			= &CurrentFullListNode->Data->Name;	char			**Map			= &CurrentFullListNode->Data->Map;	char			**MapURL		= &CurrentFullListNode->Data->MapURL;        


I call that code every time the current node is changed. The set of pointers however still don't point to anything valid....

At the point when the pointers are meant to be assigned nothing happens. The pointer stays dangling and pointing at rubbish

Any suggestions?

thanks

gimp

Edited by - gimp on June 23, 2000 1:04:53 AM
Chris Brodie
Advertisement
gimp,

Rather than having seperate pointers to each item, why not have a single pointer to a struct of the same type as the Data member of your list items?

Then, instead of maintaining 9 pointers you would need only one...

                <whatever_type>    *CurrentData;CurrentData = CurrentFullListNode->Data;        


Then you can reference each of the members of CurrentData as you need them, without needing to refer back to CurrentFullListNode , eg. CurrentData->Port;

Remeber the second rule of programming - if something can go wrong, it will.


Edited by - Osc on June 23, 2000 6:23:54 AM
Gimp don''t worry your not the only one who was never taught pointers.
I still gotta grab the reference books now and then.
referencing , dereferencing, grrrr drives me insane.

Then there''s pointer to a pointer.
or pointer to a reference to a pointer to a pointer to a reference.

confusing stuff.
Don''t even think about references until pointers are wet-ware accelerated by you''re brain.

(they are different than dereferences, which go hand & hand with pointers)

A few examples that may help you...
int x;
int* lpx;

lpx = &x //the & gets the address of x, it ''converts'' an int to an int*, or anything to an anything* really

x = *lpx; //this will copy the contents of lpx (because of the *) to x - it is important that lpx points to something for this to work

char c;
char* str;

char** handle;

c = ''x'';
str = new("Test");
handle = &str //you gotta match up the number of *''s, str is char*, but we want a char** so we need to dereference once to get another * to make it a char**

delete[] str; //a char* is an array! when you delete, use the array delete!

//pretend we didn''t just delete str for a second
delete[] *handle; //this will delete str as well, stuff a * there to bring it down to a char* from a char**...

//or if you like

for(int i;i<4;i++)
{
delete str; //this will delte one char at a time
//All array''s (in C, and usally in C++) are pointers. the [] operator will ''remove a * level'' as well by indexing to just one of the items... so str[1] is just a char. which means you sue delete & not delete[]...
}



Any questions :-P
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
hehe I had lots of trouble with pointers too...ah, the good old days...


Pointers are just another type of variable, but they are a special type. They have their own set of operators (->, ., *) and also they do nothing but hold a memory address. That''s it.

Just to make things simpler, let''s say you are working in a 32-bit environment. That means that there are roughly 4 billion possible memory locations, or 4 GigaBytes (GB) of possible memory. If you had a 24-bit environment, that would be 16 MegaBytes (MB) of possible memory locations. In a 16-bit environment, that would be roughly 64 KiloBytes (KB) of possible memory locations.

This is all just simple probability math.

That is, 8 slots (bits), each with 2 possible values (0 and 1), can hold a maximum of 2^8 unique numbers in the range: {1,2,3...254,255,256}. We call that 1 byte. So, 1 byte can hold 256 unique numbers.

2 bytes together can hold 2^16 unique numbers in the range: {1,2,3...65534,65535,65536} because there are 16 slots in 2 bytes. 3 bytes can hold 2^24 unique numbers in the range: {1,2,3...16777214,16777215,16777216}. 4 bytes can hold 2^32 unique numbers. 8 bytes can hold 2^64 unique numbers. etc.

You are probably familiar with all the different types of integers in C++, right? That''s because a given calculation will produce a range of possible results. For example, take this simple equation: x = 2 * x. If x = 5, then the result is 10. If x = 6, the result is 12. And on and on... But what happens when x = 45872? The result may or may not be correct...it depends on the size of the integer type that we have used for x. If we used a short, the result must be in the range of {1,2,3...65534,65535,65536} because a short uses 2 bytes of storage. Since we cannot store the number 91744 in 2 bytes, the result will "overflow" and x will not hold the result we expect.

If we used a long for x, then the result will be as we expected, because a long uses 4 bytes. 4 bytes of storage holds numbers in the range of: {1,2,3...4294967294,4294967295,4294967296}.


What in the world does that have to do with pointers? Well, in a computer, the memory is allocated, deallocated, and used in a unit called a byte. Now that you understand what a byte is, and how it works, most operating systems used right now are 32-bit. That is, they can handle 4294967296 bytes of memory. To refer to the position of one of those bytes unambiguously, you need 32 bits, or 4 bytes. Pointers are just a special variable type that refers to a byte in memory, and thus the only data they store is 4 bytes.


In C++, pointers have types. All pointers are 4 bytes in size in a 32-bit environment, but they can refer to any amount of bytes. A char* is 4 bytes in size, but using that type tells C++ that you only want to refer to a 1 byte variable. A short* is 4 bytes in size, but using it tells C++ that you want to refer to a 2 byte variable. A long* is 4 bytes in size, but using it tells C++ that you want to refer to a 4 byte variable.

Think of pointers like the light and the motor on a film projector. The memory is the film, and the little light that shows each frame is the pointer. As the motor moves the film (memory) through the light (the pointer), the light refers to the next frame, and that is what is shown up on the screen. This keeps going on and on, exactly like a for() loop in C++ when you cycle through an array.

In fact, arrays in C++ are really just shorthand for pointers. When you create an array of 5 longs, you are telling C++ to allocate 20 bytes of contiguous memory and treat it as 5 4-byte integers. Here is an example:


void main()
{
long array[5]; // allocate 20 bytes

array[0] = 4; // use long #1, bytes 1-4
array[1] = 7; // use long #2, bytes 5-8
array[2] = 5; // use long #3, bytes 9-12
array[3] = 2; // use long #4, bytes 13-16
array[4] = 7; // use long #5, bytes 17-20
}



So, if we look at the array in memory, it looks something like this:


00 00 00 04; 00 00 00 07; 00 00 00 05; 00 00 00 02; 00 00 00 07



When you use an array subscript (the "[" and "]" brackets with a number inside) you are merely moving the pointer to another location in that array memory. For our long array, if we wanted to move to the 4th position, we would use "array[3]" and that gives us the fourth long stored in the array.

But how does the array work? Arrays use pointers to do their job!

The code "array[3]" is really just like shorthand for this code: "*(array + 3)". In the latter code, we are moving the pointer 3 longs forward and then dereferencing it so that we get the value stored there. That''s all the subscripts really do.

And here''s a shocker: the variable we named "array" is really just a pointer to long #1. Since the memory is contiguous, all we need is the start of the array and the number of the long that we wish to use. Knowing those two things we can find the address of any element of that array.

So we can safely re-write the array example like this:


void main()
{
long array[5]; // allocate 20 bytes

*(array + 0) = 4; // use long #1, bytes 1-4
*(array + 1) = 7; // use long #2, bytes 5-8
*(array + 2) = 5; // use long #3, bytes 9-12
*(array + 3) = 2; // use long #4, bytes 13-16
*(array + 4) = 7; // use long #5, bytes 17-20
}



And the output would be exactly the same.


This brings us into the subject of pointer math -- really, C++ pointer math in 32-bit is easy.

Basically, C++ remembers the type of your variables and automatically adjusts the math by the right sizes. For our array example, when the EXE was built the compiler simply took out "*(array + 1)" and replaced it with "*(array + 1 * sizeof(long))", and did the same for all of the statements. But what happens when you have an array of shorts? The compiler uses sizeof(short) in its calculations, without us even having to know about it.

So we can let the compiler worry about the size of the types -- yet another example of the beauty of type-checking. We tell the compiler with pointer math that we want to do something with long #3, rather than having to tell the compiler that we want to use bytes 9,10,11,12.

This code will work just fine (notice the math is the same):


void main()
{
short array[5]; // allocate 10 bytes

*(array + 0) = 4; // use short #1, bytes 1-2
*(array + 1) = 7; // use short #2, bytes 3-4
*(array + 2) = 5; // use short #3, bytes 5-6
*(array + 3) = 2; // use short #4, bytes 7-8
*(array + 4) = 7; // use short #5, bytes 9-10
}



Now that you know about pointers (or your brain needs to reboot), let''s look at pointers to pointers. Trust me, compared to the other stuff, this''ll be a cinch!

Remember in C++ how the default is to pass variables by value? Temporary variables are created for the functions, right? Well, temporary pointers are created for the functions, too. (Pointers are just a special type of variable!)

So, the following won''t work as we expect it to:


template &lttypename pointer_type>
inline void safe_delete(pointer_type pointer)
{
delete pointer;
pointer = NULL;
}

void main()
{
long* mylong = new long;

safe_delete(mylong);
}



(Just ignore the template stuff for now if you don''t like it. Templates just make this function type-safe with any pointer...)

While delete works just fine with the copied pointer (after all, delete is interested in the pointer''s value and not the pointer), setting the copied pointer to NULL doesn''t change mylong in main(). How do we solve this problem? Well, pointers are just variables, aren''t they? We''ll use a reference!

Just change the definition of safe_delete to this:


template &lttypename pointer_type>
inline void safe_delete(pointer_type& pointer)
{
delete pointer;
pointer = NULL;
}



Now mylong in main() is set to NULL, just like we want!!


Now that you understand that pointers are just variables too, pointers to pointers are easy. (yeah, right)

A pointer to a pointer just holds the address of another pointer. Have you ever worked with DirectX? You know, when you call DirectDrawCreate() and you have to give it the address of your pointer? DirectDrawCreate() creates a new DirectDraw object for you, and it sets your pointer to the new object. How did they change your pointer''s value?

Take a look at this example:


void main()
{
// declare a 4-byte integer and set its value to 5
long x = 5;

// create a 4-byte pointer, and set its value to
// the address of x
long* pointer_to_x = &x

// now create a 4-byte pointer to a pointer, and
// set its value to the address of pointer_to_x
long** pointer_to_pointer_to_x = &pointer_to_x;
}



See what''s happening? When we use the name x, this is called accessing x directly. When we use pointer_to_x to access x, this is called accessing x indirectly, or indirection . The same thing goes for pointer_to_x and pointer_to_pointer_to_x -- pointers are really just variables.

In other words, you can store the location of a pointer variable in memory.

Remember our example with safe_delete()? The reference operator (&) told the safe_delete() function to use the actual pointer, right? How do you think the compiler does that? With pointers, of course! The compiler simply hides it from you, making you think you are using the actual pointer, when in reality you are using a pointer to a pointer.

Check out this version of the safe_delete() function, which works just fine:


template &lttypename pointer_type>
inline void safe_delete(pointer_type* pointer)
{
delete (*pointer);
(*pointer) = NULL;
}



Now that is (basically) what the compiler does behind-the-scenes with references. Pointers work all kinds of magic in C++ -- in fact, if we couldn''t get the addresses of variables in C++ we would have a very hard time doing anything. Pointers allow us to directly manipulate any kind of memory however we like (although the OS might shut us down if we try to access another program''s memory ).

Pointers are also a great way (and also the only way) to work with dynamically allocated memory, like with the new() and delete() operators.


Now I''ll explain the operators that were created especially for use with pointers.

1) REFERENCE OPERATOR (&variable )

Returns the memory address of a variable.


2) DEREFERENCE OPERATOR (*pointer )

Returns the variable at the pointer''s address.


So, you can get the address from a variable, and you can get the variable from the address.


I hope that helped a little bit... If there is anything I haven''t covered (or anything I didn''t explain very well), then please tell me about it.

Good Luck!




- null_pointer
Sabre Multimedia

This topic is closed to new replies.

Advertisement