Pointers in c++

Started by
9 comments, last by Glak 18 years, 8 months ago
i'm a bit new to c++ programming and i was wondering about those things called pointers, now i know they are used to return the memory address of a variable, but why would you want to do that and why to some functions need the memory address of variable in their parmater list and some dont.
Advertisement
Usually when a function requires a pointer, it's because it's trying to be efficient. A pointer is only a 32-bit integer(Edit: when using a 32-bit compiler), so if you have a big object (like an image), it's easier to just pass a pointer of it. If you were to pass the object itself, then the compiler would replicate it taking more RAM and extra time. Also, using pointers is the only way to have dynamic objects (like when you don't know how many you need), so a function may be using your pointer and using either the 'new' keyword or the 'malloc()' function to create something on the heap.

Think of a 2D RPG. You can either have each NPC townperson having their own image, or you can just create one image and give them all pointers to it. Memory efficient!
A pointer is used to store the memory address of a variable, the address of operator (&) is used to retrieve the memory address of a variable.


There are three ways to pass a variable into a function.


1) Pass-by-value

#include<iostream>void function(int IntegerParameter){IntegerParameter = 3;}int main(){int IntegerVariable = 0;function(IntegerVariable);std::cout << IntegerVariable ;}


Above we have an example of pass-by-value. When the IntegerVariable is passed into the function, the integer is used to initialize the IntegerParameter variable, which is a totally seperate variable from the IntegerVariable which was passed into the function. As a result, when the value 3 is assigned to the IntegerParameter variable, it does not modify the IntegerVariable that was passed into the function. The cout statement verifies this, by printing 0.


2) Pass-By-Pointer


#include<iostream>void function(int* pIntegerParameter){*pIntegerParameter = 3;}int main(){int IntegerVariable = 0;function(&IntegerVariable);std::cout << IntegerVariable ;}


With 'pass-by-pointer' function calls, the address of the IntegerVariable is used to initialize the pIntegerParameter variable.

When we use the dereferncing operator (*) on the pIntegerParameter, we obtain the integer at the address which is stored in the pIntegerParameter variable, which in this case is the IntegerVariable. The above code then assigns the value 3 to the variable at the address stored in the pIntegerParameter variable, and so the cot statement prints 3 instead of 0.


3) Pass-by-reference


#include<iostream>void function(int& IntegerParameter){IntegerParameter = 3;}int main(){int IntegerVariable = 0;function(IntegerVariable);std::cout << IntegerVariable ;}


The above code is an example of pass-by-reference. A reference is a variable which is an alias for another variable. When we pass the IntegerVariable into the above function, the IntegerParameter becomes an alias for the IntegerVariable, and so assigning 3 to the IntegerParameter assigns 3 to the variable that it is an alias for, which is IntegerVariable in this case. The cout statement prints 3.


So the above examples give you a possible use of pass-by-pointer as opposed to pass-by-value. Though the above example does not show much of a difference between pass-by-pointer and pass-by-reference, which both give the same result.

The difference between references and pointers as function parameters is that you can pass a null value to a pointer parameter in a function, but you cannot pass a null value to a reference parameter.

#include<iostream>void function(int* pIntegerParameter){if(pIntegerParameter != NULL)*pIntegerParameter = 3;}int main(){function(NULL);}


Here we have passed in a value of NULL (which is 0), which signifies that the pointer does not point to a valid object, it points to nothing. We can then check to see if the pointer points to something valid in the function, as demonstrated above.

With references we cannot pass in a NULL value, a reference is an alias for an object, and so must be initialized with an object.

So the basic guidlines are

1) If you want to modify the variable, you need to pass by pointer or reference
2) If you want the function to be able to be called with a NULL value, use a pointer parameter
3) If you dont want the function to be able to be called with a null value, use a reference.
4) If you want the object passed into the function to be copied, use pass by value.

It should be noted that there a performance issues with using pass-by-value for classes, and usually const references are prefered over pass-by-value in this case.



Pointers just make things faster, take less memory, and if the function needs to change something so it cannot get the value or it will be useless.
and it's also used to pass arrays, you cannot pass a whole array to a function, you must use a pointer.
Well said MXZ!
Wow...Wow. That was fast. Seriously though, thanks a bunch guys, this just cleared a hell of alot up for me, I am so saving this page for future reference *sniff*, I can't thank you guys enough.
Sorry to interrupt on this pointer tips here.. i got a very tough pointer issue to settle.. hope you guys can help out.. sorting things out for me..

Check out the example below.. :
Quote:
------------------------------------------------------------------------------
int _tmain()
{
IPin *pinout = 0;
hr = GetUnconnectedPin(srcfilter, PINDIR_OUTPUT, &pinout);
...
...
return 0;
}


HRESULT AVOp::GetUnconnectedPin(IBaseFilter *anyfilter, PIN_DIRECTION pinway, IPin **ppin) // Receives a pointer to the pin.
{
ppin = 0;
IEnumPins *enumpin = 0;
IPin *pin = 0;

hr = anyfilter->EnumPins(&enumpin);
if (FAILED(hr))
{
Console::WriteLine("No enumerated pins");
return hr;
}
while (enumpin->Next(1, &pin, NULL) == S_OK)
{
PIN_DIRECTION pinwaythis;
pin->QueryDirection(&pinwaythis);
if (pinwaythis == pinway)
{
IPin *temppin = 0;
hr = pin->ConnectedTo(&temppin);
if (SUCCEEDED(hr)) // Already connected--not the pin we want
{
Console::WriteLine("Connected to the pin ");

}
else // Unconnected--this is the pin we want
{
enumpin->Release();
ppin = &pin <--------------- Successful and assign

return S_OK;
}
}
pin->Release();
}
enumpin->Release();
// Did not find a matching pin.
return E_FAIL;
}
--------------------------------------------------------------------------------


Take the example above, by right.. i am passing the address of the "pinout" variable to "ppin" of the the function right?

Okay, since in the function, let say it is successful,

ppin=&pin --> in the function

so ,

pinout = ppin = &pin --> outside the funtion

It should be like pass by reference right? however, i didnt get the expected result.. can anyone tell me why ?

i have been puzzled in this for almost 2 weeks time.....
Thank you
Thank you so much Guys
int _tmain(){	IPin *pinout = 0;	hr = GetUnconnectedPin(srcfilter, PINDIR_OUTPUT, &pinout);	...		...		return 0;}HRESULT AVOp::GetUnconnectedPin(IBaseFilter *anyfilter, PIN_DIRECTION pinway, IPin **ppin) // Receives a pointer to the pin.{	ppin = 0;	IEnumPins *enumpin = 0;	IPin *pin = 0;		hr = anyfilter->EnumPins(&enumpin);	if (FAILED(hr))	{		Console::WriteLine("No enumerated pins");		return hr;	}	while (enumpin->Next(1, &pin, NULL) == S_OK)	{		PIN_DIRECTION pinwaythis;		pin->QueryDirection(&pinwaythis);		if (pinwaythis == pinway)		{			IPin *temppin = 0;			hr = pin->ConnectedTo(&temppin);			if (SUCCEEDED(hr)) // Already connected--not the pin we want			{				Console::WriteLine("Connected to the pin ");							}			else // Unconnected--this is the pin we want			{				enumpin->Release();				ppin = &pin; <--------------- Successful and assign										return S_OK;			}		}		pin->Release();	}	enumpin->Release();	// Did not find a matching pin.	return E_FAIL;}

Wow, you managed to get the word pin in almost every variable name :)

One obvious problem in your code is the very first line of the GetUnconnectedPin function. It nulls the last parameter (perhaps you intended *ppin = NULL;?), which means that it is not pointing to the pinout pointer anymore. You may also want to check if your Next function in the enum actually fills the pointer with a new pin...

Tom

EDIT: you might consider thinking of more descriptive names for many of your variables. Especially a global variable called 'hr' is evil.

EDIT2: and a similar problem resides in the line ppin = &pin. That should be *ppin = pin;.

[Edited by - dimebolt on August 18, 2005 10:56:09 AM]
Just to clarify:

If you pass an object by value to a function, the compiler copies the object passed by calling the object's copy constructor. For non-modifiable fundamental (basic) types this is OK. For large non-modifiable objects, this copy is expensive and is want to be avoided by use of a pointer to const, const pointer to const, or const reference. Since the syntax for passing by value or by reference is identical, here are some things to watch out for.

Pointer to const: if the function doesn't modify the pointer in any way (increment, reassign, etc.) prefer a const reference for syntactical reasons:
Object object( 2.3, "My object" );Object* pobject = new( Object(object) );func1( pobject );  // unclear whether object can be modified or notfunc2( *pobject ); // at a glance, this implies a const object

Similarly, prefer a pointer over a reference for large mutable objects, also for syntactical reasons:
Object object( 2.3, "My object" );func2( object );  // who knows?func1( &object ); // clearly, there's a high chance this object may be modified

The less you have to go hunting through your code to figure out function parameter types when a bug occurs, the more productive you can be.

:stylin:

Mxz.rate( UP );
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Pointers are used logically to represent the notion of "these two places refer to the same data, and if the data is changed in one place, the change should be seen in the other place".

// The following is all horrible coding style, but serves to prove the pointstruct Thingy {  int* sharedData;  void mingle(const Thingy& other) {    cout << *sharedData * *(other->sharedData);  }}int main() {  int theValue = 3;  Thingy a;  a.sharedData = &theValue; // tell the Thingy to look at the local data  a.mingle(a); // outputs 9, naturally.  theValue = 5;  a.mingle(a); // even though we didn't directly change any contents of the  // Thingy, it refers to data that changed, and can thus output 25.  Thingy b;  b.sharedData = &theValue;  a.mingle(b); // still 25  theValue = 15;  a.mingle(b); // 225; the 'a' picked up the value as well as 'b' did.}


Often (especially in OO programming) we need to think in terms of "object identity"; if I have an egg in my pocket, it may be "equal" in all ways that I care to measure to an egg that's in the fridge, but it is not the same egg. If I wish to express a desire to scramble the egg that's in the fridge, then instead of carrying around an 'identical' egg and scrambling it (that isn't doing what I really wanted), I make a mental note about where the egg is (i.e. in the fridge), and then I go get THAT egg and scramble it.

One powerful application of the technique occurs when objects hold pointers to other objects *of the same type*. You might think of this like a bunch of people holding hands; their hands are pointers to the other people. So if you hold hands with your girlfriend (I know you're intending to be a programmer - bear with me ;) ), then she holds *your* hands, not the hands of someone who looks just like you, who in turn holds the hand of someone who looks just like your girlfriend, who holds the hand of some *other* you, ad infinitum. Which is where the pointers come in; they let you refer to an already existing object instead of needing a new one.

class Person {  Person* held; // To simplify things, we'll assume people only hold hands with  // one hand.  std::string name; // so that we can identify people.  public:  void unlinkHands() {    // Let go of the person we're holding hands with.    if (held) {      // Remember who we were holding hands with      Person * previousHeld = held;      // Let go      held = NULL;      // Tell that other person to stop touching me!      previousHeld->unlinkHands();      // When that person in turn calls unlinkHands() back on me,      // I will have already dropped the hand, so 'held' will already be NULL      // which lets the process end.    }    // Otherwise, I wasn't holding anyone's hand, and don't have to do anything.  }  void holdHandOf(Person* other) {    // Make sure the other person can hold hands with me    other->unlinkHands();    // Grab the other person's hand    held = other;    // and make sure s/he feels it!    other->held = this;  }  void report(ostream& os) {    // Output a report of who I am and who I'm holding hands with.    os << "Hi, I'm " << name << ", and I'm";    if (held) {      os << " holding hands with " << held->name << " :)" << endl;    } else {      os << " lonely :(" << endl; // if not holding anyone's hand.    }  }  Person(const std::string& name) : name(name) {}}// Play around with it!// Person sally("Sally");// Person bob("Bob");// sally.report(cout);// bob.report(cout);// sally.holdHandsWith(&bob); <-- The '&' here gets a pointer to bob.// sally.report(cout);// bob.report(cout);// bob.unlinkHands();// sally.report(cout);// bob.report(cout);// bob.holdHandsWith(&sally);// sally.report(cout);// bob.report(cout);


Optimization is a secondary concern - the common case is of passing a pointer (or much better in C++, a reference) to a function, to avoid copying the object. The rationale is that changes to the parameter can be seen by the caller, so the function can just make those changes. (The function doesn't necessarily care about those changes, but it will end shortly anyway, so it doesn't matter - its own reference will be "forgotten" at the end when the function ends and the parameters go out of scope.) If the data doesn't need to change, you can pass a const reference (the 'const' keyword is asking the compiler "please check to make sure I'm not changing things that I decided shouldn't change in my design phase"), and the "changes" can still be "seen by the caller" - all zero of them. :)

This topic is closed to new replies.

Advertisement