views:

803

answers:

3

I understand having one asterisk * is a pointer, what does having two ** mean?

I stumble upon this from the documentation:

- (NSAppleEventDescriptor *)executeAndReturnError:(NSDictionary **)errorInfo
+1  A: 

A pointer to a pointer.

robert mcbean
+12  A: 

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 " old.".
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.

paxdiablo
C and it's pointers confuse me... :(
Charlie Somerville
Can you elaborate on how you would use it in code? This still boggles me on how to use it.
John
Wow, need some time to digest... but thanks!!
John
You can use realloc() instead of malloc()+strcpy()+free() in appendToStr(). Also, no reason to be ashamed of bubble sorting 5 elements: bubble sort/insertion sort/selection sort are definitely the fastest algorithms for very small data sets -- most higher-order sort functions switch to one of these as a base case during divide-and-conquer.
Adam Rosenfield
@Adam, the malloc/copy/free was a kludge to ensure the pointer changed, otherwise my debug output may not be as obvious :-) And I'm not *really* ashamed of bubble sort, I actually use it (a slightly optimized and two-direction modification) for mostly-sorted data sets (e.g., an OOo spreadsheet for my business where transactions sorted by date where you add a small number of new ones at the end but they may be out of order) and it works better than many others in that situation.
paxdiablo
But mostly-sorted datasets, very small datasets and homework assignments are probably the only three places I'd use it.
paxdiablo
+1  A: 

In C pointers and arrays can be treated the same, meaning e.g. char* is a string (array of chars). If you want to pass an array of arrays (e.g. many strings) to a function you can use char**.

Thomas
With one difference: sizeof (char[]) != sizeof (char*) : plenty of people have been bitten by this.
paxdiablo