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
Related
#include <iostream>
using namespace std;
int func(int ar[5]){
//the code is written here with returning whatever the requirement is
}
int main(){
int ar[5];
func(ar);
return 0;
}
In this kind of situation where we are passing array through a function why the address of the array is used in actual parameter whereas the array in formal parameter?
This is because C handled arrays weirdly.
In C, arrays convert to a pointer to their first element at the drop of a hat, cannot be passed as arguments to a function, cannot be returned from functions, and cannot be copied by assignment.
C++, originally based off C, carries these screwed up design decisions as a legacy issue. Fixing them would break insane amounts of code.
If you want an array that behaves more reasonable, use std::array.
void foo(int[5]);
this is actually taking an int* argument.
int arr[7];
foo(arr);
this converts the 7 element arr to a pointer and passes it to foo. Which seems to take int[5] but that 5 does nothing.
Yes this is utterly crazy.
Now,
void bar(int(&)[5])
because C does not have references, the above is an actual reference to an array if 5 elements, so
bar(arr)
won't compile.
There is nothing weird, nor screwed up about how arrays are passed in C. C is expressed pretty simply, really. C /does/ have references; they are explicit, so, easy to see when they're in use. We call them pointers.
One simply needs to understand that there is not dedicated storage for array types, that arrays are not first-class types, so, won't behave like first-class types. Can't be assigned into (directly), nor passed into and out of functions directly.
In C/C++ we use pointers to refer to a chunk of memory where the contents of the array are stored.
So, all that's left is that we understand that when we declare an array, the name of the array is really a pointer to the beginning of the array.
int a[12] ;
/* a's type is this: */
int *a ;
So, when we "pass an array" to a function, we are copying the pointer into the function. The semantics are straightforward, and entirely consistent (unlike Java).
The array, itself, is never passed directly.
Finally, realise that there is no difference between a pointer to a single integer, and a pointer to a contiguous chunk of integers in memory.
Remember, a[i] is equivalent to *(a+i). So, in this example:
int i = 12 ;
int *p = &i ;
, *p is the same as *(p+0); the p can be thought of as an array of length 1.
With a slight change in perspective you should be able to see the simplicity, the elegance, that is C.
From Programming Language Pragmatics, by Scott
For systems programming, or to facilitate the writing of
general-purpose con- tainer (collection) objects (lists, stacks,
queues, sets, etc.) that hold references to other objects, several
languages provide a universal reference type. In C and C++, this
type is called void *. In Clu it is called any; in Modula-2,
address; in Modula-3, refany; in Java, Object; in C#, object.
In C and C++, how does void * work as a universal reference type?
void * is always only a pointer type, while a universal reference type contains all values, both pointers and nonpointers. So I can't see how void * is a universal reference type.
Thanks.
A void* pointer will generally hold any pointer that is not a C++ pointer-to-member. It's rather inconvenient in practice, since you need to cast it to another pointer type before you can use it. You also need to convert it to the same pointer type that it was converted from to make the void*, otherwise you risk undefined behavior.
A good example would be the qsort function. It takes a void* pointer as a parameter, meaning it can point to an array of anything. The comparison function you pass to qsort must know how to cast two void* pointers back to the types of the array elements in order to compare them.
The crux of your confusion is that neither an instance of void * nor an instance of Modula-3's refany, nor an instance of any other language's "can refer to anything" type, contains the object that it refers to. A variable of type void * is always a pointer and a variable of type refany is always a reference. But the object that they refer to can be of any type.
A purist of programming-language theory would tell you that C does not have references at all, because pointers are not references. It has a nearly-universal pointer type, void *, which can point to an object of any type (including integers, aggregates, and other pointers). As a common but not ubiquitous extension, it can also point to any function (functions are not objects).
The purist would also tell you that C++ does not have a (nearly-)universal pointer type, because of its stricter type system, and doesn't have a universal reference type either.
They would also say that the book you are reading is being sloppy with its terminology, and they would caution you to not take any one such book for the gospel truth on terminological matters, or any other matters. You should instead read widely in both books and CS journals and conference proceedings (collectively known as "the literature") until you gain an "ear" for what is generally-agreed-on terminology, what is specific to a subdiscipline or a community of practice, and so on.
And finally they would remind you that C and C++ are two different languages, and anyone who speaks of them in the same breath is either glossing over the distinctions (which may or may not be relevant in context), decades out of date, or both.
Probably the reason is that you can take address of any variable of any type and cast it to void*.
It does by a silent contract that you know the actual type of object.
So you can store different kinds of elements in a container, but you need to somehow know what is what when taking elements back, to interpret them correctly.
The only convenience void* offers is that it's idiomatic for this, i.e. it's clear that dereferencing the pointer makes no sense, and void* is implicitly convertible to any pointer type. That is for c/
In c++ this is called type erasure techniques preferred. Or special types, like any (there is a boost version of this too.)
void* is no more just a pointer. Thus, it holds an address of an object (or an array and stuffs like that)
When your program is running, every variable should have it owns address in memory, right? And pointer is somethings point to that address.
In normal, each type of pointer should be the same type of object int b = 5; int* p = &b; for example. But that is the case you know what the type is, it means the specific type.
But sometimes, you just want to know that it stores somethings somewhere in memory and you know what "type" of that address, you can cast easily. For example, in OpenCV library which I am learning, there are a lot of functions where user can pass the arguments to instead of declaring global variables and most use in callback functions, like this:
void onChange(int v, void *ptr)
Here, the library does not care about what ptr point to, it just know that when you call the function, if you pass an address to like this onChange(5,&b) then you must cast ptr to the same type before dealing with it int b = static_cast<int*>(ptr);
Probably this explanation from Understanding pointers from Richard Reese will help
A pointer to void is a general-purpose pointer used to hold references to any data type.
It has two interesting properties:
A pointer to void will have the same representation and memory alignment as a pointer to char
A pointer to void will never be equal to another pointer. However, two void pointers assigned a NULL value will be equal.
Any pointer can be assigned to a pointer to void. It can then be cast back to its original pointer type. When this happens the value will be equal to the original pointer value.
This is illustrated in the following sequence, where a pointer to
int is assigned to a pointer to void and then back to a pointer to int
#include<stdio.h>
void main()
{
int num = 100;
int *pi = #
printf("value of pi is %p\n", pi);
void* pv = pi;
pi = (int*)pv;
printf("value of pi is %p\n", pi);
}
Pointers to void are used for data pointers, not function pointers
I was studying pointers references and came across different ways to feed in parameters. Can someone explain what each one actually means?
I think the first one is simple, it's that x is a copy of the parameter fed in so another variable is created on the stack.
As for the others I'm clueless.
void doSomething1(int x){
//code
}
void doSomething2(int *x){
//code
}
void doSomething3(int &x){
//code
}
void doSomething3(int const &x){
//code
}
I also see stuff like this when variables are declared. I don't understand the differences between them. I know that the first one will put 100 into the variable y on the stack. It won't create a new address or anything.
//example 1
int y = 100;
//example 2
int *y = 100;
//Example 3: epic confusion!
int *y = &z;
Question 1: How do I use these methods? When is it most appropriate?
Question 2: When do I declare variables in that way?
Examples would be great.
P.S. this is one the main reasons I didn't learn C++ as Java just has garbage collection. But now I have to get into C++.
//example 1
int y = 100;
//example 2
int *y = 100;
//Example 3: epic confusion!
int *y = &z;
I think the problem for most students is that in C++ both & and * have different meanings, depending on the context in which they are used.
If either of them appears after a type within an object declaration (T* or T&), they are type modifiers and change the type from plain T to a reference to a T (T&) or a pointer to a T (T*).
If they appear in front of an object (&obj or *obj), they are unary prefix operators invoked on the object. The prefix & returns the address of the object it is invoked for, * dereferences a pointer, iterator etc., yielding the value it references.
It doesn't help against confusion that the type modifiers apply to the object being declared, not the type. That is, T* a, b; defines a T* named a and a plain T named b, which is why many people prefer to write T *a, b; instead (note the placement of the type-modifying * adjacent the object being defined, instead of the type modified).
Also unhelpful is that the term "reference" is overloaded. For one thing it means a syntactic construct, as in T&. But there's also the broader meaning of a "reference" being something that refers to something else. In this sense, both a pointer T* and a reference (other meaning T&) are references, in that they reference some object. That comes into play when someone says that "a pointer references some object" or that a pointer is "dereferenced".
So in your specific cases, #1 defines a plain int, #2 defines a pointer to an int and initializes it with the address 100 (whatever lives there is probably best left untouched ), and #3 defines another pointer and initializes it with the address of an object z (necessarily an int, too).
A for how to pass objects to functions in C++, here is an old answer from me to that.
From Scott Myers - More Effective C++ -> 1
First, recognize that there is no such thing as a null reference. A reference must always refer to some object.Because a reference must refer to an object, C++ requires that references be initialized.
Pointers are subject to no such restriction. The fact that there is no such thing as a null reference implies that it can be more efficient to use references than to use pointers. That's because there's no need to test the validity of a reference before using it.
Another important difference between pointers and references is that pointers may be reassigned to refer to different objects. A reference, however, always refers to the object with which it is initialized
In general, you should use a pointer whenever you need to take into account the possibility that there's nothing to refer to (in which case you can set the pointer to null) or whenever you need to be able to refer to different things at different times (in which case you can change where the pointer points). You should use a reference whenever you know there will always be an object to refer to and you also know that once you're referring to that object, you'll never want to refer to anything else.
References, then, are the feature of choice when you know you have something to refer to, when you'll never want to refer to anything else, and when implementing operators whose syntactic requirements make the use of pointers undesirable. In all other cases, stick with pointers.
Read S.Lippmann's C++ Premier or any other good C++ book.
As for passing the parameters, generally when copying is cheap we pass by value. For mandatory out parameters we use references, for optional out parameters - pointers, for input parameters where copying is costly, we pass by const references
Thats really complicated topic. Please read here: http://www.goingware.com/tips/parameters/.
Also Scott Meiers "Effective C++" is a top book on such things.
void doSomething1(int x){
//code
}
This one pass the variable by value, whatever happens inside the function, the original variable doesn't change
void doSomething2(int *x){
//code
}
Here you pass a variable of type pointer to integer. So when accessing the number you should use *x for the value or x for the address
void doSomething3(int &x){
//code
}
Here is like the first one, but whatever happens inside the function, the original variable will be changed as well
int y = 100;
normal integer
//example 2
int *y = 100;
pointer to address 100
//Example 3: epic confusion!
int *y = &z;
pointer to the address of z
void doSomething1(int x){
//code
}
void doSomething2(int *x){
//code
}
void doSomething3(int &x){
//code
}
And i am really getting confused between them?
The first is using pass-by-value and the argument to the function will retain its original value after the call.
The later two are using pass-by-reference. Essentially they are two ways of achieving the same thing. The argument is not guarenteed to retain its original value after the call.
Most programmers prefer to pass large objects by const reference to improve the performance of their code and provide a constraint that the value will not change. This ensures the copy constructor is not called.
Your confusion might be due to the '&' operator having two meanings. The one you seem to be familiar with is the 'reference operator'. It is also used as the 'address operator'. In the example you give you are taking the address of z.
A good book to check out that covers all of this in detail is 'Accelerated C++' by Andrew Koening.
The best time to use those methods is when it's more efficient to pass around references as opposed to entire objects. Sometimes, some data structure operations are also faster using references (inserting into a linked list for example). The best way to understand pointers is to read about them and then write programs to use them (and compare them to their pass-by-value counterparts).
And for the record, knowledge of pointers makes you considerably more valuable in the workplace. (all too often, C++ programmers are the "mystics" of the office, with knowledge of how those magical boxes under the desks process code /semi-sarcasm)
Is this correct?
int (*(*ptr)())[];
I know this is trivial, but I was looking at an old test about these kind of constructs, and this particular combination wasn't on the test and it's really driving me crazy; I just have to make sure. Is there a clear and solid understandable rule to these kind of declarations?
(ie: pointer to... array of.. pointers to... functions that.... etc etc)
Thanks!
R
The right-left rule makes it easy.
int (*(*ptr)())[];can be interpreted as
Start from the variable name ------------------------------- ptr
Nothing to right but ) so go left to find * -------------- is a pointer
Jump out of parentheses and encounter () ----------- to a function that takes no arguments(in case of C unspecified number of arguments)
Go left, find * ------------------------------------------------ and returns a pointer
Jump put of parentheses, go right and hit [] ---------- to an array of
Go left again, find int ------------------------------------- ints.
In almost all situations where you want to return a pointer to an array the simplest thing to do is to return a pointer to the first element of the array. This pointer can be used in the same contexts as an array name an provides no more or less indirection than returning a pointer of type "pointer to array", indeed it will hold the same pointer value.
If you follow this you want a pointer to a function returning a pointer to an int. You can build this up (construction of declarations is easier than parsing).
Pointer to int:
int *A;
Function returning pointer to int:
int *fn();
pointer to function returning a pointer to int:
int *(*pfn)();
If you really want to return a pointer to a function returning a pointer to an array of int you can follow the same process.
Array of int:
int A[];
Pointer to array of int:
int (*p)[];
Function returning pointer ... :
int (*fn())[];
Pointer to fn ... :
int (*(*pfn)())[];
which is what you have.
You don't. Just split it up into two typedefs: one for pointer to int array, and one for pointer to functions. Something like:
typedef int (*IntArrayPtr_t)[];
typedef IntArrayPtr_t (*GetIntArrayFuncPtr_t)(void);
This is not only more readable, it also makes it easier to declare/define the functions that you are going to assign the variable:
IntArrayPtr_t GetColumnSums(void)
{ .... }
Of course this assumes this was a real-world situation, and not an interview question or homework. I would still argue this is a better solution for those cases, but that's only me. :)
If you feel like cheating:
typedef int(*PtrToArray)[5];
PtrToArray function();
int i = function;
Compiling that on gcc yields: invalid conversion from 'int (*(*)())[5]' to 'int'. The first bit is the type you're looking for.
Of course, once you have your PtrToArray typedef, the whole exercise becomes rather more trivial, but sometimes this comes in handy if you already have the function name and you just need to stick it somewhere. And, for whatever reason, you can't rely on template trickery to hide the gory details from you.
If your compiler supports it, you can also do this:
typedef int(*PtrToArray)[5];
PtrToArray function();
template<typename T> void print(T) {
cout << __PRETTY_FUNCTION__ << endl;
}
print(function);
Which, on my computer box, produces void function(T) [with T = int (* (*)())[5]]
Being able to read the types is pretty useful, since understanding compiler errors is often dependent on your ability to figure out what all those parenthesis mean. But making them yourself is less useful, IMO.
Here's my solution...
int** (*func)();
Functor returning an array of int*'s. It isn't as complicated as your solution.
Using cdecl you get the following
cdecl> declare a as pointer to function returning pointer to array of int;
Warning: Unsupported in C -- 'Pointer to array of unspecified dimension'
(maybe you mean "pointer to object")
int (*(*a)())[]
This question from C-faq is similar but provides 3 approaches to solve the problem.
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.