Pointers

Started by
8 comments, last by rip-off 12 years, 2 months ago
Hello,

I have been learning some c++ and realized that i have no freaking idea what a pointer is. I read through the book and i still dont quite understand. First it says you should use a referance rather than a poninter, so why use pointers than. and second, could someone please send me to mabye a video, book, or try explaining pointers to me in the noobiest fashion.

thanks for all your help :)
Advertisement
Using a pointer, you can describe the concept of "which object" at runtime. To access the object pointed to by a pointer, use the dereference operator (*), and use the address-of operator (&) on an object to make it "pointable":


int a = 3;
int b = 4;

// ptr is a pointer-to-an-int that doesn't yet point to an object
int* ptr = 0;

// make ptr point to either a or b (determined at runtime)
if ( rand() % 2 == 1 )
ptr = &a;
else
ptr = &b;

// modify the integer pointed to by ptr, which is either a or b
*ptr = 5;

// the output will either be
// 3 5
// or
// 5 4
cout << a << ' ' << b << endl;


A reference is an alias for an object -- and in most cases can be treated as a constant pointer (as in, the pointer cannot point to a different object) that cannot be null.

The code above using references can look something like this with similar output:

int a = 3;
int b = 4;

// a reference must be initialized when it is defined and cannot be reseated (see C++ faq lite link below)
int& ref = (rand()&1) ? a : b;
ref = 5;

cout << a << ' ' << b << endl;


There's also a turotial at cplusplus.com and a section about references on C++ FAQ Lite.

EDIT:
Washu has a point; I've edited my post to be less confusing. [size=2](And to save face.)
I dont know C++ howver I know they are in C#.

Surely a search of "Pointers in C++" will yield the webpage though.
When I was learning about pointers in C I often had this thought of variables or whatever the pointer was pointing to being houses which would require a lot of effort to move and a pointer being some guy on skates with a stick where you would order him to go point at an 'address' then bring him back, naturally this would be faster than bringing the house (address) back and forth.

Pointers have many uses, I sadly didnt read too much into C++ to care much for them, especially when references popped up, from what I believe it is much cheaper to use a pointer to get information (or change) than the actual object, however when to use them instead of references, that I dont know
Zeiger.PNG
Numbers on the right side of picture are memory addresses.


int b = 15; // Object
int * a = NULL; //Pointer

a = &b; //We use (&) to get address of (a) object.


Try to output in console: &b, b, &a, a, *a.

{...}
(( I am learning English. ))
A pointer is an unsigned integer (32-bits or 64-bits), that literally holds an address of memory.

No. Just no. Pointers are not integers, and don't ever mistake the conceptual machinations of imagining memory addresses as numbers to mean that they are simply "integers". They aren't. Heck on some machines they aren't even a single "integer", hello segmentation. This misconception is why so many people fail this quiz. The links were good though.
It should help you, pointer "stores" a address to another object. It is faster. Also, We use them to create a new objects using (new).

A pointer simply describes an object whose value provides a reference to an object of the referenced type. Its not "faster", that doesn't even make sense. Faster than WHAT?

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.


[quote name='fastcall22' timestamp='1329682700' post='4914603']A pointer is an unsigned integer (32-bits or 64-bits), that literally holds an address of memory.

No. Just no. Pointers are not integers, and don't ever mistake the conceptual machinations of imagining memory addresses as numbers to mean that they are simply "integers". They aren't. Heck on some machines they aren't even a single "integer", hello segmentation. This misconception is why so many people fail this quiz. The links were good though.
It should help you, pointer "stores" a address to another object. It is faster. Also, We use them to create a new objects using (new).

A pointer simply describes an object whose value provides a reference to an object of the referenced type. Its not "faster", that doesn't even make sense. Faster than WHAT?
[/quote]
Fast than hell... I meant, useful in functions, powerful etc.Maybe I overcoloured using this word.
(( I am learning English. ))
A pointer holds a memory address. You can expand that analogy into meaning that the pointer is like an arrow that points you to where you can find something.

If you know how classes work, say you're modeling a kind of store:[spoiler]// some people
Person john;
Person amy;
// cashier
Person* cashier = null; // we don't have a cashier yet
cashier = &john; // our cashier pointer now points to john.
// in other words, if we ever want to get to our cashier to make sure they're not sleeping...
cashier->wakeUp();
// equivalent to john.wakeUp().

// Say we fire john.
cashier = NULL;
// john still exists. the only change here is that cashier does not point to him anymore.

// this would be a bad idea because we don't have a cashier anymore, so we have no one to wake up:
// cashier->wakeUp();
// but we can freely hire another cashier:
cashier = &amy;

// if cashier was a reference, we wouldn't be able to switch cashiers. we wouldn't be able to fire them, either.
// instead, you would be turning John into Amy. That's not exactly what we want to happen...

// and now we can wake up amy:
cashier->wakeUp();
// say some new manager comes in, and he doesn't know about who works here or not. All he knows is that he needs to pay the cashier.
Manager bigboss;
bigboss.pay(cashier);
// inside Manager:
// --------------------
// Now, since we're using pointers, we can also make sure that we actually have a cashier before paying them:
void pay(Person* cashier) {
if (cashier != NULL) {
cashier->giveMoney(100);
}
}
[/spoiler]

Hello,

I have been learning some c++ and realized that i have no freaking idea what a pointer is. I read through the book and i still dont quite understand. First it says you should use a referance rather than a poninter, so why use pointers than. and second, could someone please send me to mabye a video, book, or try explaining pointers to me in the noobiest fashion.

thanks for all your help smile.png


Simply put, a pointer variable is a variable that points at another variable or object.
a reference variable is a variable that holds a reference to another object.

They are similar but with a few important differences:

1) You can directly manipulate pointers or reassign them.

2) References always point to an object and cannot be reassigned once created, pointers can be null or point to complete garbage (unallocated or deallocated memory for example).

Thus references are far harder to screw up with, as you have to initialize the reference when it is created it is fairly tricky to get a reference to an invalid object (It can be done by for example having a heap allocated object that takes a reference variable in its constructor but that requires that you use a pointer for the heap allocated object anyway).

Thus since references are pretty darn difficult to mess up with they should be prefered unless you need to use a pointer. (If you don't use pointers at all then i can't think of a way to mess up references either (If there is a way to mess it up anyway i'm sure someone will point it out :) ))

in C++11 you also have unique_ptr , shared_ptr and weak_ptr which are preferable to raw pointers in most situations. (usually unique_ptr should be prefered but there are situations where shared_ptr and weak_ptr can help).

The only time when raw pointers are necessary is when:
1) You need to do pointer arithmetics.
2) There is a significant performance overhead if using smart pointers. (unique_ptr shouldn't have any overhead and the overhead you get with shared and weak pointers is pretty irrelevant unless you abuse them)
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
I believe the question is better answered by demonstrating what pointers do.

There are a couple of common reasons to use pointers:

  • Optional semantics
  • Pass without copy
  • Write to a object outside the current scope
  • Create an object that must exist outside its current scope
  • Variable length data

Let us look at each in turn.

[indent=1]Optional semantics

The first is simple. We have a function, and one or more parameters are "optional", that is the function has meaningful behaviour even if the parameter is omitted. Let us imagine you want to write a function that turns a string into an integer. Sometimes, when the string is not an integer, you want to report that as a critical error. Other times, you are happy enough to go with a default - you want to pass that in and have it returned.

One way to write that is to have two implementations, one with a parameter indicating the default:

int tryParseInt(std::string string) {
std::stringstream stream(string);
int result;
if(stream >> std::ws && stream >> result && stream >> std::ws && stream.eof()) {
return result;
}
throw std::runtime_error("Failed to parse " + string + " as integer");
}


int tryParseInt(std::string string, int defaultValue) {
std::stringstream stream(string);
int result;
if(stream >> std::ws && stream >> result && stream >> std::ws && stream.eof()) {
return result;
}
return defaultValue;
}

Now, we have duplicated the essential logic here.

Another way of implementing this is to have the logic written once, and use a pointer parameter for the "defaultValue". If the pointer is null, then there is no default and we throw an exception.


namespace {
int tryParseInt(std::string string, int *defaultValue) {

std::stringstream stream(string);
int result;
if(stream >> std::ws && stream >> result && stream >> std::ws && stream.eof()) {
return result;
}

if(defaultValue) {
return *defaultValue;
}
throw std::runtime_error("Failed to parse " + string + " as integer");
}
}

int tryParseInt(std::string string) {
return tryParseInt(string, nullptr);
}


int tryParseInt(std::string string, int defaultValue) {
return tryParseInt(string, &defaultValue);
}

Now we only have one function which needs to worry about how to parse strings, and it is configurable in how it deals with errors. Here I have presented it as a hidden helper function. I'm not necessarily advocating this as the most appropriate solution here, I'm just trying to give a simple example of an "optional" parameter.

This idea of optionality can also apply to member variables. Consider a simple Guard Ogre class. If it has seen a Player, it must chase them. If it has not, then it must walk around search for a Player, or some other edible morsel. One implementation is to treat the currently chased player as an optional member. We can again use a pointer to indicate this.

We might write:

class OgreGuard {
public:
void update() {
if(!player) {
player = trySeePlayer();
}

if(player) {
chase(*player);
} else {
searchForPlayer();
}
}

private:
// ...
Player *target;
};

Again, there are other solutions.

In all cases, we are relying on the null pointer to provide a special value that can be tested for to indicate the presence of a value.

[indent=1]Pass without copy

Let us imagine we have a lot of data in our program - a container with millions of elements for example. A simple example might be searching this container for a given element, returning the index on success and a negative value on failure.

A naive implementation might copy the elements into the function, and then do a linear search on the copy:

int searchCopy(std::vector<int> v, int value) {
for(int i = 0 ; i < v.length() ; ++i) {
if(v == value) {
return i;
}
}
return -1;
}


An alternative to passing a copy is somehow allow the function to see the original data. We can do this with a pointer or reference:


int searchPointer(const std::vector<int> *v, int value) {
if(!v) {
// TODO: what to do if passed NULL?
}

for(int i = 0 ; i < v->length() ; ++i) {
if((*v) == value) {
return i;
}
}
return -1;
}


int searchRef(const std::vector<int> &v, int value) {
for(int i = 0 ; i < v.length() ; ++i) {
if(v == value) {
return i;
}
}
return -1;
}

Note that the searchRef looks almost identical to the searchCopy, with the added bonus that it avoids copying. Sweet! Also note that searchPointer has a corner case: how should it handle being passed NULL?

Finally, note that by using "const" the compiler will warn us if we accidentally try to change the vector. We will see why this is a problem below.

[indent=1]Write to a object outside the current scope

Now imagine we want to reverse strings. Like in the above case, the string might be very long. Again, the naive implementation might return a freshly reversed string:

std::string reverse(const std::string &input) {
std::string result;
for(int i = input.size() - 1 ; i >= 0 ; --i) {
resut.push_back(input);
}
return result;
}

However, we often don't care about the original order of the data, we just want it reversed. The naive implementation forces us to have both the original data and the reversed copy in memory, at least during the sorting process.

One approach is to instead reverse the data "in place". That is, instead of returning a fresh, sorted container, we simply re-arrange the elements in the original data. We can do this with a pointer or reference:


void reverseRef(std::string &string) {
int length = string.size();
for(int i = 0 ; i < length / 2 ; ++i) {
std::swap(string, string[length - i - 1]);
}
}


void reversePointer(std::string *string) {
if(!string) {
return;
}

int length = string->size();
for(int i = 0 ; i < length / 2 ; ++i) {
std::swap((*string), (*string)[length - i - 1]);
}
}

Again, we note that using a pointer here is uglier and introduces a special case where the input is NULL.

Note that this approach retains flexibility, because if the caller does care about the original order, they can always make a copy themselves and ask our function to sort this new copy in place:

int main() {
std::string original = readFile("reallyBigFile.txt");
std::string copy = original;
reverseRef(copy);
// Use reversed copy...
}


[indent=1]Create an object that must exist outside its current scope

In C++, any automatically allocated objects exist in a given scope. For function local objects, once the function ends they are destroyed. We can only copy them out of the function. Sometimes, a copy isn't sufficient. Some objects are not copyable (e.g. file streams, sockets, in fact most OS level resources have no intuitive copy semantics). What would a copy of std::cin be? In other cases object "identity" is important, as distinct from the object's values. In these cases, we can use new to allocate memory and create an object for us. This memory exists in a separate space to the automatic allocations, which means that when the function returns this memory is still valid. We receive a pointer to this memory.

We can return this pointer to calling functions, or possibly pass this pointer into a function which stores it (e.g. push_back the pointer into a vector). Conversely, if we do not store or return the pointer, we must de-allocate it, because when we return from the function the memory will not be cleaned up automatically.

[indent=1]Variable length data

Imagine you want to read data from a file, or the console, but you don't know how much there is in advance. Also, you do not have access to the standard library for some reason (yes, it is contrived =P).

Again, the naive approach is to use a "really big" array, and hope that you never are asked to read more data than that.

int main() {
const int MAX = 1000; // Surely enough???
int array[MAX];

int index = 0;
while(std::cin) {
if(index == MAX) {
std::cerr << "Sorry bud, out of luck. This program supports up to " << MAX << " numbers.\n";
return 1;
}

if(std::cin >> array[index]) {
++index;
}
}

// Use the data
}

We can trivially see that if we enter 1001 numbers into this program, it cannot handle it.

However, another approach is to dynamically allocate the variable length data:


int main() {
int allocated = 1000;
int *data = new int[allocated];

int index = 0;
while(std::cin) {
if(index == allocated) {
int more = allocated * 2;
int *pointer = new int[more];
std::copy(data, data + allocated, pointer);
std::swap(data, pointer);
delete [] pointer;
allocated = more;
}

if(std::cin >> data[index]) {
++index;
}
}

// Use the data
}

This is bascially what std::string and std::vector do under the hood.

[hr]
Some of these are advanced concepts, and you might not have come across them yet.

Please note I have not tested any of the above code, it is just for illustration.

This topic is closed to new replies.

Advertisement