I am trying to learn C++ via some web tutorials. I don't have a compiler available to me, otherwise I would try this out. I'm not sure what is meant by a const pointer. Does that just mean it always points to the same memory address? Why would you ever want to do that? Would the following code be legal?
...
int * const aPointer = new int;
... //do something with aPointer
delete aPointer;
... //do something else, including possibly more 'new' statements
aPointer = new int;
...
A simple way to remember how const works with pointers is to remember that it always applies to whatever is to the left of it, unless it's the left-most keyword, in which case it applies to the right.
Examples:
Pointer to a constant char:
The pointer can be changed to point to something else, but the char it initally points to cannot change value.
const char * p;
Constant pointer to a char:
The pointer cannot be changed to point to anything else, but the char it points to can change value.
char *const p;
Constant pointer to a constant char:
The pointer cannot be changed to point to anything else, and the char it points to cannot change value.
const char *const p;
Const pointer could mean a few different things. I recommend checking out the C++ FAQ Lite on the matter.
You can have:
const int* p;
int* const p;
const int* const p;
All three mean different things. Yes, it's kind of confusing.
In your case, you have the second, which means you have a constant pointer to a non-constant object. That is, you can change the value of the integer via the pointer, but you can't change what the pointer points to. So the code you posted would not be legal.
You can't learn to drive a car just by reading books.
Get yourself a C++ compiler if you want to learn C++. g++ is free of charge, as well as Visual Studio 2008 Express Edition.
As for your question, a const pointer is a zone of memory that is ready only. Example: A class may provide read-only access to an internal buffer.
Note that you also have the const pointer that is also const, aka
const char * const p
In that case, even the value of the pointer cannot be modified.
G'day,
To remember this easily you can use the trick that Scott Meyers describes in his excellent book "Effective C++" (sanitised Amazon link)
You draw a line through the declaration where the asterisk is located.
If the keyword const appears to the left of the line, then you can't change the value of the item that you're pointing to.
If the keyword const appears to the right of the line, then you can't change the pointer to point to another location.
If const appears on both sides, then you can't change the pointer and you can't change the value of what you're pointing to.
BTW That book is excellent, and while not for a beginner, is definitely a way of taking your C++ knowledge to the next level! Highly recommended.
HTH
cheers,
A const pointer means that you can change the value of the variable which is being pointed to, but you can't change where the pointer is pointed. I don't use them often myself, but a common application for const pointers is in defining specific memory segments that you need to address. See this question for more information.
As an aside, you should try to get a compiler on your computer if you can. I've shown myself many times that human brains are poor C++ compilers.
Your code is not legal. You can't assign to aPointer (except using copy-style initialisation, which in fact is not assignment even though it looks like it) if aPointer is declared const like that.
But usually when people say "a const pointer", they mean const int * aPointer, not int * const aPointer as you have in your code. The whole internet will explain the difference at the drop of a hat. As far as I know, the term "const pointer" isn't defined in the standard, so we're free to do this even though it's potentially confusing. "Pointer-to-const" would be an unambiguous description, and a pointer-to-const is much more commonly used than a pointer-which-is-itself-const.
A pointer-which-is-itself-const is used to refer to something, where you won't want the pointer to refer to a different thing at any point in its life. For instance, this is a pointer-which-is-itself-const, because "this object" remains the same object through the execution of a member function. The C++ language has opted not to let you decide part way through that you want to assign some other value to this, to make it refer to some other object. In C++ references often serve that purpose too, since they cannot be "re-seated" to change the referand.
In your code, the pointer cannot move, but the data pointed to can change.
It's legal up to the first delete. A subsequent new would not work because it's an assignment to a constant.
It's relatively unusual to see this, more common is to see where the data pointed to is unchangeable but the pointer can move.
int bar;
int baz;
const int * foo = &bar;
*foo = 4; // illegal
foo = &baz; // legal
having both pointer and value being const is common with strings
const wchar_t * const myString = L"String that will never change.";
Since you're new to C++, for the answer to this question and many other questions you may have or don't know you have, check out the C++ FAQ
I don't have a compiler available to me, otherwise I would try this out
Everyone has a C++ Compiler available to them: http://www.online-compiler.com
There are many others, but this seems to work...
As it has already been pointed out the perhaps most common const pointer is
const char* p;
The variable p can change, but the data p points to is unmodifable.
However, moving the const keyword to the left of the asterisk does not alter the meaning of the declaration:
char const* p;
I prefer the later since it becomes much easier to remember where to place the const keywords when declaring const pointers to const pointers:
char const* const* p;
Again, the variable p can change and the data pointed to is unmodifiable. Furthermore, the data is declared as const pointers meaning that it points to data that cannot be modified.
The more common notation for this type is
const char* const* p;
Placing the const keyword immediately to the left of the asterisk it modifies (or ampersand for reference) makes it easy to create complex types involving the const keyword. For example, a pointer to const pointers:
char const** p;
and a const pointer to pointers:
char* const* p;
Remember to "read" pointer declarations from the right to the left, and don't declare more than one pointer in each statement to avoid a lot of confusion.
Related
Let's say we have a class called object.
int main(){
object a;
const object* b = &a;
(*b);
}
Question:
b is a pointer to const, but the object it is pointing to is actually not a constant object. My question is, when we dereference the pointer "b" why do we get a constant object instead of what it is actually pointing at which is normal non constant object.
Because that's how the built-in dereference operator * works in C++. If you dereference a pointer of type T *, you get an lvalue of type T. In your case T is const object.
Neither * operator, not the pointer itself cares (or knows) that the object the pointer is pointing to is actually non-constant. It just can't be any other way within the concept of static typing used by C++ language.
The whole purpose of const-qualification at the first (and deeper) levels of indirection is to provide you with an ability to create restrictive access paths to objects. I.e. by creating a pointer-to-const you are deliberately and willingly preventing yourself (or someone else) from modifying the pointee, even if the pointee is not constant.
The type of an expression is just based on the declared types of the variables in the expression, it can't depend on dynamic run-time data. For instance, you could write:
object a1;
const object a2;
const object *b;
if (rand() % 2 == 0) {
b = &a1;
} else {
b = &a2;
}
(*b);
The type of *b is determined at compile-time, it can't depend on what rand() returns when you run the program.
Since b is declared to point to const object, that's what the type of *b is.
Because the const keyword means 'you can not modify that object' rather than 'the object can not be modified'.
This is useful when you pass an object to some function to use the object's value only, but but not to modify it:
// We expect PrintMyString to read the string
// and use its contents but not to modify it
void PrintMyString(const char *string);
void myfunction(int number)
{
// build and print a string of stars with given length
if(number>0 && number<=16]
{
char string[17];
int i;
for(i=0, i<number; i++)
string[i] = '*';
string[number] = '\0';
PrintMyString(string);
}
}
The function PrintMyString will get an array of characters, but the array will be passed as 'read only' to the function. The array is certainly modifiable within the owning function, but the PrintMyString can only read what it gets and not alter the contents.
There are plenty of other answers here, but I'm driven to post this because I feel that many of them provide too much detail, wander off topic, or assume knowledge of C++ jargon that the OP almost certainly doesn't have. I think that's unhelpful to the OP and others at a similar phase of their careers so I'm going to try to cut through some of that.
The situation itself is actually very straightforward. The declaration:
const SomeClassOrType *p = ...
simply says that whatever p is pointing to cannot be modified through that pointer, and that is useful to ensure that code that gains access to that object via p doesn't modify it when it isn't supposed to. It is most often used in parameter declarations so that functions and methods know whether or not they can modify the object being passed in (*).
It says nothing at all about the const-ness of what is actually being pointed to. The pointer itself, being just a simple soul, does not carry that information around with it. It only knows that, so far as the pointer is concerned, the object pointed to can be read from but not written to.
Concrete example (stolen from CiaPan):
void PrintString (const char *string);
char string [] = "abcde";
const char *p = string;
PrintString (p);
void PrintString (const char *ptr_to_string)
{
ptr_to_string [0] = 0; // oops! but the compiler will catch this, even though the original string itself is writeable
}
You can just pass string to PrintString directly of course, same difference, because the parameter is also declared const.
Another way to look at this is that const is information provided by the programmer to help the compiler check the correctness / consistency of the code at compile time. It lets the compiler catch mistakes like the one above, and perhaps perform better optimisations. By the time you actually run your code, it is history.
(*) The modern idiom is probably to use a const reference, but I don't want to muddy the waters by throwing that into the main discussion.
const object* b = &a;
This means: b is a pointer to a const (read-only) object
Doesn't say anything about the const-ness of a. It just means b has no right to modify a.
Also known as low-level const.
Contrary to the above, top-level const is when the pointer itself is const.
object *const b = &a; // b is a const pointer to an object
b = &some_other_object; // error - can't assign b to another object
// since b is a const pointer
*b = some_value; // this is fine since object is non-const
Simply put,
For plain poiters, the result of unary operator * is a reference to pointed-to type.
So, if the type pointed-to is const-qualified, the result is a reference to a constant.
References to constants can bind to any objects, even to mutable objects of otherwise the same type. When you bind a constref to a value, you promise to not ad-hoc modify that value through this refence (you still can do it explicitly though, cast it to a non-const reference).
Likewise, const-pointers can point to objects still modifiable otherwise.
By writing
const object* b = &a;
you declare that b is a pointer (*) to a const of type object, to which you then assign the address of a. a is of type object (but not const); you are permitted to use the adress of the non-const object in place of an adress of an const object.
When you dereference * b however, the compiler can only go according to your declaration - thus *b is a const object (however you can still modify a as you like, so beware of thinking that the object b points to cannot change - it mere cannot be changed via b)
const object* b = &a;
b will treat what it points to as const, i.e. it cannot change a
object* const b = &a;
b itself is const, i.e. it cannot point to other object address, but it can change a
Because at run-time it might be a const object, meaning that any operations performed on the dereference must be compatible with const. Ignore the fact that in your example it has been pointed to a non-const object and cannot point to anything else, the language doesn't work like that.
Here’s a specific example of why it is the way it is. Let’s say you declare:
int a[] = {1, 2, 3};
constexpr size_t n_a = sizeof(a)/sizeof(a[0]);
extern int sum( const int* sequence, size_t n );
sum_a = sum( a, n_a );
Now you implement sum() in another module. It doesn’t have any idea how the original object you’re pointing to was declared. It would be possible to write a compiler that tagged pointers with that information, but no compilers in actual use today do, for a number of good reasons.¹
Inside sum(), which might be in a shared library that cannot be recompiled with whole-program optimization, all you see is a pointer to memory that cannot be altered. In fact, on some implementations, trying to write through a pointer to const might crash the program with a memory-protection error. And the ability to reinterpret a block of memory as some other type is important to C/C++. For example, memset() or memcpy() reinterprets it as an array of arbitrary bytes. So the implementation of sum() has no way to tell the provenance of its pointer argument. As far as it’s concerned, it’s just a pointer to const int[].
More importantly, the contract of the function says that it’s not going to modify the object through that pointer.² It could simply cast away the const qualifier explicitly, but that would be a logic error. If you’re declaring a pointer const, it’s like engaging the safety on your gun: you want the compiler to stop you from shooting yourself in the foot.
¹ Including extra instructions to extract the address from a pointer, extra memory to store them, compatibility with the standard calling convention for the architecture, breaking a lot of existing code that assumes things like long being able to hold a pointer, and the lack of any benefit.
² With the pedantic exception of mutable data members.
I have searched and searched for an answer to this but can't find anything I actually "get".
I am very very new to c++ and can't get my head around the use of double, triple pointers etc.. What is the point of them?
Can anyone enlighten me
Honestly, in well-written C++ you should very rarely see a T** outside of library code. In fact, the more stars you have, the closer you are to winning an award of a certain nature.
That's not to say that a pointer-to-pointer is never called for; you may need to construct a pointer to a pointer for the same reason that you ever need to construct a pointer to any other type of object.
In particular, I might expect to see such a thing inside a data structure or algorithm implementation, when you're shuffling around dynamically allocated nodes, perhaps?
Generally, though, outside of this context, if you need to pass around a reference to a pointer, you'd do just that (i.e. T*&) rather than doubling up on pointers, and even that ought to be fairly rare.
On Stack Overflow you're going to see people doing ghastly things with pointers to arrays of dynamically allocated pointers to data, trying to implement the least efficient "2D vector" they can think of. Please don't be inspired by them.
In summary, your intuition is not without merit.
An important reason why you should/must know about pointer-to-pointer-... is that you sometimes have to interface with other languages (like C for instance) through some API (for instance the Windows API).
Those APIs often have functions that have an output-parameter that returns a pointer. However those other languages often don't have references or compatible (with C++) references. That's a situation when pointer-to-pointer is needed.
It's less used in c++. However, in C, it can be very useful. Say that you have a function that will malloc some random amount of memory and fill the memory with some stuff. It would be a pain to have to call a function to get the size you need to allocate and then call another function that will fill the memory. Instead you can use a double pointer. The double pointer allows the function to set the pointer to the memory location. There are some other things it can be used for but that's the best thing I can think of.
int func(char** mem){
*mem = malloc(50);
return 50;
}
int main(){
char* mem = NULL;
int size = func(&mem);
free(mem);
}
I am very very new to c++ and can't get my head around the use of double, triple pointers etc.. What is the point of them?
The trick to understanding pointers in C is simply to go back to the basics, which you were probably never taught. They are:
Variables store values of a particular type.
Pointers are a kind of value.
If x is a variable of type T then &x is a value of type T*.
If x evaluates to a value of type T* then *x is a variable of type T. More specifically...
... if x evaluates to a value of type T* that is equal to &a for some variable a of type T, then *x is an alias for a.
Now everything follows:
int x = 123;
x is a variable of type int. Its value is 123.
int* y = &x;
y is a variable of type int*. x is a variable of type int. So &x is a value of type int*. Therefore we can store &x in y.
*y = 456;
y evaluates to the contents of variable y. That's a value of type int*. Applying * to a value of type int* gives a variable of type int. Therefore we can assign 456 to it. What is *y? It is an alias for x. Therefore we have just assigned 456 to x.
int** z = &y;
What is z? It's a variable of type int**. What is &y? Since y is a variable of type int*, &y must be a value of type int**. Therefore we can assign it to z.
**z = 789;
What is **z? Work from the inside out. z evaluates to an int**. Therefore *z is a variable of type int*. It is an alias for y. Therefore this is the same as *y, and we already know what that is; it's an alias for x.
No really, what's the point?
Here, I have a piece of paper. It says 1600 Pennsylvania Avenue Washington DC. Is that a house? No, it's a piece of paper with the address of a house written on it. But we can use that piece of paper to find the house.
Here, I have ten million pieces of paper, all numbered. Paper number 123456 says 1600 Pennsylvania Avenue. Is 123456 a house? No. Is it a piece of paper? No. But it is still enough information for me to find the house.
That's the point: often we need to refer to entities through multiple levels of indirection for convenience.
That said, double pointers are confusing and a sign that your algorithm is insufficiently abstract. Try to avoid them by using good design techniques.
A double-pointer, is simply a pointer to a pointer. A common usage is for arrays of character strings. Imagine the first function in just about every C/C++ program:
int main(int argc, char *argv[])
{
...
}
Which can also be written
int main(int argc, char **argv)
{
...
}
The variable argv is a pointer to an array of pointers to char. This is a standard way of passing around arrays of C "strings". Why do that? I've seen it used for multi-language support, blocks of error strings, etc.
Don't forget that a pointer is just a number - the index of the memory "slot" inside a computer. That's it, nothing more. So a double-pointer is index of a piece of memory that just happens to hold another index to somewhere else. A mathematical join-the-dots if you like.
This is how I explained pointers to my kids:
Imagine the computer memory is a series of boxes. Each box has a number written on it, starting at zero, going up by 1, to however many bytes of memory there is. Say you have a pointer to some place in memory. This pointer is just the box number. My pointer is, say 4. I look into box #4. Inside is another number, this time it's 6. So now we look into box #6, and get the final thing we wanted. My original pointer (that said "4") was a double-pointer, because the content of its box was the index of another box, rather than being a final result.
It seems in recent times pointers themselves have become a pariah of programming. Back in the not-too-distant past, it was completely normal to pass around pointers to pointers. But with the proliferation of Java, and increasing use of pass-by-reference in C++, the fundamental understanding of pointers declined - particularly around when Java became established as a first-year computer science beginners language, over say Pascal and C.
I think a lot of the venom about pointers is because people just don't ever understand them properly. Things people don't understand get derided. So they became "too hard" and "too dangerous". I guess with even supposedly learned people advocating Smart Pointers, etc. these ideas are to be expected. But in reality there a very powerful programming tool. Honestly, pointers are the magic of programming, and after-all, they're just a number.
In many situations, a Foo*& is a replacement for a Foo**. In both cases, you have a pointer whose address can be modified.
Suppose you have an abstract non-value type and you need to return it, but the return value is taken up by the error code:
error_code get_foo( Foo** ppfoo )
or
error_code get_foo( Foo*& pfoo_out )
Now a function argument being mutable is rarely useful, so the ability to change where the outermost pointer ppFoo points at is rarely useful. However, a pointer is nullable -- so if get_foo's argument is optional, a pointer acts like an optional reference.
In this case, the return value is a raw pointer. If it returns an owned resource, it should usually be instead a std::unique_ptr<Foo>* -- a smart pointer at that level of indirection.
If instead, it is returning a pointer to something it does not share ownership of, then a raw pointer makes more sense.
There are other uses for Foo** besides these "crude out parameters". If you have a polymorphic non-value type, non-owning handles are Foo*, and the same reason why you'd want to have an int* you would want to have a Foo**.
Which then leads you to ask "why do you want an int*?" In modern C++ int* is a non-owning nullable mutable reference to an int. It behaves better when stored in a struct than a reference does (references in structs generate confusing semantics around assignment and copy, especially if mixed with non-references).
You could sometimes replace int* with std::reference_wrapper<int>, well std::optional<std::reference_wrapper<int>>, but note that is going to be 2x as large as a simple int*.
So there are legitimate reasons to use int*. Once you have that, you can legitimately use Foo** when you want a pointer to a non-value type. You can even get to int** by having a contiguous array of int*s you want to operate on.
Legitimately getting to three-star programmer gets harder. Now you need a legitimate reason to (say) want to pass a Foo** by indirection. Usually long before you reach that point, you should have considered abstracting and/or simplifying your code structure.
All of this ignores the most common reason; interacting with C APIs. C doesn't have unique_ptr, it doesn't have span. It tends to use primitive types instead of structs because structs require awkward function based access (no operator overloading).
So when C++ interacts with C, you sometimes get 0-3 more *s than the equivalent C++ code would.
The use is to have a pointer to a pointer, e.g., if you want to pass a pointer to a method by reference.
What real use does a double pointer have?
Here is practical example. Say you have a function and you want to send an array of string params to it (maybe you have a DLL you want to pass params to). This can look like this:
#include <iostream>
void printParams(const char **params, int size)
{
for (int i = 0; i < size; ++i)
{
std::cout << params[i] << std::endl;
}
}
int main()
{
const char *params[] = { "param1", "param2", "param3" };
printParams(params, 3);
return 0;
}
You will be sending an array of const char pointers, each pointer pointing to the start of a null terminated C string. The compiler will decay your array into pointer at function argument, hence what you get is const char ** a pointer to first pointer of array of const char pointers. Since the array size is lost at this point, you will want to pass it as second argument.
One case where I've used it is a function manipulating a linked list, in C.
There is
struct node { struct node *next; ... };
for the list nodes, and
struct node *first;
to point to the first element. All the manipulation functions take a struct node **, because I can guarantee that this pointer is non-NULL even if the list is empty, and I don't need any special cases for insertion and deletion:
void link(struct node *new_node, struct node **list)
{
new_node->next = *list;
*list = new_node;
}
void unlink(struct node **prev_ptr)
{
*prev_ptr = (*prev_ptr)->next;
}
To insert at the beginning of the list, just pass a pointer to the first pointer, and it will do the right thing even if the value of first is NULL.
struct node *new_node = (struct node *)malloc(sizeof *new_node);
link(new_node, &first);
Multiple indirection is largely a holdover from C (which has neither reference nor container types). You shouldn't see multiple indirection that much in well-written C++, unless you're dealing with a legacy C library or something like that.
Having said that, multiple indirection falls out of some fairly common use cases.
In both C and C++, array expressions will "decay" from type "N-element array of T" to "pointer to T" under most circumstances1. So, assume an array definition like
T *a[N]; // for any type T
When you pass a to a function, like so:
foo( a );
the expression a will be converted from "N-element array of T *" to "pointer to T *", or T **, so what the function actually receives is
void foo( T **a ) { ... }
A second place they pop up is when you want a function to modify a parameter of pointer type, something like
void foo( T **ptr )
{
*ptr = new_value();
}
void bar( void )
{
T *val;
foo( &val );
}
Since C++ introduced references, you probably won't see that as often. You'll usually only see that when working with a C-based API.
You can also use multiple indirection to set up "jagged" arrays, but you can achieve the same thing with C++ containers for much less pain. But if you're feeling masochistic:
T **arr;
try
{
arr = new T *[rows];
for ( size_t i = 0; i < rows; i++ )
arr[i] = new T [size_for_row(i)];
}
catch ( std::bad_alloc& e )
{
...
}
But most of the time in C++, the only time you should see multiple indirection is when an array of pointers "decays" to a pointer expression itself.
The exceptions to this rule occur when the expression is the operand of the sizeof or unary & operator, or is a string literal used to initialize another array in a declaration.
In C++, if you want to pass a pointer as an out or in/out parameter, you pass it by reference:
int x;
void f(int *&p) { p = &x; }
But, a reference can't ("legally") be nullptr, so, if the pointer is optional, you need a pointer to a pointer:
void g(int **p) { if (p) *p = &x; }
Sure, since C++17 you have std::optional, but, the "double pointer" has been idiomatic C/C++ code for many decades, so should be OK. Also, the usage is not so nice, you either:
void h(std::optional<int*> &p) { if (p) *p = &x) }
which is kind of ugly at the call site, unless you already have a std::optional, or:
void u(std::optional<std::reference_wrapper<int*>> p) { if (p) p->get() = &x; }
which is not so nice in itself.
Also, some might argue that g is nicer to read at the call site:
f(p);
g(&p); // `&` indicates that `p` might change, to some folks
I understand the concept of final in Java or const in C++ for forcing constant values and allowing the compiler to enforce that on anyone using the modules you write.
I am not able to see where would you want to have a const pointer, why would you not want the pointer to change regardless of the data being constant or not:
e.g
why this?
char greetings[] = "Hello";
char * const p = greetings; // const pointer, non-const data
const char * const p = greetings; // const pointer and const data
I cannot visualize an exmaple where you want to keep the pointer const, could it be for a file handle or something similar? or just a pointer to an object you don't want to change?
You may want to make a pointer itself constant for several reasons:
Helping others understand your code - When you declare a pointer and make it constant, you tell the readers that there are no changes done to that pointer in the rest of your function, so they would have a better idea of how you use the pointer.
Helping others maintain your code - Someone else who maintains your code after you will have better idea of what you expected to do with the pointer. If he tries to pass a non-constant reference or a pointer to the pointer that you declared const, the compiler is going to catch this error.
Enable compiler optimizations - When compilers see something declared const, they can optimize more aggressively.
I tried to search answer for this but I found it very hard to find 'exact' example of this kind. I understand very little about pointers to pointers and I feel there is something more laying under the skin of this than just pointer to something.
So how would you guys translate this?
void free(shame_1 * const * const group_1);
Am I right if I translate it as const group_1 pointer to const pointer to shame_1?
Thanks.
shame_1 * const * const group_1;
declares a variable named group_1, whose type is a const pointer (you cannot change where it points at) to another const pointer (same) to a shame_1-type object, whose value you can actually change.
So, for instance, you cannot compile:
group_1 = nullptr;
*group_1 = nullptr;
You can, however, do:
void f(shame_1& group) {
//stuff that modifies group
...
}
f(**group1);
As zakinster commented, having a look at the spiral rule should help you understand this kind of notation.
Generally, just read from right to left:
void free(shame_1 * const * const group_1);
free takes a parameter called group_1 that's a const pointer to a const pointer to a shame_1 object.
It's not relevant here, but the main hassle reading right-to-left is that some people use T const * and others use const T* and they mean the same thing, so whether you see either T const or const T you should read it as const T. In your code, only the pointers are const anyway.
For example:
shame_1 a_shame;
const shame_1* p_a_shame = &a_shame;
free(&p_a_shame);
There's very little utility in this... free() could have accepted a const pointer to a shame_1 without an extra level of indirection, and would still have been able to do all the same things to the shame_1, so it's only useful if free() has to pass the const-pointer-to-const-pointer to some other function (with ultimately there being no good reason for the whole mess).
You can visualise the memory usage / relationships as:
[a_shame] <------ [p_a_shame] <------ [group_1]*
this is likely only in a CPU register, though the others may be too after optimisation.
Am I right if I translate it as const group_1 pointer to const pointer to shame_1?
So, to me the "const group_1 pointer" bit is a bit suspect... I think it reads better as "group_1 is a const pointer to..." or "const pointer (called group_1) to...".
Embarrassing though it may be I know I am not the only one with this problem.
I have been using C/C++ on and off for many years. I never had a problem grasping the concepts of addresses, pointers, pointers to pointers, and references.
I do constantly find myself tripping over expressing them in C syntax, however. Not the basics like declarations or dereferencing, but more often things like getting the address of a pointer-to-pointer, or pointer to reference, etc. Essentially anything that goes a level or two of indirection beyond the norm. Typically I fumble with various semi-logical combinations of operators until I trip upon the correct one.
Clearly somewhere along the line I missed a rule or two that simplifies and makes it all fall into place. So I guess my question is: do you know of a site or reference that covers this matter with clarity and in some depth?
I don't know of any website but I'll try to explain it in very simple terms. There are only three things you need to understand:
variable will contain the contents of the variable. This means that if the variable is a pointer it will contain the memory address it points to.
*variable (only valid for pointers) will contain the contents of the variable pointed to. If the variable it points to is another pointer, ptr2, then *variable and ptr2 will be the same thing; **variable and *ptr2 are the same thing as well.
&variable will contain the memory address of the variable. If it's a pointer, it will be the memory address of the pointer itself and NOT the variable pointed to or the memory address of the variable pointed to.
Now, let's see a complex example:
void **list = (void **)*(void **)info.List;
list is a pointer to a pointer. Now let's examine the right part of the assignment starting from the end: (void **)info.List. This is also a pointer to a pointer.
Then, you see the *: *(void **)info.List. This means that this is the value the pointer info.List points to.
Now, the whole thing: (void **)*(void **)info.List. This is the value the pointer info.List points to casted to (void **).
I found the right-left-right rule to be useful. It tells you how to read a declaration so that you get all the pointers and references in order. For example:
int *foo();
Using the right-left-right rule, you can translate this to English as "foo is a function that returns a pointer to an integer".
int *(*foo)(); // "foo is a pointer to a function returning a pointer to an int"
int (*foo[])(); // "foo is an array of pointers to functions returning ints"
Most explanations of the right-left-right rule are written for C rather than C++, so they tend to leave out references. They work just like pointers in this context.
int &foo; // "foo is a reference to an integer"
Typedefs can be your friend when things get confusing. Here's an example:
typedef const char * literal_string_pointer;
typedef literal_string_pointer * pointer_to_literal_string_pointer;
void GetPointerToString(pointer_to_literal_string_pointer out_param)
{
*out_param = "hi there";
}
All you need to know is that getting the address of an object returns a pointer to that object, and dereferencing an object takes a pointer and turns it into to object that it's pointing to.
T x;
A a = &x; // A is T*
B b = *a; // B is T
C c = &a; // C is T**
D d = *c; // D is T*
Essentially, the & operator takes a T and gives you a T* and the * operator takes a T* and gives you a T, and that applies to higher levels of abstraction equally e.g.
using & on a T* will give you a T**.
Another way of thinking about it is that the & operator adds a * to the type and the * takes one away, which leads to things like &&*&**i == i.
I'm not sure exactly what you're looking for, but I find it helpful to remember the operator precedence and associativity rules. That said, if you're ever confused, you might as well throw in some more parens to disambiguate, even if it's just for your benefit and not the compiler's.
edit: I think I might understand your question a little better now. I like to think of a chain of pointers like a stack with the value at the bottom. The dereferencing operator (*) pops you down the stack, where you find the value itself at the end. The reference operator (&) lets you push another level of indirection onto the stack. Note that it's always legal to move another step away, but attempting to dereference the value is analogous to popping an empty stack.