What does having two asterisk ** in Objective-C mean?
It's a pointer to a pointer, just like in C (which, despite its strange square-bracket syntax, Objective-C is based on):
char c;char *pc = &c;char **ppc = &pc;char ***pppc = &ppc;
and so on, ad infinitum (or until you run out of variable space).
It's often used to pass a pointer to a function that must be able to change the pointer itself (such as re-allocating memory for a variable-sized object).
=====
Following your request for a sample that shows how to use it, here's some code I wrote for another post which illustrates it. It's an appendStr()
function which manages its own allocations (you still have to free the final version). Initially you set the string (char *
) to NULL and the function itself will allocate space as needed.
#include <stdio.h>#include <stdlib.h>#include <string.h>void appendToStr (int *sz, char **str, char *app) { char *newstr; int reqsz; /* If no string yet, create it with a bit of space. */ if (*str == NULL) { *sz = strlen (app) + 10; if ((*str = malloc (*sz)) == NULL) { *sz = 0; return; } strcpy (*str, app); return; }
/* If not enough room in string, expand it. We could use realloc but I've kept it as malloc/cpy/free to ensure the address changes (for the program output). */ reqsz = strlen (*str) + strlen (app) + 1; if (reqsz > *sz) { *sz = reqsz + 10; if ((newstr = malloc (*sz)) == NULL) { free (*str); *str = NULL; *sz = 0; return; } strcpy (newstr, *str); free (*str); *str = newstr; } /* Append the desired string to the (now) long-enough buffer. */ strcat (*str, app);}
static void dump(int sz, char *x) { if (x == NULL) printf ("%8p [%2d] %3d [%s]\n", x, sz, 0, ""); else printf ("%8p [%2d] %3d [%s]\n", x, sz, strlen (x), x);}static char *arr[] = {"Hello.", " My", " name", " is", " Pax", " and"," I", " am", " old."};int main (void) { int i; char *x = NULL; int sz = 0; printf (" Pointer Size Len Value\n"); printf (" ------- ---- --- -----\n"); dump (sz, x); for (i = 0; i < sizeof (arr) / sizeof (arr[0]); i++) { appendToStr (&sz, &x, arr[i]); dump (sz, x); }}
The code outputs the following. You can see how the pointer changes when the currently allocated memory runs out of space for the expanded string (at the comments):
Pointer Size Len Value ------- ---- --- -----# NULL pointer here since we've not yet put anything in. 0x0 [ 0] 0 []# The first time we put in something, we allocate space (+10 chars).0x6701b8 [16] 6 [Hello.]0x6701b8 [16] 9 [Hello. My]0x6701b8 [16] 14 [Hello. My name]# Adding " is" takes length to 17 so we need more space.0x6701d0 [28] 17 [Hello. My name is]0x6701d0 [28] 21 [Hello. My name is Pax]0x6701d0 [28] 25 [Hello. My name is Pax and]0x6701d0 [28] 27 [Hello. My name is Pax and I]# Ditto for adding " am".0x6701f0 [41] 30 [Hello. My name is Pax and I am]0x6701f0 [41] 35 [Hello. My name is Pax and I am old.]
In that case, you pass in **str
since you need to be able to change the *str
value.
=====
Or the following, which does an unrolled bubble sort (oh, the shame!) on strings that aren't in an array. It does this by directly exchanging the addresses of the strings.
#include <stdio.h>static void sort (char **s1, char **s2, char **s3, char **s4, char **s5) { char *t; if (strcmp (*s1, *s2) > 0) { t = *s1; *s1 = *s2; *s2 = t; } if (strcmp (*s2, *s3) > 0) { t = *s2; *s2 = *s3; *s3 = t; } if (strcmp (*s3, *s4) > 0) { t = *s3; *s3 = *s4; *s4 = t; } if (strcmp (*s4, *s5) > 0) { t = *s4; *s4 = *s5; *s5 = t; } if (strcmp (*s1, *s2) > 0) { t = *s1; *s1 = *s2; *s2 = t; } if (strcmp (*s2, *s3) > 0) { t = *s2; *s2 = *s3; *s3 = t; } if (strcmp (*s3, *s4) > 0) { t = *s3; *s3 = *s4; *s4 = t; } if (strcmp (*s1, *s2) > 0) { t = *s1; *s1 = *s2; *s2 = t; } if (strcmp (*s2, *s3) > 0) { t = *s2; *s2 = *s3; *s3 = t; } if (strcmp (*s1, *s2) > 0) { t = *s1; *s1 = *s2; *s2 = t; }}int main (int argCount, char *argVar[]) { char *a = "77"; char *b = "55"; char *c = "99"; char *d = "88"; char *e = "66"; printf ("Unsorted: [%s] [%s] [%s] [%s] [%s]\n", a, b, c, d, e); sort (&a,&b,&c,&d,&e); printf (" Sorted: [%s] [%s] [%s] [%s] [%s]\n", a, b, c, d, e); return 0;}
which produces:
Unsorted: [77] [55] [99] [88] [66] Sorted: [55] [66] [77] [88] [99]
Never mind the implementation of sort, just notice that the variables are passed as char **
so that they can be swapped easily. Any real sort would probably be acting on a true array of data rather than individual variables but that's not the point of the example.
Pointer to Pointer
The definition of "pointer" says that it's a special variable that stores the address of another variable (not the value). That other variable can very well be a pointer. This means that it's perfectly legal for a pointer to be pointing to another pointer.
Let's suppose we have a pointer p1
that points to yet another pointer p2
that points to a character c
. In memory, the three variables can be visualized as :
So we can see that in memory, pointer p1
holds the address of pointer p2
. Pointer p2
holds the address of character c
.
So p2
is pointer to character c
, while p1
is pointer to p2
. Or we can also say that p2
is a pointer to a pointer to character c
.
Now, in code p2
can be declared as :
char *p2 = &c;
But p1
is declared as :
char **p1 = &p2;
So we see that p1
is a double pointer (i.e. pointer to a pointer to a character) and hence the two *s in declaration.
Now,
p1
is the address ofp2
i.e. 5000*p1
is the value held byp2
i.e. 8000**p1
is the value at 8000 i.e.c
I think that should pretty much clear the concept, lets take a small example :
Source: http://www.thegeekstuff.com/2012/01/advanced-c-pointers/
For some of its use cases:
This is usually used to pass a pointer to a function that must be able to change the pointer itself, some of its use cases are:
- Such as handling errors, it allows the receiving method to control what the pointer is referencing to. See this question
- For creating an opaque struct i.e. so that others won't be able to allocate space. See this question
- In case of memory expansion mentioned in the other answers of this question.
feel free to edit/improve this answer as I am learning:]