- int *p[2]
- int (*p)[2]
- int (*p)()
Example 1
int *p[2];This is actually an array of two int pointers. We can also think of this as a pointer to two consecutive int pointers.
int *p[2]; int *a = new int; *a = 21; int *b = new int; *b = 22; p[0] = a; p[1] = b; std::cout << *p[0] << std::endl; // 21 std::cout << *p[1] << std::endl; // 22 int **q = p; std::cout << **q << std::endl; std::cout << **(q + 1) << std::endl; delete a; delete b;http://codepad.org/6c07RgKf
Example 2
int (*p)[2];This is a pointer to an array of two ints. Just as in the first example, we can think of this a pointer to a pointer, since an array is basically a const pointer.
int n[2]; n[0] = 4; n[1] = 7; int (*p)[2] = &n; std::cout << (*p)[0] << std::endl; // 4 std::cout << (*p)[1] << std::endl; // 7 // or ... std::cout << **p << std::endl; // 4 std::cout << *(*p + 1) << std::endl; // 7http://codepad.org/UDcH5cIQ
Example 3
int (*p)();This is a pointer to a function returning an int.
int takeThisInt() { return 123; } // ... int (*p)() = &takeThisInt; int x = p(); // note the parentheses std::cout << x << std::endl; // 123http://codepad.org/iL701mg5
Pointer-to-member-function syntax
This pointer-to-member-function syntax in C++ is not exactly making things more intuitive. Here is an example:void (SomeClass::*x)(int) const = &SomeClass::func2;Hmm… Let's take a look at the context in which this statement appears.
class SomeClass { public: void func1(int x) const { std::cout << "func 1: " << x; } void func2(int x) const { std::cout << "func 2: " << x; } void func3(int x) const { std::cout << "func 3: " << x; } }; void callSomeFunction(void (SomeClass::*m)(int) const) { SomeClass obj; (obj.*m)(1); } int main() { void (SomeClass::*x)(int) const; x = &SomeClass::func2; callSomeFunction(x); // func 2: 1 return 0; }http://codepad.org/eUtSGfbV
In main(), void (SomeClass::*x)(int) const introduces a pointer (named x) to a const member function of SomeClass that returns void and takes an int as parameter. Then, &SomeClass::func2 takes the address of func2, which happens to fit this description. Finally, in callSomeFunction(), the supplied function pointee is called with the argument 1 on the stack allocated object.
A typedef can help to make this a bit more readable:
typedef void (SomeClass::*SomeClassMember)(int) const;The program now reads:
class SomeClass { public: void func1(int x) const { std::cout << "func 1: " << x; } void func2(int x) const { std::cout << "func 2: " << x; } void func3(int x) const { std::cout << "func 3: " << x; } }; typedef void (SomeClass::*SomeClassMember)(int) const; void callSomeFunction(SomeClassMember m) { SomeClass obj; (obj.*m)(1); } int main() { SomeClassMember x = &SomeClass::func2; callSomeFunction(x); return 0; }Ok, this post was supposed to be about the reference-to-pointer syntax.
Reference vs. Pointer
Pointers are memory addresses with type information. The type specifies the kind of data stored in the pointed-to address. References are typically implemented as pointers by the compiler, but on the syntactic level there are several differences:- A reference can't, once created, be changed to reference another object.
int n = 1; int *p = &n; // p points to n int k = 2; p = &k; // p now points to k instead int &r = n; // r will always reference n
- References cannot be null.
int *p = new int; *p = 123; int &x = *p; // x is now a local reference to p x = 5; std::cout << *p << std::endl; // 5 delete p; p = 0; // pointer is now NULL if (p) { // we can't do this with a reference *p = 1; } x = 3; // undefined behavior
- References must be initialized when created, and cannot be uninitialized.
int &ref; // error: ‘ref’ declared as reference but not initialized
Why reference-to-pointer?
Let's look at some different function declarations to see how the reference-to-pointer syntax can be useful.- void takeInt(int x);
- void takeIntRef(int &r);
- void takePtr(int *p);
- void takePtrToPtr(int **p);
- void takeRefToPtr(int *&rp);
void takeInt(int x); void takeIntRef(int &r); void takePtr(int *p);The first three cases are straighforward:
- The int is passed by value, which means that a local copy is made.
- The int is passed by reference, the local name
acts as an alias for the original variable. - A pointer is passed, similar to passing a reference but less safe.
Note that a pointer is actually passed by value, i.e., only a copy of the pointer is passed to the function. The address of the int's memory location is copied to the local pointer p.
void takePtrToPtr(int **p);This is a bit more interesting. A pointer to a pointer is passed. Suppose that a points to b, and b points to an int.
a → b → int
Passing (a) to the function, we can now change not only the original int pointee, but also the pointer (b) itself. Here is an example:
void takePtrToPtr(int **p) { int *x = new int; *x = 123; *p = x; } int main() { int n = 1; int *p = &n; assert(p == &n); takePtrToPtrcout << *p; // 123 delete p; // no leaks ^_^ return 0; }Finally,
void takeRefToPtr(int *&rp);is similar to the pointer-to-pointer approach, but with a slightly more friendly syntax. We are now passing a reference to the pointer to takeRefToPtr().
void takeRefToPtr(int *&rp) { int *x = new int; *x = 123; rp = x; } int main() { int n = 55; int *p = &n; takeRefToPtr(p); assert(p != &n); std::cout << *p << std::endl; // 123; delete p; return 0; }Note how the function changes the the original pointer p.
We could think of the relationship
- int *x ↔ int *&x
as similar to that between
- int x ↔ int &x.
Finally, here is a program that demonstrates the use of the reference-to-pointer syntax:
#include <iostream> #include <assert.h> static int five = 5; static int nine = 9; void takeInt(int x) { x = 0; } void takeIntRef(int &r) { r = 0; } void takePtr(int *p) { *p = 1; p = &five; } void takePtrToPtr(int **p) { **p = 789; *p = &five; } void takeRefToPtr(int *&rp) { *rp = 1; rp = &nine; } int main() { int n = 123; takeInt(n); // local copy is modified std::cout << n << std::endl; // 123 (value unchanged) takeIntRef(n); // value passed by reference, original is modified std::cout << n << std::endl; // 0 (assigned in function) int *pt = &n; // pt points to n takePtr(pt); // pointer is passed by value assert(pt == &n); // pointer has not changed (p still points to n) ... std::cout << n << std::endl; // 1 ... but pointee has takePtrToPtr(&pt); // pass a pointer to pt (= pointer to a pointer to n) assert(pt != &n); // this time the pointer has changed, // pt now no longer points to n std::cout << *pt << std::endl; // 5 assert(pt == &five); // indeed, pt points to the static "five" std::cout << n << std::endl; // 789 (changed in first line of takePtrToPtr) takeRefToPtr(pt); // pass a pointer by reference std::cout << *pt << std::endl; // 9 (pt is now modified in function) return 0; }http://codepad.org/3OD0sVvw
1 comments:
Great Explain!
Post a Comment