tags:

views:

260

answers:

10

i'm self-teaching c++ and i get how pointers work. but the doc i'm using is quite literal and the examples don't really go into why or when pointers would be used. a couple of real world examples would help me retain the knowledge.

A: 
void print_values(int* iptr, int size)
{
    int i;
    for (i=0; i < size; i++)
    {
        printf("%d ", *(iptr++));
    }
    printf("\n");
}

int main()
{
    int table[256];
    memset(table, 0, sizeof(table));
    print_values(table, sizeof(table)/sizeof(int));
}

Or like a array of functions (example):

#define ___FUNC_B { func_1, func_2, func3 }
void ( __closure __fastcall * __FUNC_B [__FUNC_MAX] )( TObject* ) = ___FUNC_B;

Usage objects by pointers is in many cases is better:

CClass *ptr = new CClass();
/* something */
delete ptr;

If you have many objects and you must for example get it in a some arrange (eg. sort) you can use pointers for sort pointers to objects non objects:

vector <CClass*> Vptr;
for (i=0; i < 100; i++)
{
    Vptr.push_back(new CClass());
}

sort(Vptr.begin(), Vptr.end(), __cmp_func);
/* something */

while (!Vptr.empty())
{
    delete *(Vptr.begin());
    Vptr.erase(Vptr.begin());
}

For dynamic memory alocation in C language:

char *memory = (char*) malloc(1024);
if (memory == NULL)
{
    exit(1);
}

/* you have alocated 1KB in memory */
memory = (char*) realloc(2048);
if (memory == NULL)
{
    exit(1);
}

/* you have alocated 2KB in memory */
free(memory);
/* you have nothing */
Svisstack
+8  A: 

You use pointers when you want your objects to exist longer than the current stack. You can also use them to avoid copying objects into containers.

// TODO: Remember to call DeleteObjects() when you're done here!!
std::vector<MyObject*> Objects;

void Test()
{
    MyObject *const pObject = new MyObject();
    Objects.push_back(pObject);
}

void DeleteObjects()
{
    std::vector<MyObject*>::iterator it = Objects.begin(), itEnd = Objects.end();
    for (; it != itEnd; ++it)
    {
        delete *it;
    }
    Objects.clear();
}
Mark Ingram
this is a very clearly stated "goal" of pointers. thanks.
monsto
Note that, contrary to this simple example, you should rarely ever deal with "naked" pointers referring to dynamically allocated objects. (Mark's example is silent on the important question who's responsible for freeing the objects put into that container.) As all resources, dynamically allocated objects should be managed by well-implemented types dedicated to this task. There's many of these classes readily available in the standard library (strings and other containers). If nothing exists for your particular use case, there's always smart pointers.
sbi
Yes, this example is a little lightweight, but I didn't want to confuse matters by adding in references to std::tr1::shared_ptr / boost::shared_ptr etc. I've added a little comment as a reminder to free the memory.
Mark Ingram
+1  A: 
  1. Uploading multiple data from a function. The caller would supply addresses of memory locations to be overwritten by the function
  2. Dynamic memory allocation. Allocators would return pointers to newly allocated objects.
  3. Passing array arguments: pass address instead of copying, to save performance for constant data.
Pavel Radzivilovsky
good examples. helped quite a bit.
monsto
+3  A: 

This is not an easy question to give a short and easy answer to, and I'm sure there's plenty of resources out there talking about pointers. Basically, whenever you'd like to use indirection (which may be even recursively) you need pointers.

Say for example a binary tree data structure, where each node have pointers to it's left and right sub trees, where either might a pointing to 0 (or NULL, meaning invalid pointer) to signify there's no sub tree there. This structure might look like this (not very C++-y, but that's a different story)

struct TreeNode
{
  TreeNode* left;
  TreeNode* right;
}

You can't use anything BUT a pointer in this case, as it'd be an infinitely large structure.

roe
One thing that is often forgotten is that the logical "pointer" used to describe data structures such as a list or binary tree, is not the same as pointer (address variable) used in low level languages such as C. The pointer can be for example a table index, or a structure that contains disk sector and track numbers. You can definitely create tree structure even with a high level language such as BASIC that does not have pointer variables.
PauliL
+1  A: 

Pointers are useful when you need a function to return more than one variable. As an example, consider you are shopping at a grocery store. Each product has a name and a price. The name would be a string and the price a double. If there were a function called "buy", and you wanted to return both the name and price of the item, you would need to use a pointer.

Silvae
excellent example, thanks.
monsto
No, you would not, you do need to pack the two together but even a plain C `struct` would do.
Matthieu M.
+1  A: 

This is not really a C++ matter, rather it is a C matter. For the beginner level, I would love to recommend the book Understanding Pointers in C

taskinoor
+1  A: 

A simple example use of pointers is in linked lists. More info on wikipedia.

JLWarlow
Now that's TERRIBLE.
Matthieu M.
OK, maybe not the best example, but a simple easy to understand one. Answer suitably edited :-)
JLWarlow
A: 

whats an example usage of c++ pointers?

Pointers address the following issues:

  • avoiding copying large chunks of memory around. That was in C at least, in C++ the preferred method is to use references. You can still use pointers if you want though.

  • allocating memory at runtime. This is needed when you have no idea at compilation how much memory you will use.

  • remembering the addresses of (member) functions for deferred calls (and callbacks).

  • allocating memory that outlasts it's current scope (it's still allocated after scope is finished).

  • sharing an object between multiple entities (multiple objects, multiple threads and so on). This means that you can pass the address of an object around and all entities using it will access the same data, not identical copies of it.

Sometimes pointers are also used as handles. That is, if you want to allow client code to uniquely identify a chunk of data without caring (or knowing) what the data is, you cast the address of the data (the pointer) to int/some other type and pass it around as a handle. This is commonly found in APIs that offer handles to client code but don't allow client code access to the real data (see use of WinAPI's HANDLE, HWND and so on - those are pointers in the internal implementation, but you don't know - or care - what the actual data is in order to use it).

utnapistim
A: 

A pointer can be considered a simple variable, but instead of saving a value it saves an adress to the position of the memory that stores a value.

Think the memory as a block of drawers and in each drawer you can put a value, to make it easier to find the values, you numerus the drawers. So, a position of memory would be a drawer and the block would be the full memory.

So, when you create a pointer, for example:

int* drawer = 0;

You are referring to the drawer which is labelled with the number 0 and contains an integer value, now you may think, ok, but, how can I get that value? Well, it's simple:

int value = *drawer;

By the same way you can store a new value on that drawer (memory address):

*drawer = 15;

Now comes the fun, the block of drawers is magic, and a drawer can take you to another drawer of the block, if we label them with the same number, the value that one stores is the same in the other:

int* drawer = 0;
int* drawer_copy = drawer;
*drawer = 15;

And what happens? That "drawer_copy", which is referring to the address 0 as "drawer" allows you to access to the integer value 15.

We can also save the address of a normal variable, we use the "&" prefix to get that adress:

int value = 15;
int* drawer = &value;

If we do this now:

value = 5;

"*drawer" will return a 5.

As you can see, pointers allow the user to have more control on the memory and to have reserved memory permanently, once you have declared a pointer you can preserve an address and access to it whenever you want. :)

aitorkun
A: 

Okay, I've seen so many terrible responses that I feel myself obligated to add yet another one.

First things first: we are talking C++ here. So many C uses are completely invalidated.

Terrible uses of pointers

You should learn RAII: this example is completely unsafe in the face of exception

// BAD
void func(size_t n)
{
  int* array = new int[n];
  // .. use array
  delete[] array;
}

// GOOD
void func(size_t n)
{
  std::vector<int> array(n, 0);
  // .. use array
}

Rule of Thumb: if you see delete, you're doing it wrong. Chances are that if you see new too, though it's not as true because of argument forwarding issues.

Use references whenever possible

// BAD: Contract: do not pass NULL
void func(int* i);

// GOOD
void func(int& i);

Whenever the passing NULL does not make sense (and you don't need to rebind), use either a plain value or a (const) reference instead of a pointer.

Good uses of pointers:

Aliasing

void printSorted(std::vector<BigType> const& values)
{
  std::vector<BigType*> references = from(values);

  std::sort(references.begin(), references.end(), ByPointer());

  std::transform(references.begin(), references.end(),
                 std::ostream_iterator<BigType>(std::cout, " "),
                 Dereference());
}

Optional Result

Object* find(Key const& key);

this is equivalent to:

boost::optional<Object&> find(Key const& key);

but quite less verbose.

Clone method

The use of a bare pointer as the return type of the clone method is mandated by the Boost Cloneable concept:

struct Base
{
  virtual ~Base() {}

  virtual Base* clone() const = 0;
};

There is a sound reason: taking advantage of covariant return types for the overloading of virtual methods. This allows us to write:

struct Derived: Base
{
  virtual Derived* clone() const { return new Derived(*this); }
};

Thus taking full advantage, when cloning from a Derived const&, of the fact that we know that what is returned is at least a Derived.

It is up to the programmer to take care of the allocated memory unfortunately, so it must be used in conjunction with Smart Containers:

std::unique_ptr<Base> u = derived.clone();
Matthieu M.