Что означает наличие двух звездочек ** в Objective-C?

Я понимаю, что одна звездочка * является указателем, что означает наличие двух **?

Я натыкаюсь на это из документации:

- (NSAppleEventDescriptor *)executeAndReturnError:(NSDictionary **)errorInfo

Ответы

Ответ 1

Это указатель на указатель, как и на C (который, несмотря на свой странный синтаксис с квадратной скобкой, основан на):

char c;
char *pc = &c;
char **ppc = &pc;
char ***pppc = &ppc;

и т.д., ad infinitum (или пока вы не закончите переменное пространство).

Он часто используется для передачи указателя на функцию, которая должна иметь возможность изменять сам указатель (например, перераспределение памяти для объекта с переменным размером).

=====

Следуя вашему запросу для примера, который показывает, как его использовать, вот код, который я написал для другого сообщения, которое его иллюстрирует. Это функция appendStr(), которая управляет своими собственными выделениями (вам все равно нужно освободить окончательную версию). Сначала вы устанавливаете строку (char *) в NULL, и сама функция выделяет пространство по мере необходимости.

#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);
    }
}

Код выводит следующее. Вы можете увидеть, как изменяется указатель, когда выделенная в настоящее время память заканчивается для расширенной строки (в комментариях):

 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.]

В этом случае вы проходите в **str, так как вам нужно изменить значение *str.

=====

Или следующее, в котором разворачивается сортировка пузырьков (о, позор!) на строках, которые не находятся в массиве. Он делает это, напрямую обменивая адреса строк.

#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;
}

который производит:

Unsorted: [77] [55] [99] [88] [66]
  Sorted: [55] [66] [77] [88] [99]

Не обращайте внимания на реализацию сортировки, просто обратите внимание, что переменные передаются как char **, чтобы они могли легко меняться. Любой реальный вид, вероятно, будет действовать на настоящий массив данных, а не на отдельные переменные, но это не точка примера.

Ответ 2

Указатель на указатель.

Ответ 3

В C указатели и массивы можно обрабатывать одинаково, то есть, например, char * - строка (массив символов). Если вы хотите передать массив массивов (например, много строк) в функцию, вы можете использовать char **.

Ответ 4

(ссылка: больше разработки iOS 6)

В Objective-C методы, аргументы, включая указатели объектов, являются передается по значению, что означает, что вызываемый метод получает свою собственную копию указателя, который был передан. Так что если вызываемый метод хочет измените указатель, в отличие от данных, на которые указывает указатель, вы нужен другой уровень косвенности. Таким образом, указатель на указатель.

Ответ 5

Указатель на указатель

Поскольку определение указателя говорит о его специальной переменной, которая может хранить адрес другой переменной. Тогда другая переменная может быть указателем. Это означает, что он совершенно легален для указания указателя на другой указатель.

Предположим, что у нас есть указатель p1, который указывает на еще один указатель p2, который указывает на символ c. В памяти три переменные можно визуализировать как:

введите описание изображения здесь

Итак, мы видим, что в памяти указатель p1 содержит адрес указателя p2. Указатель p2 содержит адрес символа c.

So p2 - это указатель на символ c, а p1 - указатель на p2 или мы также можем сказать, что p2 является указателем на указатель на символ c.

Теперь в коде p2 можно объявить как:

char * p2 = & c;

Но p1 объявляется как:

char ** p1 = & p2;

Итак, мы видим, что p1 является двойным указателем (то есть указателем на указатель на символ) и, следовательно, двумя * s в объявлении.

Теперь,

  • p1 - адрес p2 i.e. 5000
  • *p1 - это значение, удерживаемое p2 i.e. 8000
  • **p1 - это значение на уровне 8000, т.е. c Я думаю, что это должно в значительной степени очистить концепцию, давайте возьмем небольшой пример:

Источник: http://www.thegeekstuff.com/2012/01/advanced-c-pointers/

В некоторых случаях использования:

Обычно это используется для передачи указателя на функцию, которая должна иметь возможность изменять сам указатель, некоторые из его вариантов использования:

  • Как и ошибки обработки, он позволяет методу приема контролировать то, на что ссылается указатель. См. этот вопрос
  • Для создания непрозрачной структуры, т.е. чтобы другие не могли выделять пространство. См. этот вопрос
  • В случае расширения памяти, упомянутого в других ответах на этот вопрос.

не стесняйтесь редактировать/улучшать этот ответ, поскольку я изучаю:]