views:

253

answers:

5

I think I understand references and pointers pretty well. Here is what I (think I) know:

int i = 5; //i is a primitive type, the value is 5, i do not know the address.
int *ptr;  //a pointer to an int. i have no way if knowing the value yet.
ptr = &i;  //now i have an address for the value of i (called ptr)
*ptr = 10; //Go to the value in ptr, use it to find a location and store 10 there

Please feel free to comment or correct these statements.

Now I'm trying to make the jump to arrays of pointers. Here is what I do not know:

char **char_ptrs = new char *[50];
Node **node_ptrs = new Node *[50];

My understanding is that I have 2 arrays of pointers, one set of pointers to chars and one to nodes. So if I wanted to set the values, I would do something like this:

char_ptrs[0] = new char[20];
node_ptrs[0] = new Node;

Now I have a pointer, in the 0 position of my array, in each respective array. Again, feel free to comment here if I'm confused.

So, what does the ** operator do? Likewise, what is putting a single * next to the instantiation doing (*[50])? (what is that called exactly, instantiation?)

+5  A: 

** is just * twice, so a pointer to a pointer.

When put next to a type, * binds left, not right. Saying new char *[50] is actually new char* [50] and instantiates an array of 50 char*.

Ignacio Vazquez-Abrams
+2  A: 

The statements made in your first code chunk are all correct.

char **char_ptrs = new char *[50];

...means you have an array of 50 char *s.

Your assesment of

char_ptrs[0] = new char[20];
node_ptrs[0] = new Node;

is correct as well.

** simply means a 'pointer to a pointer'. It's not an operator.

When you write

new char *[50];

...you are saying 'allocate storage for 50 char *s'.

George Edison
Sometimes the syntax is confusing. I prefer: "char** char_ptrs = new char* [50];" To make it explicit, but this also has its downsides.
Stefan Mai
+4  A: 

A few comments:

*ptr = 10; // Doesn't need to "go get" the value. Just overwrites it.

Also:

char **char_ptrs = new char *[50];
Node **node_ptrs = new Node *[50];

It is easier to think that you have two arrays. However, technically (and as far as the compiler is concerned) what you have is two pointers. One is a pointer to a (pointer to a char) and the other is a pointer to a (pointer to a node).

This is easily seen by the declarations of your variables, which, by the way, can be most easily read right-to-left:

char **char_ptrs

Reading right to left: char_ptrs is a pointer to a pointer to char

Putting a * next to a pointer is properly called dereferencing that pointer. Since arrays do not technically exist, operator [] on arrays is also a dereferencing operation: arr[i] is another way of writing *(arr + i). To properly understand this, you need to be familiar with pointer arithmetic.

More than one consecutive *s: each one dereferences the result of the expression it operates on. So when writing:

char c = **char_ptrs;

what happens is:

char_ptrs is a pointer to a pointer to a char. Dereferencing it once (for the rightmost *) gets you its value, which is a pointer to a char. Dereferencing that value (for the leftmost *) gives you its own value in turn, which is a char. In the end, c contains the value of the char stored in memory at the place where the pointer pointed to by char_ptrs (in other words, the first pointer in your array) points.

Conversely, if you write **char_ptrs = 'a'; then you are changing the value in that memory location.

Jon
+1 well said. excellent link too.
Stephano
Node* tmp = node_ptrs[0]; So is that correct?
Stephano
@Stephano: sure it is.
Jon
I think Jon earned the accept here. Kudos to Ignacio as your answer is direct and to the point.
Stephano
Thank you. Your question was quite good for educational purposes, so +1 from me as well.
Jon
the pointer arithmetic part is important. good answer.
klochner
I think I should also promote the RTL reading trick I mention because of its wide usefulness: what is the difference between a `const char*` and a `char* const`? :-)
Jon
+1: Very nice answer
ereOn
+1 for reading right-to-left.
Cam
A: 

Clarifying the first section:

int i = 5; // i is a primitive type, the value is 5, the address is retrieved via &i.
int *ptr;  // an unassigned pointer to an int
ptr = &i;  // ptr now point to the address of variable i
*ptr = 10; // access (dereference) the value through ptr and change it to 10 (same as i=10)

There is no ** operator, just * operator. Like the others have said, ** declares a pointer to a pointer. Because you are declaring arrays of pointers, and pointers are declared with the * operator, you need to declare them as such when allocating memory for them with new. Hence, you have:

char **char_ptrs = new char *[50]; // allocates memory for 50 contiguous char* (pointers)
Node **node_ptrs = new Node *[50]; // allocates memory for 50 contiguous Node* (pointers)

Pointers to pointers do not necessarily have to declare arrays. You can just as well have a regular pointer pointed to by another pointer, as in:

char i = 'p';
char *myptr = &i;
char **mysecondptr = &myptr;
zdawg
+3  A: 

If you find the * notation hard to read use typedef to help make your code easy to read.

typedef char*      CharPtr;
typedef CharPtr*   CharPtrPtr;
// Alternative to the line above
// typedef char**     CharPtrPtr;

// When you call new. You get a ptr to the type you are newing.
// new int returns an intPtr. new char returns a charPtr
CharPtrPtr char_ptrs = new CharPtr[50];

// So new CharPtr returns a CharPtrPtr
// In this case we return a pointer to contigious
// chunk of memory large enough to hold 50 CharPtr objects.
Martin York
No, don't. Hiding the fact that something is a pointer is very poor style, IMHO.
anon
I agree with Neil in the general case. But Ptr is fine as you are not hiding it (IMHO). But since this is a learning excersise just to get used to using pointers it works. When you get old and lazy like me an Niel the * is just as easy to read and faster to type :-)
Martin York